Detokenization Proxy

The Forter Secure proxy is a de-tokenization forward HTTP proxy that resides between the merchant’s backend and a third party API

The proxy allows the merchant to access APIs that normally require sensitive PCI data, without any need to process or store the data. The proxy will securely translate sensitive data that was previously tokenized by Forter back into its original form.

Sandbox Environment: https://pci-proxy-sandbox.checkouttools.com
Production PCI Environment: https://pci-proxy.checkouttools.com

The Forter Secure Proxy is a standard HTTP proxy and usage is possible with any standard HTTP library or tool.

Every request to the Forter Secure Proxy requires a token to be passed as a header with the name forter-token. Once this token is provided, “placeholders” can be used inside the request’s JSON payload instead of where PCI data should be placed.

Authentication

The Forter Secure Proxy uses HTTP Basic authentication in order to authenticate the merchant’s request. Provide the username and password via the “proxy-authorization” header in a basic auth format:
Header: proxy-authorization: Basic TO_BASE64(site_id:site_secret)

NOTE: The Forter Detokenization proxy shares the same credentials with the Tokenization APIs

You will also need to trust a special CA root certificate generated by Forter, that’s used to enable a secure connection with the proxy:

Sandbox Environment

-----BEGIN CERTIFICATE-----  
MIICuDCCAaACCQDx1qoYa/sqhTANBgkqhkiG9w0BAQsFADAeMQswCQYDVQQGEwJJ  
TDEPMA0GA1UECgwGRm9ydGVyMB4XDTIyMDgyNTEzMDMyMFoXDTMyMDgyMjEzMDMy  
MFowHjELMAkGA1UEBhMCSUwxDzANBgNVBAoMBkZvcnRlcjCCASIwDQYJKoZIhvcN  
AQEBBQADggEPADCCAQoCggEBAOSmh9WDhKaceirpwfVbfvpvHIQ0H5TmKZixjrzN  
QVGpeXd+e65fPjnDZtXpT06aN2Dqimh9VlEf6WX9E6zxeQZzxkmwUU5rwjWx0HQ2  
5RBh9LeRy0xlcF4rPDAaQd74eoQWPPJ+5GBtHjxWEiPAG0wB6yb++D1kDhvT5yXd  
qXqc9GL1HXex4OZexcTr5XJCPfRUZgC/78sF8H0Gg5vwOq8VprN0951TW7W1gHA4  
Hl51alZKv4VUCEhr1FDQfrW6N2IrahYhKYg38P3p+1KfT9N01/qa3yAp2JuDM4Md  
EWy/rOCZAVRREGXeH0xrhW5PzUbAD4wMK1zfqKbwa0b2WWsCAwEAATANBgkqhkiG  
9w0BAQsFAAOCAQEAqjx8Oas5GLqFH/vb9Hk2Zr9kCxgXm+66wdE+cdfILpFx8J9P  
J15aB37SgWk1V+6Ov29Z2znqJx4GOOnJlWLBAZhR9DSJDoDysJtRbrshXbY5U/YS  
bh0ESSuAfBiUzQu2cE2DhuLUABvzC789HeFyof16vdNtFIkxPvSJ/aHqemaWHbtO  
uS8/RYGmXH0N655p7TZor8FotfVYwqw8iE6Zt/8sUsd6DmBYsgxn3q6HwfzIAc2K  
f5vp/750ylKw2Dv2SWJO7OUZ20qIThq6+cJdbvTvrVlEQGVtzEVPKiDgw5dxMPNt  
3b8bjmIovuECw7123hwDQRhT2ccqr9Pv+cqZxw==  
-----END CERTIFICATE-----

Production Environment

