Hosted Fields

Quick Start

The Forter Hosted Fields SDK allows merchants to securely collect sensitive data directly from their frontend forms, alleviating the need to apply stricter measures of PCI compliance.

Form controls created using the Forter Hosted Fields SDK can be customized to blend in seamlessly with the merchant’s design language and styles.

After securely collecting the sensitive data, it is sent over an encrypted SSL connection to the Forter secure card data Vault, where it is tokenized, encrypted and stored for later use.

Installation and setup

  1. Copy and paste the following snippet into your checkout page:
<script type="text/javascript" data-site-id="XXXXXXX" id="checkoutTools__script" src="https://sdk.checkouttools.com/v1.2/collect.js"> </script>
  1. If you use CSP, please add the following CSP directives:
connect-src <https://sdk.checkouttools.com>  
frame-src <https://sdk.checkouttools.com>  
script-src <https://sdk.checkouttools.com>
  1. Initiate the SDK instance
const forterCollect = await window.checkoutTools.collect.init(
  {
    environment: 'sandbox',
    authToken: 'TOKEN.VALUE'
  }
);

And you’re good to go. Follow the API reference or one of the examples below to create your secure and customized payment form.

Authentication

Before using the Forter Hosted Fields SDK, you will need to generate a temporary auth token.

  1. Before rendering your checkout page, use the /client-key endpoint in the Tokenization API to receive a JWT authentication token.
  2. Pass this token to your frontend, and initialize the SDK using the authToken argument.

NOTE: When creating the token server-side, you are able to control its TTL. It is recommended to use the smallest TTL value necessary for the end-user to successfully complete the transaction in the frontend, in order to reduce the chance of abuse.

Creating and Updating fields

First, create “container” elements in your HTML form:

After configuring the SDK, within each container will be rendered a secure Hosted Field iframe. Make sure to set a meaningful id per each container element.

<form>
    <label>
        Name on card
        <div id="card-holder-name"></div>
    </label>
    <label>
        Card
        <div id="card-full-pan"></div>
    </label>
    <label>
        CVC
        <div id="card-cvc"></div>
    </label>
    <label>
        Expiration date
        <div id="card-expiration"></div>
    </label>
    <label>
        Example text field
        <div id="text-field"></div>
    </label>
</form>

Calling forterCollect.addFields will render the Hosted Fields iframes. Each HTML container element in the form will be assigned with a specific behavior and configuration:

