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 Authorization header 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 with https://api.centrobill.com when you're ready to go live.


How the flow works

  1. Your payment form collects card details in the customer's browser.
  2. The browser posts the card data directly to POST /tokenize. The gateway returns a short-lived token.
  3. Your server submits the token to POST /payment along with the SKU, consumer, and URL details.
  4. The gateway returns a synchronous response with an initial payment status.
  5. If 3DS is required, you redirect the customer to the challenge URL in the response. See 3DS transaction flow.
  6. The gateway sends the final payment status to your ipnUrl via 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 payFlow B: Submit direct card data
How it worksCard data is posted from the browser to POST /tokenize. The token is then used in POST /paymentCard details are sent directly in the POST /payment request body
PCI scopeReduced: raw card data never reaches your serverHigher: your server handles raw card data
When to useRecommended for most integrationsWhen 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:

HeaderValue
Content-Typeapplication/json
x-requested-withXMLHttpRequest

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:

FieldDescription
paymentSource.typeMust be "token"
paymentSource.tokenThe token from Step A1
sku.siteIdYour site ID from the Merchant Portal
sku.priceArray of price objects. Use offset: "0d" for an immediate charge
consumer.ipThe customer's IP address, required for fraud detection
url.ipnUrlYour IPN endpoint. Centrobill posts the final payment status here
url.redirectUrlThe 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:

FieldDescription
paymentSource.typeMust be "card"
paymentSource.numberFull card number (12–19 digits)
paymentSource.expirationMonth2-digit expiry month
paymentSource.expirationYear2-digit expiry year
consumer.firstNameRequired for card payments
consumer.lastNameRequired for card payments
consumer.ipThe customer's IP address, required for fraud detection
url.ipnUrlYour IPN endpoint. Centrobill posts the final payment status here
url.redirectUrlThe 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.statusMeaningNext action
successPayment approvedSave transactionId
pendingAwaiting 3DS or async confirmationRedirect the customer to the 3DS URL; wait for IPN
failPayment declinedShow 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 identifier
  • consumer.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:

  1. Test your integration in the Stage environment. Use synthetic payment credentials to simulate approvals, declines, and 3DS flows.
  2. Verify your payment form complies with Visa and Mastercard requirements. See External payment form requirements.
  3. Confirm account activation with the Merchant Support team before processing live transactions. Contact [email protected] to confirm.
  4. Switch the base URL from https://stage.api.centrobill.com to https://api.centrobill.com in 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].