Server-to-server quickstart
This guide walks you through making your first server-to-server payment using the Centrobill API. By the end, you'll have tokenized a card, charged a customer, handled the payment response, and set up subsequent payments — all from your server.
Before you begin
Make sure you have the following:
- A Centrobill merchant account. Contact [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
- Your payment form collects card details in the customer's browser.
- The browser posts the card data directly to
POST /tokenize. The gateway returns a short-lived token. - Your server submits the token to
POST /paymentalong with the SKU, consumer, and URL details. - The gateway returns a synchronous response with an initial payment status.
- If 3DS is required, you redirect the customer to the challenge URL in the response. See 3DS transaction flow.
- The gateway sends the final payment status to your
ipnUrlvia IPN callback.
Choose your integration approach
The server-to-server integration supports two payment flows. Both use POST /payment to submit the charge, but differ in how card data is passed.
| Flow A: Tokenize, then pay | Flow B: Submit direct card data | |
|---|---|---|
| How it works | Card data is posted from the browser to POST /tokenize. The token is then used in POST /payment | Card details are sent directly in the POST /payment request body |
| PCI scope | Reduced: raw card data never reaches your server | Higher: your server handles raw card data |
| When to use | Recommended for most integrations | When your infrastructure is PCI DSS certified |
Flow A: Tokenize, then pay
Step A1: Tokenize the card
Tokenization happens client-side. Post card data directly from the customer's browser to POST /tokenize. This keeps raw card data off your servers and reduces PCI scope.
Required headers:
| 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.
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": "Jane Smith",
"zip": "10001"
}'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 and retry immediately.
Step A2: Submit the payment
Submit a payment request from your server to POST /payment using the token. 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": "Jane",
"lastName": "Smith",
"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" |
paymentSource.token | The token from Step A1 |
sku.siteId | Your site ID from the Merchant Portal |
sku.price | Array of price objects. Use offset: "0d" for an immediate charge |
consumer.ip | The customer's IP address, required for fraud detection |
url.ipnUrl | Your IPN endpoint. Centrobill posts the final payment status here |
url.redirectUrl | The URL the customer is redirected to after 3DS authentication |
Flow B: Submit direct card data
Submit card details directly in the POST /payment request body. No separate tokenization step is required. This call requires your API key in the Authorization header.
Note: This flow requires your server infrastructure to be PCI DSS certified, as raw card data passes through your server.
curl --request POST \
--url https://stage.api.centrobill.com/payment \
--header 'Authorization: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"paymentSource": {
"type": "card",
"number": "4024007179366348",
"expirationMonth": "12",
"expirationYear": "25",
"cvv": "009",
"cardHolder": "Jane Smith"
},
"sku": {
"title": "Premium Monthly",
"siteId": "983759353",
"price": [
{
"offset": "0d",
"amount": 19.99,
"currency": "USD",
"repeat": false
}
]
},
"consumer": {
"id": "user-00123",
"email": "[email protected]",
"firstName": "Jane",
"lastName": "Smith",
"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 "card" |
paymentSource.number | Full card number (12–19 digits) |
paymentSource.expirationMonth | 2-digit expiry month |
paymentSource.expirationYear | 2-digit expiry year |
consumer.firstName | Required for card payments |
consumer.lastName | Required for card payments |
consumer.ip | The customer's IP address, required for fraud detection |
url.ipnUrl | Your IPN endpoint. Centrobill posts the final payment status here |
url.redirectUrl | The URL the customer is redirected to after 3DS authentication |
Handle the response
This step applies to both flows. A successful request 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",
"paymentAccountId": "ae853c41-2d85-4c8a-9a61-3a5daeea311d"
}
},
"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 | Redirect the customer to the 3DS URL; wait for IPN |
fail | Payment declined | Show an error to the customer |
When payment.action is "redirect", the response includes a payment.url field containing the 3DS challenge URL. Redirect the customer to that URL. The payment.status will be pending until the customer completes the challenge and the gateway receives the final result from the payment provider. See 3DS transaction flow for the full step-by-step sequence.
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
Note
Always use the IPN callback as the source of truth for the final payment status. The synchronous response provides an initial status only. Network issues can cause it to time out even when the charge succeeds.
Receive the IPN callback
After the payment completes, Centrobill sends an HTTP POST to your ipnUrl with the final status.
{
"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",
"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, 201, or 202 as quickly as possible to acknowledge receipt. Do any heavy processing asynchronously after acknowledging. If Centrobill doesn't receive one of these status codes, it retries the IPN according to the documented retry schedule.
Make subsequent payments
Once the initial payment is complete, you don't need the card number again. Use the paymentAccountId or consumer.id from the IPN to charge the customer without re-entering card details.
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.
curl --request POST \
--url https://stage.api.centrobill.com/payment \
--header 'Authorization: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"paymentSource": {
"type": "paymentAccountId",
"paymentAccountId": "ae853c41-2d85-4c8a-9a61-3a5daeea311d"
},
"sku": {
"name": "PREMIUM_MONTHLY"
},
"consumer": {
"id": "cb-gateway-id",
"ip": "203.0.113.45"
},
"url": {
"ipnUrl": "https://your-server.example.com/webhooks/centrobill",
"redirectUrl": "https://your-server.example.com/payment/success"
}
}'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://stage.api.centrobill.com/payment \
--header 'Authorization: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"paymentSource": {
"type": "consumer",
"id": "cb-gateway-id"
},
"sku": {
"name": "PREMIUM_MONTHLY"
},
"consumer": {
"id": "cb-gateway-id",
"ip": "203.0.113.45"
},
"url": {
"ipnUrl": "https://your-server.example.com/webhooks/centrobill",
"redirectUrl": "https://your-server.example.com/payment/success"
}
}'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 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. 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 x-requested-with: XMLHttpRequest is present on the tokenize request. The response body includes an errors object identifying the specific field.
401 Unauthorized
Your API key is missing or incorrect on 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 are short-lived. If you receive an error referencing an invalid or expired token, re-tokenize the card and retry immediately.
payment.status is pending
Check payment.action in the response. If it is "redirect", redirect the customer to payment.url to complete the 3DS challenge, then wait for the IPN callback before fulfilling the order. If there is no redirect URL, the payment is resolving asynchronously — wait for the IPN. See 3DS transaction flow.
Support
For further assistance, contact the Merchant Support team at [email protected].
