# 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

You need a `recurring_token` from a completed order — see [creating a customer token at
checkout](/checkout/recurring/minting-a-token/) — and the merchant ID that created it. A
token can't be charged under a different MID.

## Step by step

### 1. Post the charge

```http
POST /customer-token/v1/tokens/{customerToken}/order
Authorization: Basic <token>
Content-Type: application/json
```

```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

A success is small:

```json
{
  "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

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:

- **`403` with `PAYMENT_METHOD_FAILED` is 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 the `correlation_id` from the body for support.
- **An empty `400` is 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](/api/customer-tokens/read-token/) to tell which: a cancelled token reads back with `status: CANCELLED`.
- **An empty `404` means there is no such token.** Check the id you stored rather than retrying. A token that exists but belongs to another merchant ID answers `400` with `BAD_VALUE` instead.
- **An empty `500` is 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

| 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](/api/errors/#errors-customer-token) carries the coded errors with the
causes behind each one.

## Check that it works

In Playground:

1. `POST` a charge against a token you created and confirm you get an `order_id` and `fraud_status: ACCEPTED`.
2. Confirm the resulting order appears in Order Management, captured if you sent `auto_capture: true`.
3. Charge a token id you've deliberately mangled and confirm your code handles the empty `404` without retrying forever.
4. Cancel a token, charge it and confirm the empty `400` stops your retries.

## Related

- [Capture an order](/order-management/capture/) — collecting charges made without `auto_capture`
- [Charge a customer later](/checkout/recurring/) — how the two halves fit together
- [Order management](/order-management/) — where every token charge lands
- [Checkout API reference](/api/checkout/) — the order schema in full