-----BEGIN CERTIFICATE-----  
MIICnjCCAYYCCQCqd/JdClQxIDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQKDAZG  
b3J0ZXIwHhcNMjIxMTIxMTMyNDQ4WhcNMjcxMTIwMTMyNDQ4WjARMQ8wDQYDVQQK  
DAZGb3J0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4NgIfmQm5  
MkADTIxzOcKUAI4+XGBwmLobFQqtMluUn3pe4ChPFOURCbTxReSHZq0ywwFTUT51  
ARMh+tsUw+DzbH35T0YTvMXBPFzhikg9uFXUhc9EOn/mFPZxYm15LH78NAKXumPT  
+5r0+cg5TbJpPCnVZbXMTyQOcOQGGj4ZSZLF4IjGnQlKrvuA90UQL1PttUd8p6Vg  
/O8vO0IcrsA9QZ7vlJoVxi4JAI8GoMjC0b+8r+fihakQ146RPn9f5ZajpOu83btk  
n59nJWjwBG7JQd0gTb+5McywoFSGR+EMAaiMVREK1bGftewjmzuZdxgSecguL/Xy  
6qb3EJr8Mtj5AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGCmJlEiQEsBl5cud6I6  
2PiJpA1ZK7cjZ+fMPSCH7bDA/gmdc5oNG4Gb4QkBAwsNXpPeySbtkyzCWsGo5iSR  
kfhxrCN/EGyDuwYzM1ceNFUUt+V7TzDnmSTAA5xHCEBuKUE3RQdy+QqlNEwL/faI  
3P0TaiojiFypsz04tHAvXAGJzTETSZigzsexSamdUUyCcZIPgiJrXr/oNCKjapXz  
lirDWLLO5HU+wn+ZvOPrkUvRRHvrz/WQKMdqA2IU7OavI0MrRFt/wvw73wOZZhZo  
nWLGh2TlEk6wV+BT9qHZdMv045l9LG9PhnoPCt5k4RDGUs6pRYMzdZq870NHhO9p  
cCY=  
-----END CERTIFICATE-----
  1. Download one (or both of them, separated by a new line) these certificates into a file, and include it in your project
  2. In your programming HTTP framework, make sure to pass the certificate file as a new "trusted CA" in your proxy settings. Be careful not to override the framework default list of trusted CAs, but to add our custom one in addition to them.

Specifying tokens

Payment method token

One of the following pieces of data is required to use the Forter Detokenization Proxy:

  • A Forter token string, passed in via the proxy header forter-token
  • A 'token alias' string, previously specified in a /tokenize call to the Forter Tokenization API, passed in via the proxy headers forter-token-alias-key and forter-token-alias-value
    Example: forter-token-alias-key: zooz, forter-token-alias-value: ABC123456

CVC-only token

In order to facilitate periodic CVC tokenizations, the detokenization proxy supports an additional "CVC-only" token.
This feature allows authorizing a payment using your Payment platform with a multi-use token (AKA a saved customer card)

In order to use this feature, two headers are required:

  • A Forter token string, passed in via the proxy header forter-token
  • A Forter CVC token string, passed in via the proxy header forter-cvc-token

Placeholders

Placeholders are strings wrapped in double curly brackets. (See example below)
Place these inside the payload sent to the Detokenization Proxy, in order to create the request body expected by the final third party target.

  • card_number - The full credit card PAN
  • expiration_m - The month part of the expiration date. (e.g.: 8)
  • expiration_mm/expiration_month - The month part of the expiration date, as a two digit number. (e.g.: 08)
    expiration_yy - The year part of the expiration date, as a two digit number. (e.g.: 22)
    expiration_yyyy/expiration_year - The year part of the expiration date, as a four digit number. (e.g.: 2022)
  • cvc - The card’s security code. (also known as CVV) Note: The cvc is only available when using a single-use token.
  • card_holder_name - The card’s owner
  • card_bin - The card’s bin
  • card_last_four - The card’s last four digits
  • {{ text_field }} - Any 'extra' text field passed in to the tokenization apis can be inserted into the payload. Make sure to use the same exact casing and wording as the keys used in the extra field while creating the token.

Dynamic Headers

HMAC Signing

Payment service providers might require the request to be signed in order to verify its integrity. Since Detokenization Proxies alters the request body with PCI values, merchants can't implement request signing on their own.

