iFrame payment form

There are a variety of ways to send payment data to Spreedly. If you wish to implement a custom checkout experience while still limiting your PCI scope, the recommended approach is to use the iFrame payment form. It is a lower-level library which allows almost unlimited UX customization and behavior while still storing card data in your Spreedly vault without it ever touching your server environment.

If you are looking for specific commands or options, please see the iFrame API reference

🚧

iFrame Deprecation

For customers not using iframe-v1 or iframe-stable, Spreedly has implemented a deprecation plan where all versions more than three months out of date will be removed from service. This ensures all customers are on recent versions with the latest security updates. We strongly recommend implementing one of our regularly updated release channels as they will always be up to date.

iFrame Overview

Spreedly’s iFrame payment form is a pure JavaScript library that provides two Spreedly-managed fields for collecting the card number and CVV (the two PCI-sensitive fields of a payment method). No cookies are used to provide this functionality. Your host page places and styles these two fields within the checkout form, and the iFrame returns a tokenized payment method to the host page when the payment method has been successfully submitted. While an invalid PAN will prevent the iFrame from returning a payment method token, an invalid CVV will not. The CVV iFrame field may be hidden if you do not require the CVV to process payments through your gateway.

The credit card auto-complete attributes are enabled by default on the fields which will allow for browsers and password managers to more easily auto-fill the fields. The customer can make autocomplete more functional by adding the autocomplete attributes to the customer controlled fields for name (“cc-name”), expiration month (“cc-exp-month”), and expiration year (“cc-exp-year”).

By default the number input form has a maxlength of 19 and the CVV input form has a maxlength of 4. As the PAN is entered into the number input form, the maxlength of both the number form and CVV will update depending on the card type. Note: if the user highlights and replaces the entered PAN without deleting the initial value, the initial maxlength will remain in place. To ensure the cardholder can enter all digits according to their card type, they must delete the input values before reentering with another card number.

The following shows a rendered checkout page using the Spreedly iFrame to serve the number and CVV fields. The only parts contained within Spreedly-served iFrames are the individual number and CVV input fields. All the labels and other fields are part of the host checkout page.

Before you begin

You will need your environment key and access secret from the Spreedly app, as well as your gateway token. Since invalid card numbers will fail silently until error handling is implemented, you may also find it helpful to have valid test card numbers at hand for easy reference. A more complete example of a payment page that uses iFrame can be found in the sample applications guide.

Adding iFrame

To add iFrame to your checkout page, include the Javascript library in your host page:

<head>
  <script src="https://core.spreedly.com/iframe/iframe-v1.min.js"></script>
</head>

Then copy the following checkout form into the page:

<form id="payment-form"
  action="https://yoursite.com/checkout"
  onsubmit='submitPaymentForm(); return false;'>

  <input type="hidden"  name="payment_method_token" id="payment_method_token">

  <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="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>

</form>

This will create a very simple (and unstyled) payment form that is not yet functional. It must first be configured to contain the number and CVV fields before it can successfully tokenize your customers’ payment methods.

Configuration

Once the host form is on the page, the iFrame is initialized wtih the Spreedly Javascript module. Pass an environment key where users' payment methods should be stored along with the id of the form HTML elements where the two card fields are to be placed.

In this example, payment methods will be tokenized in the listed C7cRfNJG...environment and the number and CVV form fields will render with the spreedly-number and spreedly-cvv elements respectively. These elements are the CSS ID selectors of the two divs from the above form.

Place the below snippet in a new script tag on the bottom of the checkout page, swapping out the example environment key with your own:

Spreedly.init("C7cRfNJGODKh4Iu5Ox3PToKjniY", {
  "numberEl": "spreedly-number",
  "cvvEl": "spreedly-cvv"
});

Ready

The 'Pay now' button is initially disabled to prevent submitting the form before iFrames have loaded. A ready event is triggered when the iFrames have been successfully initialized. Create a listener for this event to toggle the 'Pay now' button to an enabled state.

Spreedly.on("ready", function () {
  var submitButton = document.getElementById('submit-button');
  submitButton.disabled = false;
});

Reload the checkout page to see the number and CVV fields rendered in the form.

Tokenize payment method

Once fields are rendered, iFrame doesn't automatically hook into any form events. Rather, it must be told explicitly when to send the collected card data to Spreedly for tokenization.

The most straight-forward approach is to create a form onSubmit handler that sets the required fields for a payment method (name and expiration date, in addition to number and CVV fields already handled for you) and delegates to **Spreedly.tokenizeCreditCard**.

