Tokenization Service
Exchange a card number for a one-time-use TOKEN_GUID, with verified HMAC signing and follow-up sale request requirements.
Rather than submitting the credit card number (PMT_ID) with a transaction, a token ID may be used instead. This one time use, unique ID is acquired by sending a request to the token service endpoint.
| Parameter | Description |
|---|---|
card_pan Required |
Customer card number to be tokenized. |
request_api_version Required |
API Version (e.g., 4.14). |
site_id Required |
Merchant's website ID. |
unique_id Required |
Alphanumeric ID linked to the request (Max 32 chars). |
x-timestamp Required |
Format: YYYYMMDDHHMMSS (Uses UTC). Open for 5 minutes. |
x-signature Required |
HMAC_SHA256 Base16 signature generated using x-timestamp and unique_id with the secret key. |
The v4.14 PDF, section 4.8.1.1, documents x-signature as generated from x-timestamp, unique_id, card_pan, and site_id. We verified against the gateway directly, and the gateway actually validates hmac_sha256(timestamp || unique_id || site_id, site_key), with the PAN excluded from the signed message. Signing with the PAN included fails with error 121.
The PDF and the verified gateway behavior disagree here. This page documents the verified, working behavior: sign x-timestamp, unique_id, and site_id only, and omit card_pan from the signed string.
The site key used to compute the HMAC is a separate per-site HMAC secret issued by Inovio support. It is not the same as your req_password API password used on pmt_service.cfm requests. Contact your gateway support representative to obtain it.
A TOKEN_GUID replaces PMT_NUMB only, in a subsequent sale or auth request. You must still send pmt_expiry (and pmt_key, when the processor requires it) alongside TOKEN_GUID. Omitting pmt_expiry causes the API to respond with error 110, "Required field".
curl -X POST "https://api.inoviopay.com/payment/token_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "card_pan=4111111111111111&request_api_version=4.14&site_id=12345&unique_id=REQ999&x-timestamp=20241209230900&x-signature=8fa1f..."
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Model\{LineItem, Money, PaymentMethods};
use Inovio\Gateway\Request\TransactionRequest;
// The site key is a separate constructor argument, not your req_password.
$client = new InovioClient(
new Credentials('api_user', 'P@ssw0rd!', '12345'),
'SANDBOX',
siteKey: 'your-hmac-site-key'
);
$token = $client->tokenize(PaymentMethods::card('4111111111111111', '122026', '123'));
echo $token->token->guid(), "\n";
// The token replaces the PAN only — expiry travels with it automatically.
$sale = $client->sale((new TransactionRequest(
$token->token,
[new LineItem('SKU-1', 1, Money::of('10.00', 'USD'))]
))->withIdempotency('TOK-ORDER-1'));
echo $sale->status, "\n";
import { InovioClient, Money, PaymentMethods } from '@inovio/gateway-sdk';
// The site key is passed via client options, not req_password.
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX', siteKey: 'your-hmac-site-key' }
);
const token = await client.tokenize(PaymentMethods.card('4111111111111111', '122026', '123'));
console.log(token.token.guid);
// The token replaces the PAN only — expiry travels with it automatically.
const sale = await client.sale({
paymentMethod: token.token,
lineItems: [{ productId: 'SKU-1', count: 1, value: Money.of('10.00', 'USD') }],
idempotency: { xtlOrderId: 'TOK-ORDER-1' },
});
console.log(sale.status);
from inovio_gateway import Credentials, InovioClient, Money, PaymentMethods
from inovio_gateway.model import Idempotency, LineItem
from inovio_gateway.request import TransactionRequest
# The site key is a client constructor argument, not the API password.
client = InovioClient(
Credentials("api_user", "P@ssw0rd!", "12345"),
environment="SANDBOX",
site_key="your-hmac-site-key",
)
token = client.tokenize(PaymentMethods.card("4111111111111111", "122026", "123"))
print(token.token.guid)
# The token replaces the PAN only — expiry travels with it automatically.
req = TransactionRequest(
payment_method=token.token,
line_items=[LineItem("SKU-1", 1, Money.of("10.00", "USD"))],
idempotency=Idempotency(xtl_order_id="TOK-ORDER-1"),
)
sale = client.sale(req)
print(sale.status.value)
// The site key is set on InovioClient.Options, not the req_password field.
InovioClient.Options options = new InovioClient.Options();
options.siteKey = "your-hmac-site-key";
InovioClient client = new InovioClient(
new InovioClient.Credentials("api_user", "P@ssw0rd!", "12345"), options);
Tokenize.Result token = client.tokenize(
PaymentMethods.card("4111111111111111", "122026", "123"));
System.out.println(token.token().guid());
// The token replaces the PAN only — expiry travels with it automatically.
TransactionRequest req = new TransactionRequest(
token.token(), new LineItem("SKU-1", 1, Money.of("10.00", "USD")))
.idempotency("TOK-ORDER-1");
TransactionResult sale = client.sale(req);
System.out.println(sale.status());
{
"TOKEN_GUID": "3A393EBC3A266B9842FC55DB44B5401C697A5768",
"TOKEN_IP": "192.168.1.1",
"TOKEN_REQID": "73393758223"
}