Forter's Detokenization Proxy enables merchants to sign their requests seamlessly (implementation may differ between PSPs).

Generic HMAC signatures

Forter's Detokenization Proxy supports multiple HMAC algorithms, and a configurable signature template.

The following headers are supported:

  • forter-hmac-algo The HMAC hashing algorithm to use.
    Supported options:
    • sha256
    • sha512
    • sha1
    • md5
  • forter-hmac-target-header - The name of a header containing the computed signature, that will be appended to the outgoing request.
  • forter-hmac-secret - The shared secret part of the HMAC algorithm, used to sign the message.
  • forter-hmac-payload-template - A template describing the payload that will be the input to the HMAC algorithm. Special placeholder values denoted in curly braces are supported:
    • {{ request-body }} - The full outgoing request body, after detokenization, as a single string.
    • {{ http-method }} - The HTTP method used in the request
    • {{ http-path }} - The relative path used in the request
    • {{ header-name }} - Any header sent along the request can be used to construct the signing payload. The header name must be converted to lowercase.
Example (Fiserv PSP)

Calculate a SHA256 hash based HMAC using several headers and the request body as input, and store the result in a new header called message-signature:

curl 'https://prod.emea.api.fiservapps.com/sandbox/ipp/payments-gateway/v2/payments/' \
 -x 'https://SITE_ID:[email protected]' \
 --proxy-header 'forter-token: ftr1df95272f9e204c5791427722cc4ef407' \
 -X POST \
 -H 'Content-type: application/json' \
 -H 'client-request-id: 123456' \
 -H 'api-key: FISERV_API_KEY' \
 -H 'timestamp: 655846200000' \
 -H 'forter-hmac-target-header: message-signature' \
 -H 'forter-hmac-algo: sha256' \
 -H 'forter-hmac-secret: signing-secret' \
 -H 'forter-hmac-payload-template: {{ api-key }}{{ client-request-id }}{{ timestamp }}{{ request-body }}' \
 -d '
   {
     fullCreditCard: "{{ card_number }}",
     nameOnCard: "{{ card_holder_name }}",
     expirationMM: "{{ expiration_mm }}",
     expirationYY: "{{ expiration_yy }}",
   }'

PSP specific signing methods

Disclaimer: PSPs have different signing algorithms that we can quickly support, so please contact us about your specific needs.

Adding the following header: Forter-Ixopay-Signature: SHARED_IXOPAY_KEY, will result in a X-Signature and Date headers in the proxied request to Ixopay.

Examples

Securely making a connection to Forter's Standard 3DS integration (cURL)