Implement a top-level function submitPaymentForm that gets the values from the name and expiration date fields, passes them over to iFrame, and requests tokenization. Note that this function and all other JavaScript functions should be placed in a script tag on the checkout page.

Place the below snipped in the previously-created script tag, below the call to Spreedly.init:

function submitPaymentForm() {

  var requiredFields = {};

  // Get required, non-sensitive, values from host page
  requiredFields["full_name"] = document.getElementById("full_name").value;
  requiredFields["month"] = document.getElementById("month").value;
  requiredFields["year"] = document.getElementById("year").value;

  Spreedly.tokenizeCreditCard(requiredFields);
}

When the form's submit button is clicked, the required values from the host page will be sent to the iFrame and subsequently submitted to Spreedly for tokenzation in the selected environment. If the card is tokenized successfully, the token is sent page to the host page via an event, which must be sent to backend environments for processing. Please note, successful tokenization does not execute a transaction nor validation. In order to verify a payment method, invoke a purchase or authorization call from a secure, server-side environment.

Error handling

If a card is not successfully tokenized, an error event is generated. This sample logs errors to your browser's Javascript console; you may wish to selectively surface some errors to the user.

Spreedly.on('errors', function(errors) {
  for (var i=0; i < errors.length; i++) {
    var error = errors[i];
    console.log(error);
  };
});

See Spreedly's response codes for more information. In particular, note that 422 is returned both for request with inadequate data (such as missing name or CVV) and for declined cards.

Receiving the tokenized payment method

When a card has been tokenized by Spreedly, an event is fired that includes the generated payment method token as well as the payment method details. It is up to you to receive the token and send it to a backend environment. In the following example, it shows how to register for the onPaymentMethod event and add the payment method token to the host form, which is then submitted to the backend:

Spreedly.on('paymentMethod', function(token, pmData) {

  // Set the token in the hidden form field
  var tokenField = document.getElementById("payment_method_token");
  tokenField.setAttribute("value", token);

  // Submit the form
  var masterForm = document.getElementById('payment-form');
  masterForm.submit();
});

While this exmple shows how to submit the payment method token via a form submission, one could just as easily send the token back via AJAX.

When the checkout form is submitted, extract the payment_method_token parameter and use it to execute a purchase (or auth) against the appropriate gateway using the direct API:

$ curl https://core.spreedly.com/v1/gateways/LlkjmEk0xNkcWrNixXa1fvNoTP4/purchase.json \
  -u 'C7cRfNJGODKh4Iu5Ox3PToKjniY:4UIuWybmdythfNGPqAqyQnYha6s451ri0fYAo4p3drZUi7q2Jf4b7HKg8etDtoKJ' \
  -H 'Content-Type: application/json' \
  -d '{
        "transaction": {
          "payment_method_token": "56wyNnSmuA6CWYP7w0MiYCVIbW6",
          "amount": 100,
          "currency_code": "USD",
          "retain_on_success": true
        }
      }'
$ curl https://core.spreedly.com/v1/gateways/LlkjmEk0xNkcWrNixXa1fvNoTP4/purchase.xml \
  -u 'C7cRfNJGODKh4Iu5Ox3PToKjniY:4UIuWybmdythfNGPqAqyQnYha6s451ri0fYAo4p3drZUi7q2Jf4b7HKg8etDtoKJ' \
  -H 'Content-Type: application/xml' \
  -d '<transaction>
        <payment_method_token>56wyNnSmuA6CWYP7w0MiYCVIbW6</payment_method_token>
        <amount>100</amount>
        <currency_code>USD</currency_code>
        <retain_on_success>true</retain_on_success>
      </transaction>'

You can review the Spreedly API reference for details of the direct API, including the authentication, purchase and authorize calls.

Securing iFrame

To achieve the highest level of PCI-DSSv3 compliance with the iFrame, disable the creation of payment methods via direct API. After confirming the XML, JSON, CORS, or transparent direct methods of adding a payment method are not in use, enable the "iFrame or Spreedly Express only" option from the Environment settings page in the Spreedly application.

This prevents direct API submission of payment methods which is required to detect malicious attacks and, in general, enforce adherence to the PCI standard. The only way payment methods can be added to this environment is via the iFrame (or Express) payment forms.

Content Security Policy

Content Security Policy (CSP) adds a layer of security to your payment page, aimed at mitigating attacks like Cross-Site Scripting (XSS). By specifying trusted domains for the browser, CSP restricts the executable script sources and reduces vectors where XSS can occur. For iFrame, include https://*.spreedly.com/ as a valid domain to ensure proper loading of iFrame assets.

