# Endpoint (API v2): Корзина T-Bank — один рублёвый платёж на несколько товаров

> ℹ️ **EasyPay API v2.** Ручка второго поколения EasyPay API (база `https://api.appsign.me`): авторизация заголовком `X-Partner-Api-Key`, плоское тело запроса, идемпотентность через опциональный `client_token`, коды ошибок — в `UPPER_CASE`.

## Зачем это нужно

Корзина — это **один рублёвый платёж T-Bank за несколько ваших товаров**, у каждого своё количество и, при желании, своя цена. Покупатель платит одну сумму, а в **кассовом чеке каждая позиция идёт отдельной строкой**: название, цена за единицу, количество, сумма строки.

Используйте, когда клиент покупает комбинацию, а не один товар:

* тариф собирается из частей — «2 места в команде + 100 аккаунтов»;
* заказ из нескольких товаров каталога, который нужно оплатить одним платежом;
* AI-агент должен собрать такую ссылку по просьбе прямо в чате (через MCP — инструмент `create_partner_tbank_cart_payment`).

Для одного товара подходит и эта ручка, и [Рублёвый платёж T-Bank под заказ](https://docs.thenextgen.store/s/635a43c1-37fa-463b-bc7e-7ede7884fce2/doc/endpoint-api-v2-rublyovyj-platyozh-t-bank-pod-zakaz-ypmkgCKGjv).

> ⚠️ **Названия строк чека задать нельзя.** В чек и на страницу банка идут названия товаров из каталога — те, что прошли модерацию: кассовый чек выдаёт юридическое лицо EasyPay. Поле `description` в запросе **не принимается** (`INVALID_INPUT`). Нужна другая формулировка — заведите товар через `create_partner_ruble_payable_product`.

Рекуррентных списаний в рублях нет: корзина — разовая оплата.

## Как подключиться


1. API-ключ партнёра и разрешение `tbank_payment` — то же, что для рублёвого платежа на один товар.
2. Все товары корзины — **одобренные рублёвые товары** вашего каталога. Их `id` видны в `list_partner_invoiceable_products` (currency=RUB) и `list_partner_ruble_checkouts`.
3. Все товары одной корзины должны продаваться через **один и тот же терминал** (одна система налогообложения). Если товары на разных терминалах — разбейте корзину на отдельные платежи.

## Endpoint

```
POST https://api.appsign.me/create-partner-tbank-cart-payment
X-Partner-Api-Key: ваш-api-ключ
Content-Type: application/json
```

## Формат запроса

```json
{
  "items": [
    { "product_id": 101, "quantity": 2 },
    { "product_id": 102, "quantity": 100, "unit_amount": 85000 }
  ],
  "customer_email": "client@example.com",
  "external_order_id": "order-2417",
  "success_url": "https://example.com/thanks",
  "client_token": "order-2417"
}
```

| Параметр | Обязательный | Описание |
|----------|--------------|----------|
| `items`  | Да           | 1\..100 позиций. Каждый товар — не больше одной позиции: количество складывайте в `quantity`. |
| `items[].product_id` | Да           | Числовой `id` одобренного рублёвого товара. |
| `items[].quantity` | Да           | Количество, целое 1..9999. |
| `items[].unit_amount` | Нет          | Цена **одной единицы в копейках**: `85000` = 850 ₽. Не передали — берётся цена товара из каталога. |
| `customer_email` | Один из двух | Email покупателя — туда придёт чек. |
| `customer_phone` | Один из двух | Телефон покупателя, лучше `+79991234567`. |
| `external_order_id` | Нет          | **Ваш** номер заказа: буквы, цифры, `_` и `-`, до 64 символов. Вернётся в вебхуке (`Payment.Data.ep_order`) и в уведомлении в чат. |
| `success_url` / `fail_url` | Нет          | Куда вернуть покупателя после оплаты. Только `https://`. |
| `environment` | Нет          | `live` (по умолчанию) или `test`. |
| `client_token` | Нет          | Ваш идемпотентный токен. См. «Повторы». |

**Сумму платежа не передают** — сервер считает её сам: Σ (цена × количество) по всем позициям. Итог от 1 ₽ до 10 000 000 ₽.

## Формат ответа

HTTP-статус всегда 200 — смотрите поле `success`.

```json
{
  "success": true,
  "payment_id": 9200408148,
  "payment_url": "https://<страница оплаты банка>",
  "sbp_url": "https://<ссылка СБП>",
  "order_id": "6d56065e39f7467db98ced23",
  "amount_rub": 93300,
  "amount_kopecks": 9330000,
  "status": "NEW",
  "line_item_count": 2,
  "items": [
    { "product_id": 101, "product_name": "<название из каталога>", "unit_amount": 415000, "quantity": 2, "amount": 830000 },
    { "product_id": 102, "product_name": "<название из каталога>", "unit_amount": 85000, "quantity": 100, "amount": 8500000 }
  ],
  "duplicate": false
}
```

> ✅ **По возможности давайте покупателю** `sbp_url` — эквайринг по СБП дешевле карточного. Если придёт `null`, ведите на `payment_url`.

`items` — ровно те строки, что уйдут в кассовый чек (суммы в копейках). На странице банка покупатель видит общую сумму и название первой позиции с пометкой «и ещё N».

## Повторы

* Тот же запрос (тот же состав, цены, количества, покупатель, номер заказа) без `client_token` вернёт **тот же платёж** с `duplicate: true` — случайный повтор не создаёт второй.
* Нужен **отдельный** платёж того же состава — передайте новый `client_token`.
* Тот же `client_token` с другой корзиной вернёт `IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_PAYLOAD`.

## Ошибки

| `error_code` | Что значит |
|------------|------------|
| `INVALID_INPUT` | Пустая корзина или больше 100 позиций; товар повторяется; количество или цена не целые; товары на разных терминалах; сумма вне 1 ₽ … 10 000 000 ₽; нет ни email, ни телефона; передан `description`; неверный `external_order_id` или не `https://` адрес. Причина — в `error_message`. |
| `PRODUCT_NOT_FOUND` | Товара нет в вашем каталоге, он не рублёвый или ещё на модерации (причина — в `error_message`). |
| `PERMISSION_DENIED` | У ключа нет разрешения `tbank_payment`. |
| `OPERATION_IN_PROGRESS` | Такой же запрос ещё выполняется — дождитесь ответа. |
| `INTEGRATION_UPSTREAM_ERROR` / `INTEGRATION_TIMEOUT` | Банк не ответил или ответил ошибкой — повторите с новым `client_token`. |
| `INTERNAL_ERROR` | Внутренний сбой EasyPay, команда заботы уведомлена. |

## Как узнать, что заказ оплачен

Так же, как у платежа на один товар: вебхук `TBank.payment.succeeded` на ваш зарегистрированный адрес — [Webhook EasyPay: структура данных платежей T-Bank](https://docs.thenextgen.store/s/635a43c1-37fa-463b-bc7e-7ede7884fce2/doc/webhook-easypay-struktura-dannyh-platezhej-t-bank-IjWtNIwzgd). У корзины в `Payment.Data` поле `ep_product_id` = `null` (товаров несколько); сопоставляйте оплату с заказом по `ep_order`.


---

# T-Bank cart: one RUB payment for several products (EN)

A cart is **one T-Bank RUB payment for several of your products**, each with its own quantity and optionally its own price. The customer pays one total, and **every item is a separate line of the fiscal receipt** (name, unit price, quantity, line total). Via MCP the tool is `create_partner_tbank_cart_payment`.

Receipt line names are always the moderated catalogue names — `description` is rejected. One-time payment only.

**Endpoint:** `POST https://api.appsign.me/create-partner-tbank-cart-payment`, header `X-Partner-Api-Key`, permission `tbank_payment`. All products must be approved RUB products sold through the same terminal (same tax regime).

**Request:** `items` (1..100 of `{product_id, quantity 1..9999, unit_amount?}` — `unit_amount` is the price of ONE unit in **kopecks**, `85000` = 850 RUB, omitted → catalogue price; one entry per product), `customer_email` or `customer_phone` (one is required), optional `external_order_id`, `success_url`, `fail_url`, `environment`, `client_token`. The total is computed by the server: Σ price × quantity, 1 … 10,000,000 RUB.

**Response** (HTTP 200, check `success`): `payment_url`, `sbp_url` (prefer it; null → use `payment_url`), `payment_id`, `order_id`, `amount_rub`, `amount_kopecks`, `status`, `line_item_count`, `items[]` exactly as they go to the receipt, `duplicate`.

**Repeats:** an identical request returns the same payment with `duplicate: true`; pass a new `client_token` for a separate payment with the same composition; the same token with a different cart → `IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_PAYLOAD`.

**Errors:** `INVALID_INPUT`, `PRODUCT_NOT_FOUND`, `PERMISSION_DENIED`, `OPERATION_IN_PROGRESS`, `INTEGRATION_UPSTREAM_ERROR` / `INTEGRATION_TIMEOUT` (retry with a new `client_token`), `INTERNAL_ERROR`.

**Payment confirmation:** the `TBank.payment.succeeded` webhook, same as for a single product; in a cart `Payment.Data.ep_product_id` is `null` — match the payment to your order by `ep_order`.