Tokenization quickstart
This guide shows you how to make your first payment using the Centrobill tokenization flow. You’ll learn how to tokenize a card, charge a customer, handle the payment response, and prepare for future payments — all from your server.
Tokenization applies to credit and debit card payments only.
Before you begin
Make sure you have the following:
- A Centrobill merchant account. Contact Centrobill support at [email protected] to request access.
- Your API key (the
Authorizationheader value), available in the Merchant Portal. - A site ID or SKU name (product identifier) created in the Merchant Portal or via the Site API and SKU API respectively. You need only one of these to initiate a payment.
Note: All examples in this guide use the Stage environment at
https://stage.api.centrobill.com. Replace withhttps://api.centrobill.comwhen you're ready to go live.
How the flow works
The tokenization flow has two stages:
- Initial payment — tokenize the card on the client side, then use the token to submit the first payment from your server.
- Subsequent payments — use the
paymentAccountIdorconsumer.idreturned from the initial payment to charge the customer again without re-entering card details.
Every card must be tokenized before its first use. Don't skip tokenization for returning customers who add a new card.
Step 1: Tokenize the card
Tokenization happens client-side. Never send raw card data to your server. Instead, tokenize it first. Tokenization replaces sensitive card details with a short-lived token that you use for the actual payment call.
Required headers for the tokenize request:
| Header | Value |
|---|---|
Content-Type | application/json |
x-requested-with | XMLHttpRequest |
Note: Do not include your API key in the tokenize request. This endpoint does not require authentication.
Send a POST request to /tokenize with the cardholder's details:
curl --request POST \
--url https://stage.api.centrobill.com/tokenize \
--header 'Content-Type: application/json' \
--header 'x-requested-with: XMLHttpRequest' \
--data '{
"number": "4024007179366348",
"expirationYear": "25",
"expirationMonth": "12",
"cvv": "009",
"cardHolder": "Jonny Sonny",
"zip": "10001"
}'Required fields:
| Field | Description |
|---|---|
number | Full card number (12–19 digits) |
expirationYear | 2-digit expiry year |
expirationMonth | 2-digit expiry month |
A successful response returns a token and its expiry timestamp:
{
"token": "d86bdcbb-c369-46d9-a9d0-9c5010e3bfdb",
"expireAt": 1611735028
}Save the token value — you'll use it in the next step. Tokens are short-lived; if the token expires before you submit the payment, re-tokenize the card and retry immediately.
Step 2: Submit the initial payment
With the token, submit a server-to-server payment request to /payment. This call requires your API key in the Authorization header.
curl --request POST \
--url https://stage.api.centrobill.com/payment \
--header 'Authorization: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"paymentSource": {
"type": "token",
"token": "d86bdcbb-c369-46d9-a9d0-9c5010e3bfdb"
},
"sku": {
"title": "Premium Monthly",
"siteId": "983759353",
"price": [
{
"offset": "0d",
"amount": 19.99,
"currency": "USD",
"repeat": false
}
]
},
"consumer": {
"id": "user-00123",
"email": "[email protected]",
"firstName": "Jonny",
"lastName": "Sonny",
"country": "USA",
"ip": "203.0.113.45"
},
"url": {
"ipnUrl": "https://your-server.example.com/webhooks/centrobill",
"redirectUrl": "https://your-server.example.com/payment/success"
}
}'Required fields:
| Field | Description |
|---|---|
paymentSource.type | Must be "token" for the initial payment |
paymentSource.token | The token from Step 1 |
sku.siteId | Your site ID from the Merchant Portal |
sku.price | Array of price objects. Use offset: "0d" for an immediate charge |
consumer.id | Your internal user ID |
consumer.ip | The customer's IP address, required for fraud detection |
url.ipnUrl | Your IPN endpoint. Centrobill posts the final payment status here |
Step 3: Handle the payment response
A successful charge returns HTTP 201 with a payment object:
{
"payment": {
"code": "0",
"description": "APPROVED",
"action": "charge",
"mode": "sale",
"status": "success",
"amount": 19.99,
"currency": "USD",
"orderId": "235324530",
"transactionId": "12345",
"source": {
"method": "card",
"number": "402400****6348",
"brand": "visa",
"bankName": "BENDIGO AND ADELAIDE BANK, LTD",
"bankCountry": "AUS",
"expirationMonth": 2,
"expirationYear": 27,
"paymentAccountId": "d86bdcbb-c369-46d9-a9d0-9c5010e3bfdb",
"disabled": true
}
},
"consumer": {
"id": "cb-gateway-id",
"externalId": "user-00123",
"email": "[email protected]"
},
"subscription": {
"id": "sub-abc123",
"status": "active",
"renewalDate": "2025-01-17 13:43:02"
}
}Check payment.status to determine the outcome:
payment.status | Meaning | Next action |
|---|---|---|
success | Payment approved | Save transactionId |
pending | Awaiting 3DS or async confirmation | Wait for the IPN callback |
fail | Payment declined | Show an error to the user |
Save the following values from the response — you'll need them for subsequent payments:
payment.source.paymentAccountId— the stored card identifierconsumer.id— the Centrobill gateway consumer ID
Always treat your IPN callback as the source of truth for final payment status, not the synchronous response. Network issues can cause the synchronous call to time out even when the charge succeeds.
Step 4: Receive the IPN callback
After the payment completes, Centrobill sends an HTTP POST to your ipnUrl.
{
"payment": {
"code": "0",
"description": "APPROVED",
"action": "charge",
"status": "success",
"transactionId": "txn-7890",
"amount": 19.99,
"currency": "USD",
"source": {
"method": "card",
"brand": "visa",
"number": "402400****6348",
"paymentAccountId": "ae853c41-2d85-4c8a-9a61-3a5daeea311d"
}
},
"consumer": {
"id": "cb-gateway-id",
"externalId": "user-00123",
"email": "[email protected]"
},
"metadata": {},
"timestamp": {
"unixTime": "1670000000"
}
}Respond with HTTP 200 as quickly as possible to acknowledge receipt. If Centrobill doesn't receive a 200, it retries the callback. Do any heavy processing asynchronously after acknowledging.
HTTP/1.1 200 OKStep 5: Make subsequent payments
Once the initial payment is complete, you don't need the card number again. Use the paymentAccountId and consumer.id from the IPN to charge the customer in subsequent transactions.
There are two options depending on your use case:
Option 1: Charge using stored credentials
Use paymentAccountId when a customer has multiple saved cards, and you need to charge a specific one, or if you want to associate different cards with one account. Use consumer when a customer has one card, or when you want the gateway to automatically use the card from their last successful payment.
curl --request POST \
--url https://api.centrobill.com/tokenizeWithPaymentAccountId \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '
{
"paymentAccount": "03a470ee-6fb8-4e46-8d7c-b47f345cd09a",
"consumerId": "12345678",
"cvv": "009"
}Option 2: Re-tokenize with CVV, then charge
Use this option to display the customer's saved card on a payment form and handle 3DS challenges for EU users.
Step 5.1: Display the saved card to the customer
Show the masked card number from payment.source.number (available in the IPN or Feed API) so the customer can identify which card they're paying with. Prompt them to enter the CVV.
Step 5.2: Re-tokenize using the saved credentials + CVV
curl --request POST \
--url https://stage.api.centrobill.com/tokenizeWithPaymentAccountId \
--header 'Content-Type: application/json' \
--header 'x-requested-with: XMLHttpRequest' \
--data '{
"paymentAccountId": "ae853c41-2d85-4c8a-9a61-3a5daeea311d",
"consumerId": "cb-gateway-id",
"cvv": "009"
}'A successful response returns a new short-lived token:
{
"token": "f91cdebb-d480-57e0-b0e1-0d6121f4ceca",
"expireAt": 1611735028
}Step 5.3: Submit the payment using the new token
Use this token in POST /payment exactly as in Step 2, with paymentSource.type = "token".
All EU users will be redirected to complete the 3DS challenge. If the bank confirms that the flow is frictionless, the transaction will be completed without the 3DS challenge. Non-EU transactions will be completed without the 3DS challenge.
Before you go live
Before switching to the Production environment, complete the following steps:
- Test your integration in the Stage environment. Use synthetic payment credentials from the Test Data API to simulate approvals, declines, and 3DS flows.
- Verify your payment form complies with Visa and Mastercard requirements. See External payment form requirements.
- Confirm account activation with the Merchant Support team before processing live transactions. Your profile must be activated before production payments will be processed. Contact [email protected] to confirm.
- Switch the base URL from
https://stage.api.centrobill.comtohttps://api.centrobill.comin all requests.
Troubleshooting
400 Bad Request
A required field is missing or invalid. Check that all required headers are present. Missing Content-Type or x-requested-with on the tokenize request will cause this error. The response body includes an errors object identifying the specific field.
401 Unauthorized
Your API key is missing or incorrect in the /payment request. Verify the Authorization header matches your key exactly as it appears in the Merchant Portal.
403 Forbidden
Your account doesn't have permission for this operation, or the request IP isn't on the allowlist. Contact Centrobill support.
Token expired
Tokens from /tokenize and /tokenizeWithPaymentAccountId are short-lived. If you receive an error referencing an invalid or expired token, re-tokenize and retry immediately.
Payment status is pending
Some flows, particularly those involving 3DS, resolve asynchronously. Don't provision access based on the synchronous response. Wait for the IPN callback with payment.status = "success" before fulfilling the order.
Support
For further assistance, contact the Merchant Support team at [email protected].
