# POST /sandbox/purchase/{productId} — Тестовая покупка

Симуляция покупки без денег.

`sandbox-success` → `201`, `sandbox-insufficient-balance` → `402`, `sandbox-unavailable` → `409`, любой другой ID → `404` с подсказкой.

`idempotencyKey` и `webhookUrl` работают так же, как в боевом API. Вебхук приходит только для успешного заказа, подписан тем же секретом и содержит `"sandbox": true`; до 3 попыток.

Тестовые заказы хранятся 24 часа. Лимит песочницы отдельный и строже боевого.

Авторизация: заголовок `X-Entity-Key`.

**Параметры пути**

| Имя | Тип | Обязателен | Описание |
|---|---|---|---|
| `productId` | string | да | ID товара песочницы. Пример: `sandbox-success`. |

**Тело запроса (JSON, до 10 КБ)**

| Имя | Тип | Обязателен | Описание |
|---|---|---|---|
| `idempotencyKey` | string | нет | Как в боевом API, до 128 символов. Пример: `order-7731`. |
| `webhookUrl` | string (URL) | нет | Куда отправить уведомление об исходе заказа. Только https, до 2048 символов, без логина/пароля в URL, не localhost и не внутренние адреса. Пример: `https://shop.example.com/hooks/merchant`. |

**Ответы**

- `201 Created` — Успех

```json
{
  "status": "success",
  "order": {
    "id": "sandbox_px8cqEgyiFCA",
    "clientId": 7,
    "productId": "sandbox-success",
    "quantity": 1,
    "idempotencyKey": "sb-1",
    "price": 100,
    "currency": "RUB",
    "status": "completed",
    "failureReason": null,
    "webhookUrl": null,
    "webhookStatus": null,
    "webhookAttempts": 0,
    "sandbox": true,
    "createdAt": "2026-11-13T12:36:52.979Z",
    "updatedAt": "2026-11-13T12:36:52.980Z"
  }
}
```

- `201 Created` — Повтор запроса

```json
{
  "status": "success",
  "order": {
    "id": "sandbox_px8cqEgyiFCA",
    "clientId": 7,
    "productId": "sandbox-success",
    "quantity": 1,
    "idempotencyKey": "sb-1",
    "price": 100,
    "currency": "RUB",
    "status": "completed",
    "failureReason": null,
    "webhookUrl": null,
    "webhookStatus": null,
    "webhookAttempts": 0,
    "sandbox": true,
    "createdAt": "2026-11-13T12:36:52.979Z",
    "updatedAt": "2026-11-13T12:36:52.980Z"
  },
  "replayed": true
}
```

- `402 Payment Required` — Недостаточно средств

```json
{
  "status": "error",
  "message": "insufficient_balance",
  "order": {
    "id": "sandbox_LO3g828mKFl_",
    "clientId": 7,
    "productId": "sandbox-insufficient-balance",
    "quantity": 1,
    "idempotencyKey": null,
    "price": 100,
    "currency": "RUB",
    "status": "failed",
    "failureReason": "insufficient_balance",
    "webhookUrl": null,
    "webhookStatus": null,
    "webhookAttempts": 0,
    "sandbox": true,
    "createdAt": "2026-11-13T12:36:52.979Z",
    "updatedAt": "2026-11-13T12:36:52.980Z"
  }
}
```

- `409 Conflict` — Недоступен

```json
{
  "status": "error",
  "message": "Product is not available for purchase"
}
```

- `404 Not Found` — Неизвестный товар

```json
{
  "status": "error",
  "message": "Product not found",
  "hint": "Sandbox product ids: sandbox-success, sandbox-insufficient-balance, sandbox-unavailable"
}
```

- `429 Too Many Requests` — Лимит песочницы

```json
{
  "status": "error",
  "message": "Too many sandbox requests, try again later"
}
```

- `503 Service Unavailable` — Песочница недоступна

```json
{
  "status": "error",
  "message": "Sandbox is temporarily unavailable"
}
```

Веб-версия: https://docs.merchant.dbdlobby.com/ru/reference/sandbox-purchase
