Қысқаша
Ақшаны қайтару — бір ғана әдіс:
POST /api/v1/invoices/{id}/refund
X-API-Key: qp_live_…
Content-Type: application/json
{ "amount": 1500, "reason": "Тауар қайтарылды" }
amount жазбасаңыз — счёт толық қайтарылады. Жазсаңыз — сол сома ғана қайтады, счёт partially_refunded күйіне өтеді.
Екі нәрсені бірден есте ұстаңыз: кілтте refunds:write құқығы болуы керек, ал refund_unknown немесе refund_pending_unknown қатесі келсе сұрауды қайталамау керек — оның орнына счёт күйін оқисыз.
Сұрау өрістері
| Өріс | Тип | Міндетті | Сипаттама |
|---|---|---|---|
amount | number | жоқ | Қайтарылатын сома, теңге. Жазылмаса — толық қайтару |
reason | string | жоқ | Себебі. Есепте және кабинетте көрінеді |
Жол параметрі {id} — счёттың id мәні (inv_…). externalOrderId бойынша қайтару жоқ: алдымен GET /api/v1/invoices?externalOrderId=… арқылы счётты тауып, содан соң оның id мәнін қолданыңыз.
Қайтару қандай счётқа жасалады
| Счёт күйі | Қайтаруға бола ма |
|---|---|
new, pending | Жоқ. Ақша әлі түспеген — счётты болдырмау керек: POST /invoices/{id}/cancel |
paid | Иә, толық та, ішінара да |
partially_refunded | Иә, қалған сома шегінде |
refunded | Жоқ, бәрі қайтарылған |
cancelled, expired | Жоқ |
Ақша біздің шотымыздан емес, сіздің Kaspi шотыңыздан кетеді. Біз ақшаны ешқашан ұстамаймыз, сондықтан қайтаруды да Kaspi орындайды. Шотта қалдық жеткіліксіз болса, операция өтпейді.
Толық және ішінара қайтару
Толық қайтару — денесі бос немесе тек reason бар сұрау:
curl -X POST https://api.qut.kz/api/v1/invoices/inv_7Kd2/refund \
-H "X-API-Key: $QUTPAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reason":"Клиент бас тартты"}'
Ішінара қайтару — amount көрсетілген сұрау. Бірнеше рет ішінара қайтаруға болады, бірақ ереже қатаң:
Барлық қайтарулардың қосындысы төленген сомадан аспауы керек.
Мысалы, 10 000 ₸ счёт бойынша 3 000 және 4 000 қайтарсаңыз, үшінші рет ең көбі 3 000 қайтара аласыз. Одан асырсаңыз invalid_refund_amount келеді. Соңғы қалдықты қайтарғаныңызда счёт refunded күйіне өтеді.
Кезекті қайтарудан бұрын ағымдағы қалдықты GET /api/v1/invoices/{id} арқылы оқып алу дұрыс — жауапта счёт бойынша жасалған қайтарулар тізімі де болады.
Қате кодтары
| Код | HTTP | Не болды | Не істеу керек |
|---|---|---|---|
invoice_not_refundable | 409 | Бұл счётты қайтаруға болмайды | Күйін оқыңыз: төленген бе, мерзімі өтпеген бе |
invalid_refund_amount | 422 | Сома дұрыс емес | Оң сан, төленген сомадан аспауы керек |
refund_failed | 502 | Kaspi қайтаруды орындамады | Kaspi шотындағы қалдықты тексеріп, қайталауға болады |
refund_unknown | 502 | Kaspi жауап бермеді, нәтижесі белгісіз | Қайталамаңыз. Счёт күйін оқыңыз |
refund_pending_unknown | 409 | Алдыңғы қайтарудың нәтижесі әлі белгісіз | Қайталамаңыз. Күте тұрыңыз |
refund_state_conflict | 409 | Қайтару күйі күткеннен басқа | Счёт күйін қайта оқып, содан шешіңіз |
insufficient_scope | 403 | Кілтте refunds:write жоқ | Кабинетте кілтке құқық қосыңыз |
invoice_not_found | 404 | Счёт жоқ немесе кілтке көрінбейді | Идентификаторды және кілт байланған кассирді тексеріңіз |
kaspi_session_expired | 409 | Кассир байланысы үзілген | Қайта байланыстырыңыз |
Қателерді әрқашан error коды бойынша өңдеңіз — message мәтіні өзгеруі мүмкін, код өзгермейді.
Нәтижесі белгісіз болғанда
refund_unknown — Kaspi сұрауға жауап бермеді. Ақша кетті ме, жоқ па — бұл сәтте біз де білмейміз. refund_pending_unknown — алдыңғы қайтару әлі анықталмағандықтан жаңа сұрау қабылданбайды.
Екі жағдайда да реті бірдей:
- Сұрауды қайталамаңыз. Қайтару әдісінде идемпоттылық кепілдігі жоқ — екінші сұрау екінші қайтаруға айналуы мүмкін.
- Бірнеше секунд, қажет болса бір минут күтіңіз.
GET /api/v1/invoices/{id}арқылы күйді оқыңыз.- Күй
refundedнемесеpartially_refundedболса — қайтару өтті, қайталаудың қажеті жоқ. - Күй әлі
paid, ал қайтарулар тізімі бос болса — сонда ғана қайталаңыз.
Клиентке «ақша қайтарылды» деп счёт күйінен көрмей тұрып айтпаңыз.
async function refundOnce(id, amount) {
const r = await fetch(`${API}/invoices/${id}/refund`, {
method: 'POST',
headers: { 'X-API-Key': KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ amount, reason: 'Қайтару' }),
});
if (r.ok) return 'done';
const { error } = await r.json();
if (error === 'refund_unknown' || error === 'refund_pending_unknown') {
// қайталамаймыз — күйді оқып барып шешеміз
return 'check_status';
}
if (error === 'refund_failed') return 'retry_later';
throw new Error(error);
}
Оқиғалар
Қайтару өткен соң webhook келеді:
| Оқиға | Қашан |
|---|---|
invoice.refunded | Счёт толық қайтарылды |
invoice.partially_refunded | Сома ішінара қайтарылды |
refund.done | Жеке қайтару операциясы орындалды |
refund.failed | Қайтару орындалмады |
refund.unknown | Нәтижесі белгісіз күйде қалды |
Автоматты есеп жүргізсеңіз, күйді сұрап отырғаннан гөрі осы оқиғаларға сүйенген ыңғайлы. Webhook өңдеуіңіз идемпотентті болсын: бір қайтару туралы екі рет хабар келгенде бухгалтерияда екі жазба пайда болмауы керек.
Құқық және кассир
- Кілтте
refunds:writeқұқығы болуы керек, әйтпесеinsufficient_scope. Толығы: Құқықтар (scopes). - Қайтару да Kaspi-дегі «Кассир» рөлі арқылы жүреді, сондықтан кассир байланысы белсенді болуы шарт. Байланыс үзілсе қайтару да, счёт жасау да өтпейді.
- Кілт нақты кассирге байланған болса, ол басқа кассирдің счёттарын көрмейді — сондай счётқа қайтару жіберсеңіз
invoice_not_foundкеледі.
Мерзім
Қайтару мәңгі қолжетімді емес: белгілі бір уақыттан кейін Kaspi ескі операция бойынша қайтаруға рұқсат бермейді. Мерзімді Kaspi белгілейді, біз оны ұзарта алмаймыз. Мерзімі өткен счётқа invoice_not_refundable келеді.
Sandbox режимінде бүкіл циклді қауіпсіз сынап көруге болады: счёт жасап, төлемді симуляциялап, содан кейін қайтаруды жіберіңіз.
Жиі қойылатын сұрақтар
Қайтаруды кері қайтаруға бола ма? Жоқ. Өткен қайтару кері оралмайды — клиенттен қайта төлеуді сұрауға тура келеді.
Комиссия қайтарыла ма? Kaspi өз комиссиясын өзі анықтайды, оны Kaspi қолдауынан сұраңыз. Біз транзакциядан пайыз алмаймыз, сондықтан бізден қайтаратын ештеңе жоқ.
Клиентке ақша қашан жетеді? Оны Kaspi шешеді, мерзімге біз әсер ете алмаймыз.
Қайтаруды кабинеттен жасауға бола ма? Иә, Счёттар бөлімінде счётты ашып, қайтару батырмасын басасыз. API мен кабинет бір операцияны орындайды.
Қате кодтарының толық тізімі қайда? Қателер каталогы бетінде. Қайтару өтпей жатқанда себебін іздеу реті: Қайтару өтпей жатыр.