- Checkout
- Features
Charge a customer token
Charge a stored customer token through the Customer Token API, with no checkout and no customer present, and tell declines worth retrying from permanent ones.
Every charge after the first one goes to the Customer Token API. No checkout is rendered, the customer doesn’t have to be present and the result is a real order that lands in Order Management like any other.
Before you start
Section titled “Before you start”You need a recurring_token from a completed order — see creating a customer token at
checkout — and the merchant ID that created it. A
token can’t be charged under a different MID.
Step by step
Section titled “Step by step”1. Post the charge
Section titled “1. Post the charge”POST /customer-token/v1/tokens/{customerToken}/orderAuthorization: Basic <token>Content-Type: application/json{ "merchant_reference1": "45aa52f387871e3a210645d4", "auto_capture": true, "purchase_currency": "EUR", "order_amount": 999, "order_tax_amount": 0, "order_lines": [ { "type": "digital", "reference": "19-402", "name": "Streaming Service Monthly", "quantity": 1, "unit_price": 999, "tax_rate": 0, "total_amount": 999, "total_discount_amount": 0, "total_tax_amount": 0 } ]}auto_capture defaults to false, which leaves the charge authorized but not
collected. For a subscription you almost always want true, which captures the full amount
as soon as the order is created. Without it, each charge waits for you to capture it through
Order Management, in a single capture.
Send a shipping_address whole or not at all. Left out, the order ships to the address
stored with the token. Sent, it must carry every one of given_name, family_name, email,
street_address, postal_code, city and country — an empty string counts as missing —
or the charge is refused with 400 BAD_VALUE, naming the first field it found missing:
Bad value: shipping_address.email. There’s no merging with the stored address, so don’t
send only the field that changed.
Use the right token. Nothing in the API can tell you that you charged the wrong customer. Only your own bookkeeping can.
2. Read the result
Section titled “2. Read the result”A success is small:
{ "order_id": "a89ec121-1276-419d-882a-c343d58fd1bc", "fraud_status": "ACCEPTED", "authorized_payment_method": { "type": "card" }}A 200 always carries fraud_status: ACCEPTED, so there’s nothing to branch on there. If you
sent merchant_urls.confirmation, the response also echoes it back as redirect_url.
Store the order_id against your own record of the charge. It’s how you find the order
again in Order Management, and it’s the only handle you get.
3. Decide what a failure means before you retry
Section titled “3. Decide what a failure means before you retry”Only one failure carries an error code that describes the charge. Most of the rest come back as a bare status with no body, so branch on the status first:
403withPAYMENT_METHOD_FAILEDis worth retrying. The method behind the token didn’t work this time. Cards recover from temporary declines — insufficient funds, an issuer blip — so back off and try again rather than cancelling the subscription on the first miss. Keep thecorrelation_idfrom the body for support.- An empty
400is permanent. It means the token is no longer active — it was cancelled — or recurring isn’t enabled on your merchant account. Retrying will never succeed. Read the token to tell which: a cancelled token reads back withstatus: CANCELLED. - An empty
404means there is no such token. Check the id you stored rather than retrying. A token that exists but belongs to another merchant ID answers400withBAD_VALUEinstead. - An empty
500is ours, not theirs. Don’t involve the customer, but don’t assume nothing happened either — the outcome is unknown, so check for the order in the Kustom Portal before you charge again.
When retries on a PAYMENT_METHOD_FAILED keep failing, the token isn’t going to start
working on its own. The only fix is asking the customer to update their payment method —
which means a new first purchase and a new token, not a repair of the old one. Design
that path before you need it: a subscription that silently stops charging is worse than
one that asks.
Troubleshooting
Section titled “Troubleshooting”| Status | Body | Meaning | Retry? |
|---|---|---|---|
403 | PAYMENT_METHOD_FAILED | The charge failed | Yes, with backoff, then ask the customer to update |
400 | empty | The token is cancelled, or recurring isn’t enabled on your account | No |
400 | BAD_VALUE | A field in the request is invalid (named in error_messages) — including any shipping_address field left empty — or the token belongs to another merchant ID | Not until you fix the request |
404 | empty | No such token | No |
500 | empty | Kustom fault | Only after checking the order wasn’t created |
The error catalogue carries the coded errors with the causes behind each one.
Check that it works
Section titled “Check that it works”In Playground:
POSTa charge against a token you created and confirm you get anorder_idandfraud_status: ACCEPTED.- Confirm the resulting order appears in Order Management, captured if you sent
auto_capture: true. - Charge a token id you’ve deliberately mangled and confirm your code handles the empty
404without retrying forever. - Cancel a token, charge it and confirm the empty
400stops your retries.
Related
Section titled “Related”- Capture an order — collecting charges made without
auto_capture - Charge a customer later — how the two halves fit together
- Order management — where every token charge lands
- Checkout API reference — the order schema in full
This page is also available as Markdown — append .md to its URL — and is indexed in llms.txt.