Hosted Fields SDK - Overviews

Hosted Fields SDK

Intro Intro

Our hosted fields are rendered in secure iframes, ensuring that card numbers, CVV codes, and other sensitive payment data are handled securely. This approach allows you to customize the look and feel of the payment form while keeping sensitive data processing isolated from your application infrastructure.

Example Implementation Example Implementation

You can see a working example of our hosted fields integration in this CodeSandbox:

Hosted Fields Example

Integration Steps Integration Steps

Install the SDK Install the SDK

Add the Hosted Fields SDK to your checkout page:

HTML

<script type="text/javascript" data-site-id="XXXXXXX" id="checkoutTools__script" src="https://sdk.checkouttools.com/v1.2/collect.js"></script>

If using Content Security Policy (CSP), update your directives:

Text

connect-src https://sdk.checkouttools.com
frame-src https://sdk.checkouttools.com
script-src https://sdk.checkouttools.com

Generate an auth token Generate an auth token

Before using the Forter Hosted Fields SDK, you will need to generate a temporary authentication token in your backend.

Initialize the SDK Initialize the SDK

Call the init method on the hosted fields SDK, passing in the auth token you received in the previous step:

JS

const forterCollect = await window.checkoutTools.collect.init({
    environment: 'sandbox',
    authToken: 'TOKEN.VALUE'
});

Add Hosted Fields to the checkout page form Add Hosted Fields to the checkout page form

HTML

<form>
    <div id="cc-holder"></div>
    <div id="cc-number"></div>
    <div id="cc-exp"></div>
    <div id="cc-cvc"></div>
</form>

Submit the form & receive a token Submit the form & receive a token

When the customer submits the form, call the submit() method to receive a single-use token.

JS

const tokenResult = await forterCollect.submit();
console.log(result);

Successful Response: (example)

JSON

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

Send the token to your backend for later processing Send the token to your backend for later processing

JS

const response = await fetch("https://example.org/pay", {
    method: "POST",
    body: JSON.stringify({ shippingAddress, cartItems, ..., forterToken: tokenResult.token }),
});

Client-Side Encryption Client-Side Encryption

For scenarios where you need to collect and encrypt card data directly (without using Hosted Fields iframes), you can use client-side encryption. This encrypts sensitive card data in the browser or mobile app before transmitting it to your server.

How It Works How It Works

The encryption uses a hybrid encryption scheme to securely encrypt card data:

  1. A random AES-256 key is generated for each encryption
  2. Card data is encrypted with AES-256-GCM (fast, authenticated encryption)
  3. The AES key is wrapped (encrypted) with RSA-OAEP-SHA256 using Forter's public key
  4. Only Forter's backend can decrypt the data using the corresponding private key

Encrypted Payload Format Encrypted Payload Format

Both JavaScript and mobile implementations produce the same JSON structure:

JSON

{
    "kid": "prod-v1",
    "alg": "RSA-OAEP-256",
    "enc": "A256GCM",
    "wrappedKey": "<base64-encoded-wrapped-aes-key>",
    "nonce": "<base64-encoded-12-byte-iv>",
    "ciphertext": "<base64-encoded-encrypted-data>",
    "tag": "<base64-encoded-16-byte-auth-tag>"
}
Field Description
kid Key ID for key rotation (e.g., prod-v1, sandbox-v1)
alg Algorithm used to wrap the AES key (RSA-OAEP-256)
enc Content encryption algorithm (A256GCM)
wrappedKey RSA-OAEP wrapped AES key (base64, 344 characters)
nonce AES-GCM initialization vector (base64, 16 characters / 12 bytes)
ciphertext Encrypted card data JSON (base64, variable length)
tag AES-GCM authentication tag (base64, 24 characters / 16 bytes)

JavaScript SDK JavaScript SDK

Installation Installation

The encryption module is included in the Hosted Fields SDK script:

HTML

<script type="text/javascript" data-site-id="XXXXXXX" id="checkoutTools__script" src="https://sdk.checkouttools.com/v1.2/encrypt.js"></script>

Usage Usage

JS

const encryptedPayload = await window.checkoutTools.encrypt.encryptTokenizeRequest({
    cardNumber: '4111111111111111',
    expirationMonth: '12',
    expirationYear: '2028',
    cvc: '123',
    cardHolderName: 'John Doe'
}, 'production' // environment: 'sandbox' | 'production'
);

// Send the encrypted payload to your backend
const response = await fetch('https://example.org/api/tokenize', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(encryptedPayload)
});

Parameters Parameters

Parameter Type Required Description
cardNumber string Yes The card number (PAN)
expirationMonth string Yes Two-digit expiration month (e.g., "12")
expirationYear string Yes Four-digit expiration year (e.g., "2028")
cvc string Yes Card security code (3-4 digits)
cardHolderName string Yes Name as it appears on the card
environment string No "sandbox"

