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

Обновлено 6 июля 2026 · Справочник · Версия в Markdown
Содержание
  1. Почему счёт с одной суммой не проходит, а требует cart_items?
  2. Как выглядит правильный запрос с корзиной?
  3. Разбор 422-ошибок по строкам корзины
  4. Как завести позиции каталога через API?
  5. Чек-лист исправления 422 за 5 минут
  6. Частые ошибки
  7. Вопросы и ответы

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

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

{ "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.

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

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?

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 и синхронизация каталога — в статье Каталог, корзина и Нацкаталог.

Чек-лист исправления 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». Совет: не забивайте все товары одной позицией «Услуги» — поштучный возврат станет неудобным.

Интеграция с ИИ

Не настраивайте вручную — поручите ИИ

Скопируйте готовый промпт и вставьте своему ИИ-ассистенту — он подключит приём платежей примерно за 15 минут. Настраивает ИИ, а не вы.

Для разработчика. Полная спецификация API — apipay.kz/docs. Эта статья в Markdown — /guides/scheta-s-korzinoy-cart-items-ofd.md. Справочник фактов для ИИ-агента — apipay.kz/llms.txt.