Қысқаша
Счёт cancelled немесе expired күйіне өтіп кеткен соң да, оған ақша кешігіп келуі мүмкін. Бұл қалыпты жағдай, сирек болғанымен, болады. Ондайда сізге invoice.paid оқиғасы late: true белгісімен келеді. Сіз екі жолдың бірін таңдайсыз: қызметті беру немесе ақшаны қайтару. Үшінші жол — елемеу — жарамайды: ақша мерчант шотында тұр, ал клиент тауарын күтіп отыр.
Ең бастысы — кодыңыз мұндай оқиғаны күте білсін. Көп интеграция invoice.paid оқиғасын «счёт ашық болса ғана» өңдейді және кеш төлемді үнсіз тастап кетеді.
Неге бұлай болады
Счёт бізде де, Kaspi-де де жүреді, әрі екі жақтың уақыт есебі әрдайым бірдей болмайды.
- Клиент QR-ды сканерлеп, төлемді растау терезесінде ұзақ отырады. Біз счётты
expiredдеп жабамыз, ал ол растауды сол күйі басады. - Байланыс үзіліп, растау Kaspi жағына кешігіп жетеді.
- Сіз счётты болдырдыңыз, дәл сол сәтте клиент төлеп жатқан болады.
- Kaspi жағында өңдеу уақытша кідіреді.
Kaspi төлемнің дәл уақытын бермейді. Payload-та «клиент қай секундта растады» деген өріс жоқ, сондықтан кідіріс қанша болғанын деректерден өлшеу мүмкін емес. Сіз тек фактіні көресіз: счёт жабық еді, ақша келді.
Қалай білесіз
| Белгі | Қайдан көресіз |
|---|---|
invoice.paid оқиғасы late: true белгісімен | Webhook payload-ынан |
Счёттың күйі жабық күйден paid күйіне өткен | GET /api/v1/invoices/{id} |
| Kaspi Pay-де транзакция бар, ал жүйеңізде тапсырыс жабылған | Kaspi шоты мен өз базаңызды салыстырғанда |
Webhook-ты тыңдамасаңыз, мұндай төлемді тек айдың соңында, есеп айырысқанда байқайсыз. Сондықтан invoice.paid оқиғасын әрқашан өңдеген жөн.
Кодта дайын болу
Кеш төлемді бөлек оқиға деп қарамаңыз — бұл жай ғана invoice.paid, оның үстінде белгісі бар. Негізгі ереже: төлемді өңдеуді счёттың алдыңғы күйіне байлап қоймаңыз.
app.post('/webhooks/qutpay', async (req, res) => {
const event = req.headers['x-webhook-event'];
const body = JSON.parse(req.rawBody); // қолтаңбаны тексергеннен кейін
if (event === 'invoice.paid') {
const orderId = body.invoice.externalOrderId;
const order = await orders.find(orderId);
// Идемпоттылық: бұл счёт бойынша бұрын өңдедік пе?
if (order.paidInvoiceId === body.invoice.id) return res.sendStatus(200);
if (body.late) {
// Счёт жабық болатын, бірақ ақша келді
if (order.status === 'cancelled') {
await flagForReview(order, body.invoice); // адам шешеді
} else {
await fulfil(order, body.invoice); // қызметті береміз
}
} else {
await fulfil(order, body.invoice);
}
}
res.sendStatus(200);
});
Үш нәрсеге назар аударыңыз:
- Идемпоттылық.
(invoice.id, status)жұбы бойынша бір рет қана өңдеңіз. 2xx емес жауап берсеңіз, оқиға 11 ретке дейін қайталанады. - Жабық тапсырысты автоматты ашпаңыз. Тапсырыс болдырылған, тауар басқа біреуге сатылған болуы мүмкін. Ондай жағдайды адамға жіберген дұрыс.
- Әрқашан 200 қайтарыңыз. Ішкі логикаңыз шешім қабылдай алмаса да, оқиғаны қабылдап алып, өз кезегіңізге қойыңыз.
Шешім қабылдау
| Жағдай | Дұрыс әрекет |
|---|---|
| Тапсырыс әлі орындалмаған, тауар бар | Қызметті беріңіз, тапсырысты paid деп белгілеңіз |
| Тауар қоймада жоқ, тапсырыс жабылған | Ақшаны қайтарыңыз, клиентке хабарлаңыз |
| Клиент екінші рет төлеп қойған | Артығын қайтарыңыз |
| Қызмет мерзімдік (жазылым, абонемент) | Мерзімді сол сәттен бастап ашыңыз |
| Іс-шара өтіп кеткен, билет жарамсыз | Ақшаны қайтарыңыз |
Ақшаны қайтару POST /api/v1/invoices/{id}/refund арқылы жасалады. Қайтару жауабы refund_unknown немесе refund_pending_unknown десе, қайталап жібермеңіз — алдымен счёт күйін оқыңыз, әйтпесе екі рет қайтарып жіберуіңіз мүмкін.
Клиентпен қалай сөйлесу керек
Кеш төлем клиенттің кінәсі емес. Ол растау батырмасын басты, ақшасы шықты — оның көзқарасы бойынша бәрі дұрыс болды.
- Хабарламаны кешіктірмеңіз. Ақша келгенін көрген бойда жазыңыз.
- Егер қызметті бере алатын болсаңыз, жай ғана беріңіз — түсіндірудің қажеті жоқ.
- Бере алмасаңыз, қайтару жасалғанын және қашан келетінін айтыңыз.
- Клиентке техникалық мәтінді көрсетпеңіз. «Төлем кешігіп өңделді» деген жеткілікті.
Қайталанбауы үшін
- Счётты тым ерте болдырмаңыз. Клиент әлі растау терезесінде отырған болуы мүмкін.
expiresAtөрісін пайдаланыңыз. Мерзімді өзіңіз ойлап таппай, счёттың өз мерзіміне сүйеніңіз.- Тапсырысты счётпен қатар жаппаңыз. Тапсырысты «төлем күтілуде» күйінде біраз ұстаған дұрыс.
- Webhook-ты міндетті түрде тыңдаңыз. Тек кабинетке қарап отырсаңыз, кеш төлемдер көзден таса қалады.
Егер счёттар мүлде төленбей жатса, себебі басқа: Мәселе менде ме, Kaspi-де ме.
Жиі қойылатын сұрақтар
Кеш төлем жиі бола ма? Жоқ, сирек. Бірақ ай сайын жүздеген счёт шығаратын болсаңыз, кездеседі. Сондықтан кодта дайын болған дұрыс.
Ақша қайда түседі — жабық счёт болса да? Тіке сіздің Kaspi шотыңызға. Счёттың күйі ақшаның қозғалысына әсер етпейді.
late: true белгісі қай оқиғада келеді? invoice.paid оқиғасында. Счёт бұрын cancelled немесе expired болған жағдайда.
Мен оқиғаны елеместен қалдырсам, қайталана ма? Иә, 2xx емес жауап берсеңіз 11 ретке дейін қайталанады. Бірақ 200 қайтарып, ішінде елемей тастасаңыз — оқиға жоғалады.
Қайтару жасауға қанша уақыт бар? Мерзім Kaspi жағында анықталады, сондықтан кешіктірмеген дұрыс. Қайтару өтпей жатса, счёттың күйін алдымен оқып алыңыз.