Flutter / Dart Implementation Flutter / Dart Implementation

For Flutter mobile applications, add the following dependencies and encryption code to your project.

Dependencies Dependencies

Add to your pubspec.yaml:

YAML

dependencies:
  pointycastle: ^3.9.1
  cryptography: ^2.7.0

Encryption Code Encryption Code

Add this file to your project (e.g., lib/forter_encryption.dart):

Dart

import 'dart:convert';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
import 'package:pointycastle/asn1.dart';
import 'package:cryptography/cryptography.dart' as crypto;

enum ForterEnvironment { production, sandbox }

const _sandboxPublicKey = 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF4SVdEVEVGQ1haTlBaVkxidzFMeQpjUWpHMzFkb3NKQjRiVGd2S2lDcVBEY3d0MC9SWno1NkxVdGFNaVFiZFp1UTU3amkvSEhvUDExL2xhZW94WGNrCi9rK1FUREJDNDFqcTQ0N05aVjNzdWtLR1VaWFgrODFEWTFDbERsRXRTSFQzM1VmTVJjZ3p3bG81WGxIR01XUWQKL3BMbGVPSEN2N0h0TGs5alRtNjI3SWRwQkVONTZBdGV6SUl6NDNHVjcrRGJjYnBVbUZoQVVCd0lnWUwrYUhKbwpZdmdTTk5obmN3QUVJdE5JRzRPdGVKaWZKWlBiU0ZWb0JwUzBrcG43T0J5NHd3UzBVTmRsZ213NWdDWEJTQ0hmClJUNVVYZzE1N2VGMkNwcDhRakFGSlRHYzJ4VUJIS3NobjRGSTczcGRZYklUM2FhSHNielFpRUpxbk5HQXo5NzQKc1FJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t';
const _prodPublicKey = 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFza3JBTWp2b01BbUF4eGJQU0d5ZgpYdXl4Vy9uQlJucWRWd0N3UVhSOUY4Z1lEV2haTEErbU1qY3hZRzd6MjJYemZzeXBnNG4yTzV0c2VXb3RBek1yCmRRUnc3RVVFWjdZaWJ2bGFPS2JRNk1FZHVNYlVvQk4yWnJENlBrakZLMDI3OGIvZ2toNERKNi9mckcwMmJ6em0KQ1V1djAyNUw2dS9tV0QyZERpdDBXR2RmdjQ0cDdVSHltRk1mc1lqYUtFYVFKZkVsUVVpRHBrZzhBWkUvbXFGZgpSS0FvRzNGMDBMUzNKZStkME4rZ3VYd3VWalh1RE1Kemg2aHkwWWxPdkFZdWtZR1h6RFc0Yk5Yd2FQR0N2SmJGClRGSHdTcmRGQlRFbHZuRVdKcUEzYkdMb05tcis5Mkd5b0lTK3RFVmNpUU0zZ1c3ckxpaVFpU09pZXJVYkZqZ1EKa3dJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==';
const _keyIds = {
    ForterEnvironment.production: 'prod-v1',
    ForterEnvironment.sandbox: 'sandbox-v1',
};

Future<Map<String, String>> encryptCardData({
    required String cardNumber,
    required String expirationMonth,
    required String expirationYear,
    required String cvc,
    required String cardHolderName,
    ForterEnvironment environment = ForterEnvironment.sandbox,
}) async {
    final payload = {
        'cardNumber': cardNumber,
        'expirationMonth': expirationMonth,
        'expirationYear': expirationYear,
        'cvc': cvc,
        'cardHolderName': cardHolderName,
    };

final publicKeyB64 = environment == ForterEnvironment.production ? _prodPublicKey : _sandboxPublicKey;
    final publicKey = _parsePublicKey(utf8.decode(base64.decode(publicKeyB64)));
    final kid = _keyIds[environment]!;

final aes = crypto.AesGcm.with256bits();
    final secretKey = await aes.newSecretKey();
    final nonce = aes.newNonce();
    final plaintext = Uint8List.fromList(utf8.encode(jsonEncode(payload)));

final secretBox = await aes.encrypt(plaintext, secretKey: secretKey, nonce: nonce);

final aesKeyBytes = Uint8List.fromList(await secretKey.extractBytes());
    final encryptor = OAEPEncoding.withSHA256(RSAEngine())..init(true, PublicKeyParameter<RSAPublicKey>(publicKey));
    final wrappedKey = encryptor.process(aesKeyBytes);

return {
        'kid': kid,
        'alg': 'RSA-OAEP-256',
        'enc': 'A256GCM',
        'wrappedKey': base64.encode(wrappedKey),
        'nonce': base64.encode(secretBox.nonce),
        'ciphertext': base64.encode(secretBox.cipherText),
        'tag': base64.encode(secretBox.mac.bytes),
    };
}