Customizing the iFrame

The form and flow in-place should be functional by default, but is basic and unstyled. Next, use the iFrame API reference to learn about iFrame customization topics, namely:

Recache existing payment method

Due to PCI requirements, Spreedly is unable to store a card's CVV past the original transaction. If you have an existing payment method already tokenized in your environment, the customer can update their CVV for a new transaction without having to re-enter the rest of their credentials.

Simply set iFrame to operate in recache mode and handle the recache event:

Spreedly.on('ready', function(){
 Spreedly.setRecache("56wyNnSmuA6CWYP7w0MiYCVIbW6" , {
   'card_type': 'visa',
   'last_four_digits': '1234'
 });
});

// Invoke Spreedly.recache() to recache CVV. On success,
// the "recache" event will be triggered.
Spreedly.on("recache", function(token, paymentMethod) {

  // Send ping back to server for post-recache transaction processing
  var masterForm = document.getElementById('payment-form');
  masterForm.submit();
});

More information about recache is available in the iFrame API reference.

Skipping name and expiration date validation

Spreedly validates the presence of name and expiration date associated with a credit card by default. However, there can be some cases where this metadata is not necessary and validation may be skipped. To skip either or both of these validations, set the following parameters to true.

Spreedly.on('ready', function(){
  Spreedly.setParam('allow_blank_name', true
  Spreedly.setParam('allow_expired_date', true)
});

Examples

The Sample Payment Frame hosts a more complete example of a payment page using iFrame.

Browser compatibility

In general, Spreedly supports browser versions that are actively maintained and have up-to-date security profiles. That list includes:

We additionally require that all browsers support TLS 1.2 or higher. For further information on keeping your browser secure, including CSP and overall payment page security, refer to the Securing iFrame section above.

Versioning

Spreedly iFrame is versioned by major numerical, e.g., v1. Backwards-incompatible features will cause the major version number to increment and will only be deployed if the script reference on a checkout page is updated (meaning you choose when to accept major upgrades).

Any critical bug fixes or security updates will be automatically deployed as the current version number, thus automatically maintaining a secure, stable version.

Do not store a copy of the JavaScript library on your servers, always reference the version hosted by Spreedly. Any self-hosted versions of Spreedly’s code will be considered unsupported.

An iFrame specific Changelog RSS feed can be subscribed to at https://core.spreedly.com/iframe/feed.xml.

Deprecation

For customers not using iframe-v1, or iframe-stable, Spreedly has implemented a deprecation plan where all versions that are more than 3 months out of date will be removed from service. This ensures that all customers are on recent versions with the latest security updates. We strongly recommended implementing one of our regularly updated release channels as they will always be up to date. The only exception to this policy is iframe-1.93 as it is the last version supporting Internet Explorer.

Scheduled releases

Spreedly offers three regularly updated channels for iFrame.

v1: This is the most commonly used iFrame asset (iframe-v1.min.js) that receives consistent updates. Updates do not come on any set schedule. iframe-v1 is recommended for users who want the latest security updates and features as soon as they are available.

Candidate: Available by using iframe-candidate.min.js, candidate is updated on the 1st Tuesday of each month. It contains all updates that have been made to iframe-v1 from the prior month. This is recommended for users who wish to test upcoming updates to iframe-stable before they reach customers and is not intended for production use.

If you encounter issues while using iframe-candidate.min.js, please contact support for assistance.

Stable: Available by using iframe-stable.min.js, stable is the sibling of candidate. It is updated on the 3rd Tuesday of the month to match the latest version of iframe-candidate. This is recommended for production use by customers performing testing on candidate.

Swapping release channels is as easy as changing the asset in the iFrame script tag as shown below:

<head>
  <script> src="https://core.spreedly.com/iframe/iframe-[desired version].min.js"</script>
</head>

Legacy browser support

iFrame dropped support for Internet Explorer on June 15, 2022, the same time as Microsoft dropped support for IE. As Internet Explorer was the last browser to not support the current recommended version of JavaScript (ES6/JavaScript 2016). Starting December 2022, iFrame does not work on browsers that do not support ES6.

If you still want to support legacy browsers, you will need to lock to iFrame version 1.93 instead of the release channels above. This version will not receive any new feature updates.

<head>
  <script src="https://core.spreedly.com/iframe/iframe-1.93.min.js"></script>
</head>