Spreedly Iframe Payment Form

Documentation for version 0.10

There are a variety of ways to send payment data to Spreedly. However, to limit your PCI compliance scope while still retaining full control of the UI, the recommended approach is to use an embedded iFrame form. This form, served by Spreedly, collects the sensitive portions of your customers’ payment data and stores it in your Spreedly vault without it ever touching your server environment.

Since browsers implement strict security guarantees that prevent cross domain pages from leaking data, the interaction between the host page (your order or payment page) and the Spreedly iFrame fields (a number input field and a cvv input field) must pass through a specific and pre-determined channel. This channel is managed by the Spreedly iFrame Payment Form and exposes a set of hooks that allow you to style the form to fit your UI and extract tokenized payment method.

Before you begin

We recommend avoiding a complex checkout form. Complex checkout forms are problematic on a variety of fronts: They are difficult to implement, have a high abandonment rate, and make error handling difficult. Make the payment portion of your checkout process as simple as possible.

The first step is to integrate the iFrame payment form into your master payment form.

Master form

For the purposes of this guide, the “master form” will represent the form which you build and place on your checkout page.

The iFrame payment form only provides fields for the user’s card number and CVV (these are the only PCI-scoped fields). Any other information you wish to collect as part of the payment process must be defined in the master form. For this example we’re assuming the collection of the following fields which, in addition to the card number, are required by Spreedly for a valid payment method:

  • Full name
  • Card expiration month and year

Create an HTML page containing a form like so:


<form id="payment-form" action="your-action.html" onsubmit='submitPaymentForm(); return false;'>

    <label for="full_name">Name</label>
    <input type="text" id="full_name" name="full_name"><br/>

    <label>Credit Card Number</label>
    <div id="spreedly-number" style="width:225px; height:35px; border: 2px solid"></div><br/>

    <label for="spreedly-exp-month">Expiration Date</label>
    <input type="text" id="month" name="month" maxlength="2">
    <input type="text" id="year" name="year" maxlength="4"><br/>

    <label>CVV</label>
    <div id="spreedly-cvv" style="width:60px; height:35px; border: 2px solid "></div><br/>

    <input id="submit-button" type="submit" value="Pay Now" disabled>
    <input type="hidden"  name="payment_method_token" id="payment_method_token">

    <script src="https://core.spreedly.com/iframe/iframe-0.10.min.js" id="spreedly-iframe"
      data-environment-key="YourEnvironmentKeyHere"
      data-number-id="spreedly-number"
      data-cvv-id="spreedly-cvv">
    </script>

  </form>

Important information:

  • The master form has a custom onsubmit handler that will manage the submission process (its implementation is covered later).
  • The master form will have input elements for all the non-sensitive data you wish to collect as part of the checkout process (here the first and last name, and card expiration month and year).
  • The master form should also have a hidden input (<input type="hidden" name="payment_method_token" id="payment_method_token" />) that will contain the token representing the user’s payment method in the Spreedly vault once it’s been tokenized.
  • Although the payment frame fields will be submitted directly to Spreedly, your master form should have an action that submits it directly to your server environment. Since the user’s payment method will have first been tokenized by Spreedly, it is safe to do so.
  • Embedded within the master form is the Spreedly iFrame payment form, which is composed of two separate input fields, each placed in its own iframe.
  • There is some extra HTML and default styling present in this example as well. None of it is mandatory, but read further before ripping it out.

