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-----
- Download one (or both of them, separated by a new line) these certificates into a file, and include it in your project
- 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 headersforter-token-alias-key
andforter-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 PANexpiration_m
/expiration_month
- The month part of the expiration date. (e.g.: 8)expiration_mm
- 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 ownercard_bin
- The card’s bincard_last_four
- The card’s last four digitsnetwork_token
- The card's associated network token.network_token_cryptogram
- A one-time cryptogram to use with the network token.network_token_eci
- Network token's electronic commerce indicator{{ 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 theextra
field while creating the token.
Handling Errors
In order to avoid confusion between the Detokenization's proxy own error status codes, and the final third party target's, special HTTP status codes are used:
- 407 - Proxy Authentication error, please check the credentials supplied to the Detokenization Proxy.
- 502 - Proxied target could not be reached because of a network error.
- 555 - Supplied token could not be found.
- 556 - Validation error, please check all inputs passed to the Detokenization Proxy.
- 565 - An unexpected error has occurred.
- Anything Else - Directly relayed from the target's response. Please note that standard error codes like 404 or 500 do not indicate an error with Forter's Detokenization proxy.
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_mm }}",
"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}},
});
Updated 2 months ago