RSAPublicKey _parsePublicKey(String pem) {
    final lines = pem.split('\n').where((l) => !l.startsWith('-----') && l.isNotEmpty).join('');
    final bytes = base64.decode(lines);
    final seq = (ASN1Parser(Uint8List.fromList(bytes)).nextObject() as ASN1Sequence);
    final pubKeyBytes = (seq.elements![1] as ASN1BitString).valueBytes!.sublist(1);
    final pubSeq = ASN1Parser(Uint8List.fromList(pubKeyBytes)).nextObject() as ASN1Sequence;
    return RSAPublicKey(
        (pubSeq.elements![0] as ASN1Integer).integer!,
        (pubSeq.elements![1] as ASN1Integer).integer!,
    );
}

Usage Usage

Dart

import 'forter_encryption.dart';  
  
// Encrypt card data  
final encrypted = await encryptCardData(  
    cardNumber: '4111111111111111',  
    expirationMonth: '12',  
    expirationYear: '2028',  
    cvc: '123',  
    cardHolderName: 'John Doe',  
    environment: ForterEnvironment.production,  
);  
  
// Send to your backend  
final response = await http.post(  
    Uri.parse('https://your-api.com/api/tokenize'),  
    headers: {'Content-Type': 'application/json'},  
    body: jsonEncode(encrypted),  
);  

Parameters Parameters

Parameter Type Required Description
cardNumber String Yes The card number (PAN)
expirationMonth String Yes Two-digit expiration month (e.g., "12")
expirationYear String Yes Four-digit expiration year (e.g., "2028")
cvc String Yes Card security code (3-4 digits)
cardHolderName String Yes Name as it appears on the card
environment ForterEnvironment No .sandbox

JS SDK Reference JS SDK Reference

Init Init

Initializes the Forter Hosted Fields SDK.

Standard Initialization Standard Initialization

Description

Initialize the Forter Hosted Fields SDK.

Parameters

Parameter Type Required Description
environment string No Tokenization environment to use - "sandbox"
authToken string Yes Authentication token retrieved from PCI Tokenization API

Example

JS

const forterCollect = await checkoutTools.collect.init({  
    environment: 'sandbox',  
    authToken: 'YOUR_AUTH_TOKEN'  
});  

Advanced Initialization Advanced Initialization

Description

This method returns a Promise that can be awaited and will surface any errors. Alternatively, you may provide an optional callback function which will be invoked asynchronously when the action completes.

Parameters

Parameter Type Required Description
options object Yes Configuration object
eventHandlers object No Object containing event handler functions

Options

Parameter Type Required Description
environment string No Tokenization environment to use - "sandbox"
authToken string Yes Authentication token retrieved from PCI Tokenization API
validationTrigger string No When to trigger fields validation.'ON_BLUR'

eventHandlers

Parameter Type Required Description
onLoad() No Triggered when fields are loaded. Note: Only triggered on first load.
onError(errors: {[fieldName: string]: {message: string, errorCode:string}}) No Triggered on form validation errors
onEnterPress() No Triggered when Enter key is pressed in any field
onEscapePress() No Triggered when Escape key is pressed in any field
onSuccess() No Triggered after successful form submission. Note: Consider showing a loading indicator until this event.
onValidityChange(fieldId:string, isValid:boolean, error?: {message: string, errorCode:string}) No Triggered when field validity changes
onCardBrandChange(cardBrand: string) No Triggered when card brand is detected

Example

JS

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);  
    }  
});  

Fields Fields

Add Fields Add Fields

Add one or more Hosted Fields to the form.

Notes:

Parameters

Parameter Type Required Description
fieldsDefinition Yes

Update Fields Update Fields

Description

Update one or more Hosted Fields in the form.

Parameters

Parameter Type Required Description
fieldsDefinition Yes

Submit Submit

Description

Once the customer finishes entering all the relevant fields in the payment form, this method will submit them securely to be stored by Forter.

Notes:

Reset Fields Reset Fields

Description

Reset all fields or a specific field in the form. If a field name is not provided, all of the fields will be reset.

Notes:

Parameters

Parameter Type Required Description
fieldName string No

Focus Focus

Description

Focus on a specific field in the form.

Parameters

Parameter Type Required Description
fieldName string No

Styling 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 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.

HTML

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

State classes 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

Input elements 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.

Examples Examples

Full Credit Card Details Form Full Credit Card Details Form

Create a common form containing fields for collecting a credit card’s number, security code, expiration date, and cardholder name.

HTML

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

Re-collecting CVC Re-collecting CVC

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