Within the master form HTML, you need to include a script to pull in Spreedly iFrame Payment Form.

  • Iframe payment form requires that the script tag has and a specific id (id="spreedly-iframe") and a specific src (src="https://core.spreedly.com/iframe/iframe-0.10.min.js"")
  • Set the data-environment-key property to your Spreedly environment key. More information about environment key can be found here: environment key
  • Set the data-number-id to be the id of the div you wish to contain the number iframe.
  • Set the data-cvv-id to be the id of the div you wish to contain the cvv iframe.

With this in place, you should be able to view the master form in a browser. It will look very bare and disjointed, but you should be able to see all fields, including the credit card number and CVV fields.

Submitting the form at this point will not work, since the form’s onsubmit handler is not yet defined and you have not yet registered to receive any iFrame events.

Ready

Spreedly iFrame requires code to load in mulitple iFrames. When the iFrames are loaded, a ‘ready’ event is triggered. In order to ensure that a form cannot be submitted prior to the iFrames loading, we recommend the submit button be disabled until the iFrame is ready.

The following event handler will remove the disabled attribute when everything is completely loaded.


Spreedly.on('ready', function() {
  document.getElementById("submit-button").removeAttribute('disabled');
});

Tokenize Credit Card

The payment method token must be created before the master form is submitted to keep sensitive payment method information out of your server environment. This sequence should be managed by the custom form onsubmit handler defined in the master form HTML.

Form onsubmit handler

Create an onsubmit handler that will be responsible for the following:

  • Create a hash of additional payment information: set additional parameters on the payment method beyond what the user has entered for the card number and CVV. Most often these values will be present in the master form. The following are required by a payment method to pass validation:
    • full_name: Card holder’s full name
    • month: Card’s expiration month as a two digit number from 1-12
    • year: Card’s expiration year as a four digit number
  • Call to tokenize the payment method.

This function can live anywhere as it’s defined in the global scope (for the purpose of this example).


function submitPaymentForm() {

  // These are the fields whose values we want to transfer *from* the
  // master form *to* the payment frame form.
  var paymentMethodFields = ['full_name', 'month', 'year'];
  options = {};
  for(var i = 0; i < paymentMethodFields.length; i++) {
    var field = paymentMethodFields[i];
    options[field] =  document.getElementById(field).value;
  }
  Spreedly.tokenizeCreditCard( options );
}

Payment method event

The Spreedly iFrame exposes a set of events. You can register to be notified when certain events have occurred. Registering to the paymentMethod event will notified that the payment method has been tokenized and you are free to submit the master form.

Register a paymentMethod callback that takes two arguments: the payment method token and the full JSON payment method representation. Within the callback set the payment_method_token field of your master form and submit the form.


Spreedly.on('paymentMethod', function(token, pmData) {
  var tokenField = document.getElementById("payment_method_token");
  tokenField.setAttribute("value", token);
  var masterForm = document.getElementById('payment-form');
  masterForm.submit();
});

Try loading the master form in your browser, filling out all fields with valid values (see here for test card numbers), and pressing the submit button. The form should successfully submit the payment method to Spreedly and result in the payment method token being submitted to your server environment.

If you don’t see this behavior, open the Javascript console in your browser (often under “Developer tools”) to see any errors or further inspect the request/response to Spreedly.

Error handling

If an error occurs in the payment frame, at any point in the flow, an errors event will be triggered. To respond to errors, simply register an errors event handler that takes an array of error objects in the form:


[
  {
    "attribute":"full_name",
    "key":"errors.blank",
    "message":"First name can't be blank"
  }
]

Errors will always have a key, which you can use to filter on, and a message which is a human readable description of the error. You can reflect errors that have an attribute in the form alongside the affected field. Errors that do not have an attribute are not field-specific.


Spreedly.on('errors', function(errors) {

  // Just here for debugging. Remove this in prod
  console.log(errors);
  
  for(var i = 0; i < errors.length; i++) {
    console.log(errors[i]["key"] + ": " + errors[i]["message"]);
    // set error style on invalid fields
  }
});

With the error handler in place, submit an empty payment form. You should receive an array of errors for each invalid field. Here is a list of possible errors:

Key Description
errors.account_inactive A non-test card number was submitted against a test (unpaid) account. This can also occur when an invalid card number is submitted while testing as Spreedly assumes all unknown card numbers are real.
errors.environment_key_parameter_required An environment was not specified. Specify one when creating the Spreedly payment frame: new Spreedly.PaymentFrame("your_environment_key");
errors.invalid_environment_key_parameter The specified environment key is not valid. Check the supplied environment key value and re-submit.
errors.blank The payment method field specified by the attribute property of this error was submitted without a value, which is required by Spreedly. Display an appropriate error message and prompt the user to fill in the specified fields.
errors.invalid The payment method field specified by the attribute property of this error was submitted with an invalid value (e.g., a month value of “13”). Display an appropriate error message and prompt the user to correct the specified fields.
errors.blank_card_type The credit card number was invalid; we were unable to determine the card type.
errors.expired The payment method month and year indicates that it is expired. Display an appropriate error message and prompt the user to enter a non-expired payment method.
errors.unknown_referrer Spreedly could not determine the source (referring URL) of the form submission. A referring URL is required to identify MITM attacks.
errors.invalid_referrer The form submission did not originate from the Spreedly payment frame and has been rejected. This is a protection against MITM attempts.

Styling

The fields that live inside the iFrame do not have access to the styling defined for the master form in the host page. To allow you to style the number and cvv fields, the iFrame library has defined a call that let you apply CSS styles. Styling can occur anytime after the ready event.

  • Spreedly.setStyle(selector, styles): Apply the given CSS style to the given field in the iFrame.

setStyle requires you to set the selector to 'number’ or 'cvv’ since those are the only 2 fields contained in the iFrame.

We can add styling to the ready event like this:


Spreedly.on('ready', function() {
  document.getElementById("submit-button").removeAttribute('disabled');

  Spreedly.setStyle('cvv', "width:60px;  height:35px; font-size: 20px; text-align: center;");
  Spreedly.setStyle('number', "width:225px;  height:35px; font-size: 20px; text-align: center");
});

Reload the master form in your browser to see the styles applied to the payment frame fields.

Validation

There are many situations where it is useful to know details of the card information the user has entered in the payment form. For instance, if you’re doing real-time validation or are displaying an icon of the card type as the user types, you need to know basic details about the user input without seeing the actual values. The iFrame library provides this functionality via the input callback which accepts a hash of metadata representing the user’s input.


Spreedly.on('input', function( data ) {
    if (data["validCvv"]){
      Spreedly.setStyle('cvv', "background-color: #CDFFE6;")
    } else {
      Spreedly.setStyle('cvv', "background-color: #FFFFFF;")
    }
    if (data["validNumber"]){
      Spreedly.setStyle('number', "background-color: #CDFFE6;")
    } else {
      Spreedly.setStyle('number', "background-color: #FFFFFF;")
    }
});

The input event is fired on every keystroke and has the following keys:

  • validNumber: the card number passes luhn validation and is the proper length for its type.
  • validCvv: the cvv is numeric and the proper length for its type.
  • numberLength: The length of the user input in the card number field.
  • cvvLength: The length of the user input in the verification value (CVV) field.
  • cardType: The type of card entered (from this list of Spreedly supported card types). nil if the type of card could not be determined.
  • luhnValid: true if the entered card number conforms to the Luhn algorithm. false otherwise. While the Luhn algorithm does not guarantee a valid card number, it is a good way to quickly check for typos. The only way to guarantee a valid card is to run an auth or use the verify operation.

It can also be useful to operate on this input metadata before form submission. In your form onsubmit handler, invoke validation instead of submit. Then, in the validation event handler, perform your custom validation logic and, only if validation passes, tokenize the payment method:


function submitPaymentForm() {
  // trigger validate to get back metadata
  Spreedly.validate();
}

Spreedly.on('validation', function(inputProperties) {
  if(!inputProperties["validNumber"]) {
    // prompt user to correct input
  } else {
    var paymentMethodFields = ['full_name', 'month', 'year']
    var options = {};
    for(var i = 0; i < paymentMethodFields.length; i++) {
        var field = paymentMethodFields[i];
        options[field] =  document.getElementById(field).value;
    }
    console.log("calling tokenize")
    Spreedly.tokenizeCreditCard( options );
  }
});

Since communicating with the iFrame is asynchronous, you must operate on the input metadata in a separate validation event helper which will then become responsible for submitting the payment frame.

Field events

The fieldEvent is triggered when the number or cvv fields experience a focus, blur, mouseover, or mouseout event. These events can be handled by listening to the fieldEvent


Spreedly.on('fieldEvent', function(name, event){
    // remove logging before pushing to prod
    console.log("The " + name + " field just had a " + event + " event.")
})

Additional payment information

It is quite common to collect more than just the required fields when storing a payment method. If you wish to provide more detailed payment information, such as billing address, email etc…, you can simply add the key, value pair to the options argument of Spreedly.tokenizeCreditCard

The following are a list of additional properties you can set:

  • first_name
  • last_name
  • full_name
  • month
  • year
  • email
  • address1
  • address2
  • city
  • state
  • zip
  • country
  • phone_number
  • company
  • shipping_address1
  • shipping_address2
  • shipping_city
  • shipping_state
  • shipping_zip
  • shipping_country
  • shipping_phone_number

Reset and Reload

Spreedly iFrame payment form offers both resetFields and reload. resetFields simply removes the any values from the number and cvv fields. reload removes all listeners and reloads all code for the iFrames.

iFrame API Reference

  • Spreedly.tokenizeCreditCard() - causes the iframe to request/return a token
  • Spreedly.resetFields() - clears the iframe fields
  • Spreedly.validate() - causes the iFrame to report the state of the inputs
  • Spreedly.setStyle(id, style) - sets a style on a field
    • id - must be either 'number’ or 'cvv’
  • Spreedly.reload() - removes all listeners, reloads the iFrame payment form.

Events

Handlers can be registered to these events. They are not required.

  • ready - triggered when the iframes are loaded and ready for styling.
  • input - triggered when a user inputs values into an iframe field. The hash passed to this function will contain the following:

    • cardType - 'visa’,'american_express’,'discover’,'jcb’,'master’,'diners_club’,'dankort’
    • validCreditCardNumber - boolean which represents if the card number is the proper length and passes luhn validation
    • validCvv - boolean which represents if the cvv is valid and the proper length for the card type
  • validation - triggered when the input properties have been retrieved. Triggered when you call Spreedly.validate()

  • paymentMethod - triggered when a payment method token has been successfully created

  • errors - triggered when any errors are returned

    • errors - an array of errors from the iframe. All errors have a key and message property.
  • fieldEvent - triggered when certain events occur in iframe fields. Passes the name of the field experiencing the event and what type of event occurred.

    • name - name of the target field, i.e. 'cvv’, 'cardNumber’
    • notification - 'focus’, 'blur’, 'mouseover’, 'mouseout’

Browser compatibility

iFrame v0.10 is compatible with the following browser versions:

  • Internet Explorer 8.0 and above. Support for IE8 will be discontinued January 2016.
  • Google Chrome 30 and above
  • Firefox 30 and above
  • Safari 6 and above
  • iOS Safari 3.2 and above

Any browser not supporting TLS 1.2 will no longer be supported after March 2016

Versioning

These docs are for iframe v0.10.

The current version of the iFrame payment form is v1 and is available at:

https://core.spreedly.com/iframe/iframe-1.min.js

The iframe library is versioned with a major and minor numerical, e.g., 1. Once the major version is 1 or greater, backwards-compatible new features will cause the minor version number to increment. Backwards-incompatible new features will cause the major version number to increment. While the major version is 0, minor version increments could contain backward breaking changes.

Any critical bug fixes or security updates will be automatically and deployed as the current version number, making no action required by you to be running on a secure, stable version. For this reason, please do not store a copy of the javsacript library on your servers, always reference the version hosted on spreedly.com.

Iframe v0.10 introduces breaking changes from v0.5. Documentation for version 0.5 is located at iFrame payment form v0.5

Security

In order to achieve the highest levels of PCI-DSS v3 compliance with Spreedly Express or iFrame, you need to disable the creation of payment methods via the direct API. After confirming you are not using the XML, JSON, JSONP, CORS or transparent direct methods of adding a payment method, enable the “Spreedly iFrame or Express Only option in the Environment Settings page of your environment.

This prevents direct API submission of payment methods which is required to detect malicious attacks and, in general, enforce adherence to the PCI standard.