> Источник: https://apipay.kz/guides/scheta-s-korzinoy-cart-items-ofd · Обновлено: 2026-07-06 · apipay.kz
> ApiPay — независимый сервис приёма платежей поверх вашего Kaspi Pay.

# Счета с корзиной (cart_items): как исправить ошибку 422?

**TL;DR.** Ошибки 422 вокруг `cart_items` означают одно: ваша организация работает с каталогом товаров (обычно это Kaspi ОФД / режим «Касса»), и счёт нужно собирать из позиций каталога, а не из «голой» суммы. Формат: `cart_items[]` с обязательными `catalog_item_id` и `count` (1–100 позиций), у каждой позиции каталога **должна быть задана цена**. Цену конкретной строки можно переопределить полем `price` прямо в запросе. Скидка — только `discount_percentage` (1–99%) на весь чек. Переданный `amount` при корзине игнорируется — сумма считается по позициям.

## Коротко

| Вопрос | Ответ |
|---|---|
| Когда нужна корзина | Организация с каталогом (Kaspi ОФД / «Касса»); без каталога `cart_items` слать нельзя |
| Схема позиции | `{ "catalog_item_id": 1, "count": 2 }` — оба поля обязательны |
| Позиций в счёте | От 1 до 100 |
| Цена позиции | Берётся из каталога; поле `price` в запросе переопределяет её для этой строки |
| Скидка | `discount_percentage` 1–99% на весь чек, только вместе с `cart_items` |
| `amount` при корзине | Игнорируется — сумма считается по позициям |
| Каталог через API | `POST /catalog` — до 100 товаров за запрос; обязательны `name`, `selling_price`, `unit_id` |
| Типовые 422 | «requires cart items» (старые интеграции), «has no price set», «does not belong», «has been deleted» |

## Почему счёт с одной суммой не проходит, а требует cart_items?

Если у организации подключена Kaspi ОФД (касса), Kaspi ожидает чек с позициями — поэтому у таких организаций в ApiPay включается каталог, и счёт собирается из его позиций. Классическая ошибка, которую видели интеграторы:

```json
{ "message": "This organization requires cart items. Include cart_items in request." }
```

Важное обновление: **с 9 июня 2026 жёсткое требование корзины снято** — счёт без `cart_items` (только `amount`) снова проходит и у организаций с каталогом, потому что режим «Касса» в Kaspi сам по себе не означает обязательный каталог. Если ваша интеграция всё ещё получает этот 422 — обновите представление о флоу и проверьте фактический ответ API.

Обратное правило действует всегда: **организация без каталога не может слать `cart_items`** — получите 422 `This organization does not support catalog. Remove cart_items from request.`

## Как выглядит правильный запрос с корзиной?

```bash
curl -X POST https://api.apipay.kz/api/v1/invoices \
  -H "X-API-Key: ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "87001234567",
    "description": "Заказ №123",
    "external_order_id_idempotency": "order-123",
    "cart_items": [
      { "catalog_item_id": 11, "count": 2 },
      { "catalog_item_id": 15, "count": 1, "price": 4900 }
    ],
    "discount_percentage": 10
  }'
```

Что здесь происходит:

- **`catalog_item_id` + `count`** — обязательные поля каждой строки (количество — целое, от 1).
- **`price` (необязательное)** — переопределяет цену каталога для этой строки (0.01–99 999 999.99). Не передали — берётся `selling_price` позиции из каталога. Это снимает страх «придётся вести весь прайс в каталоге»: позиция может быть одна («Услуга»), а фактическую цену вы передаёте в запросе.
- **`discount_percentage`** — скидка 1–99% на весь чек. Поле `discount` внутри позиции устарело: на него API ответит 422 с текстом «Поле discount устарело. Используйте discount_percentage».
- **`amount` не передаём**: при корзине сумма счёта считается по позициям (цена × количество − скидка). Если передать и `amount`, и корзину — итог всё равно посчитается по корзине; не удивляйтесь, что «моя сумма игнорируется».

В вебхуке оплаченного счёта со скидкой придут поля `subtotal`, `discount_sum`, `discount_percentage` — удобно для сверки.

## Разбор 422-ошибок по строкам корзины

| Текст ошибки | Причина | Лечение |
|---|---|---|
| `Catalog item has no price set.` | У позиции каталога не заполнена цена | Задать цену позиции в каталоге — или передавать `price` в запросе |
| `Catalog item does not belong to your organization.` | `catalog_item_id` чужой организации (частая причина — id из песочницы в проде) | Взять id из `GET /catalog` того же ключа/организации |
| `Catalog item has been deleted.` | Позиция удалена | Создать заново или взять живую позицию |
| `This organization does not support catalog…` | Каталога у организации нет, а `cart_items` переданы | Убрать `cart_items`, слать `amount` |
| `Поле discount устарело…` | Скидка передана внутри позиции | Использовать `discount_percentage` на весь чек |