curl 'https://api.forter-secure.com/v3/managed/orders/ORDER_ID' \
 -x https://SITE_ID:[email protected] \
 --proxy-header 'forter-token: ftr1df95272f9e204c5791427722cc4ef407' \
 --cacert [your CA certificate file path] \
 -X POST \
 -H 'Content-type: application/json' \
 -H 'x-forter-siteid: SITE_ID' \
 -H 'api-version: API_VERSION' \
 -H 'authorization: Basic dGVzdDo=' \
 --data '
{
  "orderId": "2356fdse0rr489",
  "orderType": "WEB",
  "authorizationStep": "PRE_AUTHORIZATION",
  "totalAmount": {
    "amountUSD": "99.95",
    "amountLocalCurrency": "105.55",
    "currency": "CAD",
    "amountMerchantMainCurrency": "125.95",
    "merchantMainCurrency": "EUR"
  },
  "totalDiscount": {
    "couponCodeUsed": "FATHERSDAY2015",
    "discountType": "COUPON"
  },
  "payment": [
   {
     "creditCard": {
         "nameOnCard": "{{ card_holder_name }}",
         "bin": "{{ card_bin }}",
         "lastFourDigits": "{{ card_last_four }}",
         "cardType": "credit",
         "expirationMonth": "{{ expiration_month }}",
         "expirationYear": "{{ expiration_year }}"
         "fullCreditCard": "{{ card_number }}"
     },
     "billingDetails": {
        "personalDetails": {
            "fullName": "Or Paul",
            "email": "[email protected]"
        },
        "phone": [],
        "address": {
            "zip": "90043", 
            "address1": "123 17th St",
            "city": "Santa Monica", 
            "region": "CA", 
            "country": "US"
        }
     },
     "amount": { // amount on first card
        "currency": "EUR",
        "amountLocalCurrency": "90.00",
        "amountUSD": "100.00"
     }
  },
  "primaryRecipient": {
    "personalDetails": {
      "firstName": "John",
      "lastName": "Smith",
      "gender": "MALE",
      "birthdate": "1987-05-22",
      "email": "[email protected]"
    },
    "address": {
      "address1": "235 Montgomery st.",
      "address2": "Ste. 1110",
      "zip": "94104",
      "city": "San Francisco",
      "region": "CA",
      "country": "US",
      "company": "Generic Corp. ltd.",
      "savedData": {
        "usedSavedData": true,
        "choseToSaveData": false
      }
    },
    "comments": {
      "userCommentsToMerchant": "Please wrap with care!!",
      "messageToBeneficiary": "Enjoy the gift John!",
      "merchantComments": "Shipping delayed"
    }
  },
  "phoneOrderInformation": {
    "customerWebId": "123456789",
    "callerFirstName": "John",
    "callerLastName": "Smith",
    "callerId": "2121234567",
    "callStartTime": 1412345911,
    "callDuration": 4,
    "remarks": "The customer is buying the product for a friend",
    "merchantAgentData": {
      "merchantAgentName": "John Smith",
      "merchantAgentId": "HG36885TZ"
    }
  },
  "historicalData": {
    "orderStatus": "COMPLETED",
    "merchantOrderStatus": "Shipped",
    "fraud": "FRAUD_CHARGEBACK"
  }
}
'

Securely making a connection to Forter's Advanced 3DS integration (cURL)

curl 'https://api.forter-secure.com/v3/adaptive-auth/3ds/init' \
 -x https://SITE_ID:[email protected] \
 --proxy-header 'forter-token: ftr1df95272f9e204c5791427722cc4ef407' \
 --cacert [your CA certificate file path] \
 -X POST \
 -H 'Content-type: application/json' \
 -H 'x-forter-siteid: SITE_ID' \
 -H 'api-version: API_VERSION' \
 -u "SITE_SECRET:''" \
 -d '{"orderId":"235631407","fullCreditCard":"{{ card_number}}","nameOnCard":"{{ card_holder_name }}","expirationMonth":"{{ expiration_month }}","expirationYear":"{{ expiration_year }}"}'}'

Securely making a connection to Forter's Advanced 3DS integration using a Forter token (NodeJS)

// NOTE: We must trust the require proxy CA certificate. In NodeJS, this can be done in one of two ways:  
// 1. Saving the CA certificate to a file, and using the NODE_EXTRA_CA_CERTS environment variable  
// 2. Supplying the CA string itself in an httpsAgent (shown below)

import HttpsProxyAgent from "https-proxy-agent";  
import url from "url";  
import axios from "axios";  
import fs from "fs/promises";

const secureProxyHttpsAgent = new HttpsProxyAgent({  
 ...url.parse("https://pci-proxy-sandbox.checkouttools.com"),  
 ca: [await fs.readFile('./forter-proxy-ca.pem')], // Contains Forter's custom CA certificate  
 headers: {  
   "proxy-authorization": `Basic ${btoa('site_id:site_secret')}`,  
   "forter-token": "ftr1df95272f9e204c5791427722cc4ef407",  
 },  
});

// Securely making a connection to Forter's 3DS API  
axios.post(  
 "https://api.forter-secure.com/v3/adaptive-auth/3ds/init",  
 {  
   fullCreditCard: "{{ card_number }}",  
   nameOnCard: "{{ card_holder_name }}",  
   orderId: "235631407",  
   expirationMonth: "{{ expiration_month }}",  
   expirationYear: "{{ expiration_year }}",  
 },  
 { 
   httpsAgent: secureProxyHttpsAgent,
   headers: {
     'x-forter-siteid': 'SITE_ID',
     'api-version': 'API_VERSION'
   },
   auth: {
     username: 'SITE_SECRET',
     password: ''
   }
 }  
);

