Коротко
Всё объясняют два правила. Счёт по телефону (kind: "phone") принимает только целые тенге — отправите тиыны, получите ошибку amount_must_be_whole_tenge. QR-счёт принимает не более двух знаков после запятой. Валюта всегда KZT, другой нет.
Отсюда следует: округлять нужно на своей стороне, до создания счёта. Тогда вы сами знаете, какая сумма посчитана, и можете показать её покупателю. Если оставить округление нам, сумма в вашей базе и реально оплаченная сумма могут разойтись.
Причина по симптому
| Симптом | Причина | Решение |
|---|---|---|
amount_must_be_whole_tenge | В счёт по телефону отправлены тиыны | Округлите до целых тенге |
invalid_amount | Сумма отсутствует, равна нулю, отрицательна или не число | Передайте положительное число |
amount_too_small | Меньше минимума | Увеличьте сумму |
amount_too_large | Больше максимума | Уменьшите или разбейте на несколько счетов |
| Покупатель видит другую сумму | Сумма отправлена строкой, разделитель разобран неверно | Передавайте числом |
| Потерялись тиыны | QR принимает два знака, остальное отсекается | Округляйте у себя |
Как передаётся сумма
Поле amount — число в тенге. Не в тиынах: чтобы выставить 1 000 ₸, пишете 1000, а не 100000.
curl -X POST https://api.qut.kz/api/v1/invoices \
-H "X-API-Key: qp_live_ВАШ_КЛЮЧ" \
-H "Content-Type: application/json" \
-d '{"amount":1000,"kind":"qr","description":"Заказ №1042"}'
Счёт по телефону:
curl -X POST https://api.qut.kz/api/v1/invoices \
-H "X-API-Key: qp_live_ВАШ_КЛЮЧ" \
-H "Content-Type: application/json" \
-d '{"amount":1000,"kind":"phone","customer":{"phone":"77771234567"},"description":"Заказ №1042"}'
Отправите здесь 1000.50 — вернётся amount_must_be_whole_tenge.
Округляйте на своей стороне
Самая частая картина: в корзине 3 333,33 ₸, а покупатель платит 3 333 ₸. Разница копеечная, но при сверке в конце месяца она вся вылезет наружу.
Стройте порядок так:
// 1. Округляем сумму у себя
const total = Math.round(cart.total); // 3333.33 → 3333
// 2. Записываем именно её в свою базу
await orders.update(orderId, { chargedAmount: total });
// 3. Отправляем ровно эту же сумму
await createInvoice({ amount: total, kind: 'phone', ... });
Три принципа:
- Одна точка округления. Если округлять в разных местах расчёта, погрешность накапливается.
- Записывайте округлённую сумму в базу. При сверке будет ясно, какая сумма настоящая.
- Показывайте покупателю ту же сумму. Одна сумма в корзине и другая в счёте — это вопросы от покупателей.
Math.round округляет вверх на 0.5. Если вы хотите всегда вниз, используйте Math.floor — но выбранное правило должно быть одним для всей системы.
Работа с сотыми долями
Если в вашей системе цены хранятся в тиынах (например 333333 = 3 333,33 ₸), делайте перевод в одном месте:
const tenge = Math.round(priceInTiyn / 100);
Осторожнее с числами с плавающей точкой: 0.1 + 0.2 во многих языках даёт 0.30000000000000004. Надёжнее хранить цены целыми числами (в тиынах) и переводить в тенге только на последнем шаге.
Разница между двумя типами счёта
| Что | QR-счёт (qr) | Счёт по телефону (phone) |
|---|---|---|
| Сумма | Не более 2 знаков после запятой | Только целые тенге |
| Описание | До 100 символов | До 60 символов |
| Номер покупателя | Не нужен | Обязателен, 7XXXXXXXXXX |
| Как выглядит | QR, ссылка, deep link | Уведомление в Kaspi покупателя |
На практике удобнее отправлять целые тенге в обоих случаях: тогда при смене типа счёта логику переписывать не придётся.
Ограничения по сумме
Минимум и максимум существуют, и ограничивает их не только наша сторона, но и Kaspi. При выходе за границы придёт amount_too_small или amount_too_large.
Если вы работаете с крупными суммами (например, опт), учтите две вещи:
- Разбить сумму на несколько счетов — рабочий приём, но каждый счёт придётся отслеживать отдельно
- На стороне покупателя тоже могут быть свои лимиты, это настройка Kaspi
Кроме того, amount_too_large нередко приходит из-за ошибки в коде: переменная передана в тиынах, и сумма выросла в сто раз. Такое число обычно видно невооружённым глазом.
Валюта
Валюта только KZT. Отправить другую нельзя, поля currency искать не нужно.
Если сайт показывает цены в нескольких валютах, пересчёт делайте у себя, до создания счёта. В Kaspi покупатель всегда платит в тенге.
Порядок проверки
- Прочитайте текст ошибки:
amount_must_be_whole_tengeилиinvalid_amount - Проверьте тип отправляемого значения: число или строка
- Для счёта по телефону — посмотрите, нет ли тиынов
- Сверьте сумму в своей базе и отправленную сумму
- Убедитесь, что округление в коде происходит в одном месте
Если сумма верна, а денег не видно, дело в другом: Счёт оплачен, а денег нет.
Вопросы и ответы
Можно ли отправлять тиыны в QR-счёт? До двух знаков после запятой — можно. Но на практике целые тенге проще и безопаснее.
Может ли покупатель сам изменить сумму? В обычном счёте нет, сумма зафиксирована. В ссылке с открытой суммой покупатель вводит её сам.
Что будет, если отправить сумму строкой? В одних случаях она примется, в других вернётся invalid_amount. Всегда передавайте числом.
Кто платит разницу при округлении? Это ваше решение. Многие округляют вниз и берут несколько тиынов на себя — объясняться с покупателем сложнее.
Счёт уже создан, можно ли изменить сумму? Нет. Нужно отменить счёт и создать новый.