Ошибки приходят в формате Laravel-валидации — с индексом строки: `"cart_items.0.catalog_item_id": ["Catalog item has no price set."]` — индекс `0` указывает, какая именно позиция сломана.

## Как завести позиции каталога через API?

```bash
curl -X POST https://api.apipay.kz/api/v1/catalog \
  -H "X-API-Key: ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "name": "Доставка", "selling_price": 1500, "unit_id": 1 }
    ]
  }'
```

- За один запрос — **до 100 товаров**; обязательны `name`, `selling_price`, `unit_id` (справочник единиц — `GET /catalog/units`).
- Ответ `202`: позиции сохраняются со статусом `pending` и уходят в Kaspi через очередь — дождитесь `active`, прежде чем использовать id в счетах.
- Изображение: `POST /catalog/upload-image` — jpg/png/gif/webp до 10 МБ.
- Список и id позиций: `GET /catalog` (до 200 на страницу).
- Скан штрихкода в Нацкаталоге (`ntin`/`gtin`), 4 режима чтения `GET /catalog` и синхронизация каталога — в статье [Каталог, корзина и Нацкаталог](/guides/katalog-korzina-nackatalog).

## Чек-лист исправления 422 за 5 минут

1. Проверьте, есть ли у организации каталог: `GET /catalog` тем же ключом.
2. Каталога нет, а вы шлёте `cart_items` → уберите корзину, передавайте `amount`.
3. Каталог есть → соберите счёт из `cart_items[] = {catalog_item_id, count}`.
4. `has no price set` → задайте `selling_price` позиции или передайте `price` в строке запроса.
5. Нужна другая цена, чем в каталоге → поле `price` в строке корзины.
6. Скидка → только `discount_percentage` (1–99) на весь чек.
7. Сумма «не совпадает» → перестаньте передавать `amount`: при корзине он игнорируется.

## Частые ошибки

- **Передавать `amount` вместе с корзиной и ждать, что итог возьмётся из него.** Сумма считается по позициям. Цена управляется полем `price` в строке.
- **Использовать `catalog_item_id` из песочницы в рабочем режиме.** Тестовые организации и их каталоги — отдельные; в проде id другие.
- **Слать счёт сразу после создания позиции.** Позиция создаётся асинхронно (`202`, статус `pending`) — дождитесь `active`.
- **Скидка в каждой позиции.** Устаревший формат; только `discount_percentage` на чек.
- **Заводить весь прайс в каталог ради одной услуги.** Достаточно 1–2 позиций + переопределение `price` в запросе.

## Вопросы и ответы

**Обязательно ли создавать товары в каталоге, чтобы выставлять счета?**
Если у организации нет каталога — нет, счёт выставляется просто суммой. Если каталог подключён (Kaspi ОФД/«Касса») — счёт можно собирать из позиций; с 9 июня 2026 жёсткое требование корзины снято, «голая» сумма тоже проходит.

**Можно ли переопределить цену позиции в счёте?**
Да: поле `price` в строке `cart_items` (0.01–99 999 999.99). В каталоге при этом цена должна быть задана — она служит значением по умолчанию.

**Как задать скидку?**
`discount_percentage` от 1 до 99 — процент на весь чек, только вместе с `cart_items`. Пер-позиционной скидки нет.

**Что за ошибка «This organization requires cart items»?**
Историческое требование корзины для организаций с каталогом; с 2026-06-09 гейт снят. Если видите её — проверьте актуальный ответ API и напишите в поддержку с телом запроса.

**Влияет ли корзина на возвраты?**
Да, возвраты можно делать поэлементно (`return_items[]` с `count` или `amount`) — подробнее в «[Возвраты Kaspi через API](/guides/vozvraty-kaspi-cherez-api)». Совет: не забивайте все товары одной позицией «Услуги» — поштучный возврат станет неудобным.

Смотрите также: [Как создать счёт по номеру](/guides/kak-sozdat-schet-kaspi-po-nomeru) · [Вебхуки ApiPay](/guides/nastroyka-webhookov-apipay) · пиллар «[Как принимать оплату Kaspi через API](/kaspi-api)».

---

ApiPay — независимый сервис и не аффилирован с АО «Kaspi Bank». Kaspi и Kaspi Pay — товарные знаки их правообладателя.
База знаний: https://apipay.kz/guides