Authorizing a transaction using worldpay's authorizations api and a token alias (NodeJS)

// NOTE: We must trust the require proxy CA certificate. In NodeJS, this can be done in one of two ways:  
// 1. Saving the CA certificate to a file, and using the NODE_EXTRA_CA_CERTS environment variable  
// e.g.: export NODE_EXTRA_CA_CERTS=[your CA certificate file path]
// 2. Extending NodeJS list of trusted CAs (shown below)

import HttpsProxyAgent from "https-proxy-agent";  
import url from "url";  
import axios from "axios";  
import fs from "fs/promises";
import tls from 'tls';

const secureProxyHttpsAgent = new HttpsProxyAgent("https://pci-proxy-sandbox.checkouttools.com",
{  
  // Passing the "ca" option will override the default Mozilla trusted CA bundle, so we need
  // to specify it explicitly again.
  ca: [...tls.rootCertificates, await fs.readFile('./forter-proxy-ca.pem')], // Contains Forter's custom CA certificate  
  headers: {  
    "proxy-authorization": `Basic ${btoa('site_id:site_secret')}`,  
    "forter-token-alias-key": "zooz",  
    "forter-token-alias-value": "ABCABC123",  
  },  
});

// Securely calling worldpay's authorizations API  
axios.post(  
 "https://try.access.worldpay.com/payments/authorizations",

  // Note: we are using a stringified payload as worldpay uses integers to represent dates which becomes  
  // invalid JSON when replaced with our placeholders  
  `{
    "transactionReference": "Memory265-13/08/1876",
    "merchant": {
      "entity": "MindPalaceLtd"
    },
    "instruction": {
      "narrative": {
        "line1": "Mind Palace"
      },
      "value": {
        "currency": "GBP",
        "amount": 250
      },
      "paymentInstrument": {
        "type": "card/plain",
        "cardNumber": "{{ card_number }}",
        "cardExpiryDate": {
          "month": {{ expiration_month }},
          "year": {{ expiration_year }}
        }
      }
    }
  }`,  
  { httpsAgent: secureProxyHttpsAgent }  
);

Securely paying using a newly collected CVC token with Stripe (NodeJS)

// NOTE: We must trust the require proxy CA certificate. In NodeJS, this can be done in one of two ways:  
// 1. Saving the CA certificate to a file, and using the NODE_EXTRA_CA_CERTS environment variable  
// e.g.: export NODE_EXTRA_CA_CERTS=[your CA certificate file path]
// 2. Extending NodeJS list of trusted CAs (shown below)

import HttpsProxyAgent from "https-proxy-agent";  
import url from "url";  
import Stripe from "stripe";  
import tls from 'tls';

const stripe = Stripe("sk_test_********************");

const secureProxyHttpsAgent = new HttpsProxyAgent("https://pci-proxy-sandbox.checkouttools.com",{  
  // Passing the "ca" option will override the default Mozilla trusted CA bundle, so we need
  // to specify it explicitly again.
  ca: [...tls.rootCertificates, await fs.readFile('./forter-proxy-ca.pem')],
  headers: {  
    "proxy-authorization": `Basic ${btoa('site_id:site_secret')}`,  
    "forter-token": "ftr1df95272f9e204c5791427722cc4ef407",  
  },  
});

stripe.setHttpAgent(secureProxyHttpsAgent);

const cvcToken = await stripe.tokens.create({  
  cvc_update: { cvc: "{{ cvc }}" },  
});

const paymentIntent = await stripe.paymentIntents.create({  
  payment_method: 'abcd',  
  customer: '1234',  
  amount: 1099,  
  currency: 'usd',  
  confirmation_method: 'manual',  
  confirm: true,  
  payment_method_options: {card: {cvc_token: cvcToken}},  
});