In this example we are adding a field with id card-full-pan of type CARD_NUMBER.

 forterCollect.addFields({  
   "card-full-pan": {  
     type: "CARD_NUMBER"  
   });

Note: In order to successfully tokenize payment details, fields of type CARD_NUMBER, CARD_HOLDER_NAME and CARD_EXPIRATION_DATE must all be added.

Note: Field ids must be unique in the scope of a single SDK initialization, and must match the ids given to their respective HTML elements described earlier.

Hosted fields can also be updated dynamically using the updateFields method:

Note: The type configuration option can not be updated using this method.

forterCollect.updateFields({  
   "card-cvc": {  
     visible: false  
   });

Waiting until the form is ready

While the addFields method is working, the Hosted Fields UI will be briefly non-interactive. It is recommended to display a loading state (e.g. a loading spinner, disabling the checkout button, or both) while this is happening.
You can do it in one of three ways:

  1. Bind the form's loading state to the the promise returned by addFields, so that the loading state is turned off after the promise is done. Alternatively, you can pass a callback to the function to achieve the same result.
  2. Add a callback to the onLoad option when creating the SDK instance (see instructions below). This callback will be triggered after the first call to addFields is completed.
  3. Query the form's isReady field periodically. (Not recommended)

Submitting the form

Once the form has been completed, we’ll want to submit it.

You can use the submit() method to perform this action. If the form is still in an invalid state when submitting, the validation errors will be returned. The onError callback can also be used to receive validation errors when submitting.

// Invalid form  
const result = await forterCollect.submit();  
console.log(result)

/**  
{  
   "success": false,  
   "errors": {  
      "cc-number": {  
         "errorCode": "CREDIT_CARD_FORMAT_INVALID",  
         "message": "Credit card number is in an invalid format"  
      }  
   }  
}

*/

// Valid form  
const result = await forterCollect.submit();  
console.log(result)

/**  
{  
   "success": true,  
   "token": "ftr12d4e830283b647d4b3b2a3d65f02b8ab"  
}

*/

API

Hosted Field SDK Object API

init(options: Options, cb ?:(err), eventHandlers ?: {}) => Promise: Initialize the Forter Collection SDK.

This method returns a Promise object that can be awaited and returns any possible errors.
Alternatively, this method accepts an optional callback function which is invoked asynchronously once the action is completed.

See the options section below for more information about available configuration options.

addFields(fieldsDefinition,[cb: (err, results)]) => Promise - Add one or more Hosted Fields to the form (See field definition explanation above).

This method returns a promise that resolves when all Hosted Fields have finished loading and are ready for use. (See also: onLoad() event below).
Alternatively, this method accepts an optional callback function which is invoked asynchronously once the action is completed.

updateFields(fieldsDefinition,[cb: (err, results)]): Promise - Update one or more Hosted Fields in the form (See field definition explanation above).

This method returns a promise that resolves when all Hosted Fields have finished updating and are ready for use.
Alternatively, this method accepts an optional callback function which is invoked asynchronously once the action is completed.

submit(cb: (err, results)): Promise<{status: boolean, token:string, errors?: Array<{message: string, errorCode:string}>}> - Once the user finishes entering all the relevant fields in the payment form, this method will submit them securely to be stored by Forter.

This method returns a Promise object that can be awaited to receive the tokenized fields.
It will return the tokenized payload if successful, or the field errors if the form is still in an invalid state.
Alternatively, this method accepts an optional callback function which is invoked asynchronously once the action is completed.
Either way, the asynchronous result of this function is a single token string, that can later be unpacked in the server side to the individual fields, or sent as is to a third party using the Forter Secure Proxy. Please note, a successful tokenization does not execute a transaction or validation. In order to verify the payment method, you will need to invoke a purchase or authorization from your secure, server-side environment.

reset(fieldName?: string, cb?: (err, results)): Promise - Reset all fields or a specific field in the form. If a field name is not provided, all of the fields will be reset.

This method returns a Promise object that can be awaited.
Alternatively, this method accepts an optional callback function which is invoked asynchronously once the action is completed.

focus(fieldName?: string): Promise - Focus on a specific field in the form.

Form state

The Forter Collection SDK instance contains useful properties about the current state of the form:

  • fields: an object containing per-field state properties: isValid, error, isEmpty, isDirty, isTouched, isReady
  • isReady: boolean - Indicates whether all configured Hosted Fields have finished loading
  • isValid: boolean - Indicates whether all fields are in a valid state, and the form is ready to be submitted.
  • isSubmitting: boolean - Indicates Whether the form is currently submitting the data to Forter.
  • cardBrand: string | null - The current card brand of PAN.

Options / events

The Forter Collection SDK instance can be configured using a configuration object passed to “checkoutTools.collect.init()” .

  • environment - sandbox | production - (default: sandbox) - The tokenization environment to use.
  • authToken (REQUIRED) - An auth token retrieved from the PCI Tokenization API in the server-side.
  • validationTrigger - ON_BLUR | ON_CHANGE (default: ON_BLUR ) - When should each input be validated, potentially triggering error styling.
  • onLoad() - Triggered when all Hosted Fields are successfully loaded. It’s recommended to disable the submit button until after this event is triggered.
    NOTE: This is only triggered the first time you add new fields to the form.
  • onError(errors: {[fieldName: string]: {message: string, errorCode:string} }) - Triggered after the form is submitted in an invalid state. This handler receives a single argument - an object containing error messages, keyed by their relevant field name.
  • onEnterPress() - Triggered when the Enter button is pressed inside one of the Hosted Field inputs. This is typically used to invoke the submit API method on the SDK object.
  • onEscapePress() - Triggered when the Escape button is pressed inside one of the Hosted Field inputs.
  • onSuccess() - Triggered once the submit method was successfully completed. It’s recommended to show a loading indicator from the moment the form is submitted and until this event is triggered.
  • onValidityChange(fieldId:string, isValid:boolean, error?: {message: string, errorCode:string}) - Triggered when the Field’s validity state has changed. In case the field is invalid, an error message code and default message text will be provided.
  • onCardBrandChange(cardBrand: string) - Triggered when a card brand is resolved from the card number

Example

let formLoading = false;

const forterCollect = window.checkoutTools.collect.init({  
 onLoad() {  
   formLoading = true;  
 },  
 async onEnterPress() {  
   const tokenizeResult = await forterCollect.submit();  
   if (tokenizeResult.success) {  
     console.log('Form successfully submitted', tokenizeResult);  
   }  
 },  
 onError(errors){  
   console.log('Form submit error:', errors)  
 }  
});

Field API

Configuration options

Required options are marked with an asterisk.

  • type* - The field’s ‘type’ dictates its appearance, behavior, and allowed values of the underlying input element. Forter Hosted Fields support these common payment field types:
    • TEXT - General purpose string field. Custom regex validation can be applied using the validation configuration option
    • CARD_HOLDER_NAME - The name of the card’s owner. Custom regex validation can be applied using the validation configuration option.
    • CARD_NUMBER - A credit card number. Luhn check validation is automatically applied.
    • CARD_CVC - A credit card security code (aka CVV). Length requirements are automatically derived from the brand of a ‘card-number’ field, if one exists.
    • CARD_EXPIRATION_DATE - Credit card expiration date.
  • placeholder - The input’s field placeholder text. A sensible default is defined for each field type.
  • initialValue - Specifies the starting value that the field.
  • style - An object with css rules allowing customization of the Field’s style. See the styling section below.
  • validation - a RegEx pattern that will be used to validate the input’s value. This is only relevant for the TEXT and CARD_HOLDER_NAME field type.
    forterCollect.addFields({  
      "card-holder-name": {  
        type: "CARD_HOLDER_NAME",  
        placeholder: "Name on card",  
        validation: /^[a-z ,.'-]+$/i
      }
    });
    

Events

Every Forter Hosted Field triggers multiple events during its lifecycle. Event handlers can be attached using the configuration object passed to “addFields()” and “updateFields()”

  • onFocus() - Triggered when the input element inside the Hosted Field receives focus
  • onBlur() - Triggered when the input element inside the Hosted Field loses focus

Error Codes

  • FIELD_INVALID_FORMAT - Field is in an invalid format.
  • CREDIT_CARD_FORMAT_INVALID - Credit card number is in an invalid format.
  • CREDIT_CARD_NUMBER_INVALID - Credit card number is incorrect.
  • CREDIT_CARD_EXPIRED - Credit card is expired.
  • GENERAL_ERROR - An unknown error occurred.

Styling

Forter Hosted Fields can mostly be styled using standard CSS rules and techniques. When customizing your field's styles, the required techniques will differ when styling the container element itself (i.e. the form control), or the input element. This separation is required because the wrapping container element is hosted on your page, and the input in another, secured, web app.

Container elements

Simply target the container elements you’ve created in HTML using the same CSS rules you apply to your other, non hosted, form controls. This applies to macro layout like width, height, and spacing, and styling like border and box-shadow.

  1. <form>
        <label>
            Name on card
           <div id="card-holder-name" style="border: solid 1px red; height: 40px" />
        </label>
    </form>
    

Note: Even if your other inputs calculate their own height, Forter Hosted fields require an explicit ‘height’ to be set on the container element.

State classes

The following css rules are automatically added to the container element, which match the internal style “form states” mentioned above.

  1. forter-hosted-fields-focus
  2. forter-hosted-fields-valid
  3. forter-hosted-fields-invalid
  4. forter-hosted-fields-empty
  5. forter-hosted-fields-touched
  6. forter-hosted-fields-empty
  7. forter-hosted-fields-dirty

Simply reference these classes in your stylesheets as normal:

{
  .card-holder-name.forter-hosted-fields-focus {
    border: solid 2px blue;
  }
}

Input elements

In case advanced styling rules are required for the input elements themselves, additional css rules can be passed in to the javascript SDK object using the style configuration option.

Two types of styles can be used:

  1. A string containing an existing CSS class you may have already defined on your page used to style input elements. All CSS rules will be copied from this existing class and applied to the Hosted Field input element.
    await sdk.addFields({
      'card-number': {
        type: 'CARD_NUMBER',
        style: '.existing-form-input-class'  
      },
    });
    
  2. A configuration object containing custom css definitions. Accepted keys in this object are:
    1. input - Standard css properties that will be applied to the internal input element (See below for supported properties)
    2. Pseudo-classes (eg. :hover, :focus) - Nest additional rules for every html state
    3. Form state classes: Specific css classes are automatically applied to every Hosted Field, depending on its state. Every class can be styled using specific css properties.
      1. Supported state classes:
        .invalid - Field has failed validation.
        .empty - Field doesn't have a value.
        .touched - Applied forever once the field’s first ‘blur’ event has triggered.
        .dirty - Applied forever once the field’s value has changed for the first time.
        :focus - Field is currently focused
        Media queries - (These apply to the iframe dimensions) - define a rule in pixels and nest additional rules of the other formats inside
      2. Allowed CSS Properties (browser specific variants will also work): "appearance", "box-shadow", "color", "direction", "font", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric", "font-weight", "letter-spacing", "line-height", "margin", "margin-top", "margin-right", "margin-bottom", "margin-left", "opacity", "outline", "padding", "padding-top", "padding-right", "padding-bottom", "padding-left", "text-align", "text-shadow", "transition", "tap-highlight-color", "tap-highlight-color"
 
await sdk.addFields({
  'card-number': {
    type: 'CARD_NUMBER',
    style: {  
      'input': {  
       'color': 'black',  
       'padding': '10px 5px',  
      }  
      ':focus': {  
        'border-color': 'blue',  
      },  
      '.touched': {  
        'color: 'green',  
      },  
      '.touched.error': {  
        'color: 'orange',  
        'text-decorations': 'underline',  
      },  
      '@media screen and (max-width: 600px)': {  
         'input': {  
           'font-size': '10px'  
        }  
      },
    },
  }
});

Example: Full Credit Card Details Form

We’ll create a common form containing fields for collecting a credit card’s number, security code and expiration date, along with the customer’s name.

First create the HTML scaffolding - a form with placeholders elements that will late be replaced with Forter Hosted Fields


<body>
<form id="payment-form">
 <label>
   Name on card
   <div id="card-holder-name" />
 </label>
 <label>
   Card
   <div id="card-number" />
 </label>
 <label>
   CVC
   <div id="card-cvc" />
 </label>
 <label>
   Expiration date
   <div id="card-expiration" />
 </label>
</form>
</body>

Next, in a Javascript tag copy and paste the following

const forterCollect = await checkoutTools.collect.init();

const commonStyles = {  
  ":focus": {  
    color: "orange",  
  },  
  ".touched": {  
    color: "green",  
  },  
  ".touched.error": {  
    color: "red",  
    "text-decorations": "underline",  
  },  
};

forterCollect.addFields({  
  "card-holder-name": {  
    type: "TEXT",  
    placeholder: "Name on card",  
    style: commonStyles,  
  },  
  "card-number": {  
    type: "CARD_NUMBER",  
    placeholder: "1111-2222-3333-4444",  
    onFocus() {  
      // TODO: handle focus  
    },  
    style: commonStyles,  
  },  
  "card-cvc": {  
    type: "CARD_CVC",  
    onError(errorMessage) {  
      console.error("Error in field card-cvv", errorMessage);  
    },  
    style: {  
      ...commonStyles,  
      display: "inline-box",  
    },  
  },  
  "card-expiration": {  
    type: "CARD_EXPIRATION_DATE",  
    style: commonStyles,  
  },  
});

Finally, wire up the form’s ‘submit’ event to Forter’s ‘submit’ method in order to securely store the Form’s state

document  
 .getElementById("#payment-form")  
 .addEventListener("submit", function (e) {  
   e.preventDefault();

   // Callback based method:  
   forterCollect.submit(function (err, results) {  
     if (err) {  
       console.error("error submitting payment form", err);  
       return;  
     }

     console.log("payment form submitted successfully!", results);

     // Combine the tokenized fields along with other non-sensitive fields that may exist in the form, and submit them to your backend server for further processing.
     backend.submitPaymentForm({ fields: { ...ownFields, ...results } });

   });

   // or alternatively, ESNext promise based method:  
   forterCollect  
     .submit()  
     .then((results) => {  
       if (results.success) {  
         console.log("Form successfully submitted", tokenizeResult);  
       }  
       console.log("payment form submitted successfully!", results);  
       backend.submitPaymentForm({ fields: { ...ownFields, ...results } });  
     })  
     .catch((e) => {  
       console.error("error submitting payment form", e);  
     });  
 });

Example: Re-collecting CVC

The Hosted Fields sdk supports the collection of just the CVC. This is useful in repeat transactions, along with a multi-use token in order to reduce the risk of a fraud-decline.

See the Detokenization proxy docs for more information on how to use the resulting token.

const forterCollect = await checkoutTools.collect.init();

forterCollect.addFields({  
  "card-cvc": {  
    type: "CARD_CVC",  
    onError(errorMessage) {  
      console.error("Error in field card-cvv", errorMessage);  
    },  
  },  
});