# Purchases and order status

How a purchase works, when money is charged and returned, and where to get the key.

## Steps

1. Optionally check availability right before buying: [GET /products/{id}/availability](https://docs.merchant.dbdlobby.com/en/reference/product-availability).
2. [POST /purchase/{productId}](https://docs.merchant.dbdlobby.com/en/reference/purchase) with an `idempotencyKey`. The order is created and its price reserved from the balance in one operation. If the balance is short, no order is created (`402`).
3. A manually fulfilled product completes immediately. An automatically fulfilled product is bought from the supplier; after the key arrives the order is checked for a short while and only then becomes `completed`.
4. The key is in the `key` field of the purchase response or [GET /orders/{id}](https://docs.merchant.dbdlobby.com/en/reference/get-order). Webhooks never contain it.

## Statuses

| status | Meaning | Money |
|---|---|---|
| `pending` | In progress. Wait for the webhook or poll the order every 5–10 seconds. | Reserved |
| `completed` | Fulfilled, the key is available. | Charged |
| `failed` | Not fulfilled, see `failureReason`. | Returned |
| `cancelled` | Cancelled by the supplier. | Returned |

`completed`, `failed` and `cancelled` are final: the status never changes again.

## The 202 response

If the supplier purchase doesn't finish within about 50 seconds, the API answers `202` with the order in `pending`. This is not an error and not a reason to buy again: the order keeps going. Learn the outcome from the webhook or `GET /orders/{id}`. In rare cases, when the outcome has to be double-checked, the order stays `pending` longer — the money stays reserved all that time and comes back if the order is not fulfilled.

> [!NOTE]
> Set your HTTP client timeout to at least 90 seconds: a purchase can take up to ~50 seconds.
