Қысқаша
Счёт мынандай жолмен жүреді:
new ──▶ pending ──┬──▶ paid ──┬──▶ refunded
│ └──▶ partially_refunded
├──▶ cancelled
└──▶ expired
Кодыңызда екі топты ажыратыңыз: ашық счёттар — new және pending; төленген деп есептелетіндер — paid, refunded, partially_refunded. Қайтарылған счёт та төленген болып қалады: ақша келген, сосын қайтарылған.
Ең маңызды шекті жағдай — кеш төлем: жабылған счётқа ақша кейін де келуі мүмкін.
Күйлер кестесі
| Күй | Мағынасы | Ашық па | Төленген деп есептеле ме |
|---|---|---|---|
new | Счёт жасалды, әлі Kaspi-де күтуде | Иә | Жоқ |
pending | Клиенттің төлеуін күтіп тұр | Иә | Жоқ |
paid | Төленді | Жоқ | Иә |
cancelled | Сіз болдырдыңыз | Жоқ | Жоқ |
expired | Мерзімі өтті, төленбеді | Жоқ | Жоқ |
refunded | Толық қайтарылды | Жоқ | Иә |
partially_refunded | Ішінара қайтарылды | Жоқ | Иә |
Тізімді сүзгенде немесе есеп жасағанда осы екі топқа сүйеніңіз, әр күйді бөлек тізіп жазбаңыз — кейін жаңа күй қосылса, кодыңыз бұзылмайды.
Ауысулар
| Қайдан | Қайда | Не болды |
|---|---|---|
new | pending | Счёт Kaspi-ге тіркелді, клиент төлей алады |
pending | paid | Клиент төледі |
pending | cancelled | Сіз POST /invoices/{id}/cancel жібердіңіз |
pending | expired | expiresAt өтті, ешкім төлемеді |
paid | refunded | Толық сома қайтарылды |
paid | partially_refunded | Сома бөлігі қайтарылды |
cancelled / expired | paid | Кеш төлем. Ақша кешігіп келді |
Кері ауысу жоқ: paid счёт қайта pending болмайды, expired счёт өзінен өзі жанданбайды.
Қай оқиға қай сәтте келеді
| Оқиға | Қашан | Дене ерекшелігі |
|---|---|---|
invoice.created | Счёт жасалғанда | id, externalOrderId, amount |
invoice.pending | Счёт төлеуге дайын болғанда | — |
invoice.paid | Клиент төлегенде | paidAt, receiptUrl |
invoice.cancelled | Болдырғанда | — |
invoice.expired | Мерзімі өткенде | — |
invoice.failed | Счёт өтпей қалғанда | — |
invoice.refunded | Толық қайтарғанда | — |
invoice.partially_refunded | Ішінара қайтарғанда | — |
invoice.status | Күй өзгергенде жалпы оқиға | Барлық ауысуға бір арна керек болса |
invoice.lost | Счёт Kaspi жағында жоғалғанда | — |
Оқиға тізімін кабинеттің Интеграциялар бөлімінде таңдайсыз. Барлығын қосудың қажеті жоқ: көбіне invoice.paid пен invoice.refunded жеткілікті. Баптау: Webhook баптау.
Уақыттар
| Не | Қанша |
|---|---|
| QR-дың сканерлеу терезесі | Шамамен 3 минут, нақты уақыты expiresAt өрісінде |
| Төлемді тексеру циклі | Әр 3 секунд сайын |
| 3 минуттан жас счёт | Әр айналымда тексеріледі |
| 30 минутқа дейінгі счёт | 20 секунд сайын |
| Одан ескі счёт | 90 секунд сайын |
| Клиент төлегеннен webhook-қа дейін | Әдетте 5 секунд ішінде |
Кезек ең жаңа счёттан басталады, сондықтан жаңа ғана жасалған счёт бірінші тексеріледі.
Kaspi төлем уақытын бермейді: payload-та төлем сәтінің дәл уақыты жоқ, сондықтан «клиент растағаннан біздің білгенімізге дейін» аралығын Kaspi деректерінен өлшеу мүмкін емес.
Мерзім туралы толығырақ: Счёттың жарамдылық мерзімі.
Кеш төлем: ең маңызды шекті жағдай
cancelled немесе expired болып жабылған счётқа ақша кешігіп келуі мүмкін. Ондайда invoice.paid оқиғасы late: true белгісімен кейін де келеді.
Бұл қате емес және сирек болса да болады. Кодыңыз оны елемеуі тиіс емес — ақша шынымен түсті.
Не істеу керек:
- Оқиғаны қабылдап, 200 қайтарыңыз.
- Тапсырысты іздеп, оның қазіргі күйін қараңыз.
- Екі жолдың бірін таңдаңыз: қызметті беріңіз (тауар бар, тапсырыс күші жойылмаған) немесе ақшаны қайтарыңыз (Қайтару API).
- Клиентке хабарлаңыз. Үнсіз қалу ең жаман нұсқа.
if (event === 'invoice.paid') {
const order = await findOrder(invoice.externalOrderId);
if (invoice.late && order.status === 'closed') {
await notifyStaff(order, invoice); // қолмен шешім керек
} else {
await fulfil(order, invoice);
}
}
Толығырақ: Кеш келген төлем.
Күйді қалай оқу керек
Екі арна бар, екеуі бірін-бірі алмастырмайды:
| Арна | Қашан қолдану |
|---|---|
| Webhook | Негізгі арна. Күй өзгергенде өзі келеді |
GET /api/v1/invoices/{id} | Нақты бір счёттың қазіргі күйін білу керек болғанда |
Клиент құрылғының алдында тұрып нәтиже күтетін сценарийде (вендинг, турникет, шлагбаум) екеуін қатар жүргізіңіз: алғашқы 30 секундта секундына бір рет күйді сұрап, қайсысы бұрын келсе соны қабылдаңыз. Сондықтан өңдеуіңіз идемпотентті болуы керек. Толығырақ: Webhook пен күйді сұрау.
GET /api/v1/invoices/{id} жауабында счёттың оқиғалары мен қайтарулары да келеді — журнал ретінде пайдалы.
Идемпотентті өңдеу
Бір счёт бойынша бір оқиға бірнеше рет келуі мүмкін: желі үзілді, сіз 200 қайтара алмадыңыз, біз қайталадық. Сондықтан өңдеуді (invoice.id, status) жұбы бойынша бір рет орындаңыз.
Тәжірибеде бұл былай көрінеді: осы жұпты өз базаңызға бірегей индекспен жазып, жазылмаса (яғни бұрын болған) ештеңе істемейсіз. Толығырақ: Идемпоттылық.
Жиі қойылатын сұрақтар
refunded счёт төленген деп есептеле ме? Иә. Ақша келген, содан кейін қайтарылған. Есепте оны төленген топтан шығарып тастамаңыз — қайтару бөлек көрсеткіш.
pending күйінде ұзақ тұрып қалса? Бұл қалыпты: клиент әлі төлемеген. expiresAt өткенде счёт expired болады. Толығырақ: Счёт pending күйінде тұрып қалды.
new күйін көрмеймін, бірден pending келеді. Қалыпты жағдай: счёт Kaspi-ге тез тіркеледі, сондықтан new күйі көбіне көрінбей қалады.
Болдырылған счётты қайта ашуға бола ма? Жоқ. Жаңа счёт жасаңыз.
Күй өзгерісін өткізіп алсам ше? Webhook журналы кабинеттің Интеграциялар бөлімінде сақталады, ал счёттың қазіргі күйін әрқашан GET /api/v1/invoices/{id} арқылы оқи аласыз.