Найдвартай хүлээн авах
Webhook event-ийг алдагдалгүй, давхардалгүй боловсруулах зөвлөмжүүд.
Webhook бол сүлжээгээр дамждаг тул хоцролт, давталт, түр алдаа гарах нь хэвийн зүйл. Доорх дөрвөн зарчмыг баримталснаар та event-ийг алдалгүй, давхардуулалгүй найдвартай хүлээн авах боломжтой.
1. 5 секундын дотор хурдан 2xx буцаа
gov-pay таны endpoint руу хүсэлт явуулахдаа 5 секундын timeout тогтоодог. Хэрэв та энэ хугацаанд 2xx хариу буцаахгүй бол хүргэлт амжилтгүй гэж тооцогдоно.
Тиймээс хариу буцаахдаа хүнд ажлыг битгий хий. Зөв загвар нь:
- Event-ийг хүлээн авч, гарын үсгийг шалга (Гарын үсэг шалгах).
- Payload-ийг queue / background job руу хий (эсвэл DB-д түр хадгал).
- Шууд
2xxбуцаа. - Бодит боловсруулалтыг (захиалга шинэчлэх, имэйл явуулах гэх мэт) async ажиллуул.
Гарт орж ирмэгц synchronous байдлаар DB бичих, гуравдагч системд хүсэлт явуулах зэрэг удаан ажил хийвэл timeout-д орж, gov-pay event-ийг "хүргэгдээгүй" гэж үзнэ. Хүнд ажлыг үргэлж async/queue руу шилжүүл.
app.post('/govpay/webhook', async (req, res) => {
// 1. Гарын үсэг шалгах (доорх линкийг үз)
if (!verifySignature(req)) {
return res.status(401).send('invalid signature');
}
// 2. Queue-д хий, бодит ажлыг async хий
await queue.add('govpay-event', {
event: req.header('X-GovPay-Event'),
body: req.body,
});
// 3. Хурдан 2xx
res.status(200).send('ok');
});2. Idempotent consumer бай — ижил event давтагдаж болно
Сүлжээний түр алдаа, timeout-ийн дараа дахин оролдлого зэргээс шалтгаалж нэг event олон удаа хүргэгдэх боломжтой. Тиймээс таны consumer idempotent байх ёстой: ижил event-ийг хоёр удаа боловсруулсан ч үр дүн нэг л байх ёстой.
Давхардлыг шүүх найдвартай түлхүүр нь payload доторх payment id. Энэ id-г аль хэдийн боловсруулсан эсэхээ шалгаж, давхардвал алгасаад 2xx буцаа.
async function handleEvent(event, payload) {
// payment id-аар давхардлыг шүү
const already = await db.processedEvents.findByPaymentId(payload.id);
if (already) {
return; // аль хэдийн боловсруулсан — алгас
}
// ... бодит боловсруулалт ...
await db.processedEvents.insert({ paymentId: payload.id, event });
}Зөвлөмж: processedEvents хүснэгтэд payment id-г unique constraint болгож тавь. Тэгвэл өрсөлдөгч (race) хүсэлт орж ирсэн ч өгөгдлийн сан давхар бичилтийг таслан зогсооно.
3. Гарын үсгийг үргэлж шалга
Хүсэлт бүр дээр дараах header ирнэ:
| Header | Утга |
|---|---|
X-GovPay-Event | Event-ийн төрөл (payment.submitted, payment.confirmed) |
X-GovPay-Signature | Хүсэлтийн биеийн HMAC-SHA256 гарын үсэг (таны secret-ээр) |
Гарын үсгийг шалгаагүй endpoint бол хэн ч хуурамч event илгээх боломжтой. Боловсруулалт хийхээсээ өмнө үргэлж гарын үсгийг баталгаажуул. Тооцоолох болон харьцуулах дэлгэрэнгүйг Гарын үсэг шалгах хуудаснаас үзнэ үү.
4. Аль болох хурдан хариул
Удаан хариу буцаах тусам timeout-д орох эрсдэл нэмэгдэнэ. Иймд:
- Гарын үсэг шалгах болон queue-д хийхээс өөр ажлыг synchronous хэсэгт бүү хий.
- Гуравдагч системийн дуудлага, имэйл, push notification зэргийг async хэсэгт шилжүүл.
- Endpoint-аа боломжийн бага latency-тэй (CDN-ийн ард биш, шууд) байлга.
Event төрлүүд ба payload
gov-pay одоогоор хоёр төрлийн event илгээдэг. Webhook бүртгэхдээ events[] дотор сонгож авна.
| Event | Хэзээ илгээгдэх | Payload |
|---|---|---|
payment.submitted | Төлбөр төрийн системд илгээгдсэн | { id, invoiceId } |
payment.confirmed | MoF баталсан (төлбөр төлөгдсөн) | { id, amount } |
payment.submitted event:
{
"id": "b4f1c2d8-1234-4a56-9bcd-0123456789ab",
"invoiceId": "a1e2c3d4-5678-4b90-8cde-fedcba987654"
}payment.confirmed event:
{
"id": "b4f1c2d8-1234-4a56-9bcd-0123456789ab",
"amount": 150000
}Хоёр event дээр ч payment id нэг хэвээр байна. Энэ id-г idempotency болон төлбөрийн төлөв хянахдаа гол түлхүүр болгон ашигла. Төлбөрийн төлөвийн дэлгэрэнгүйг Төлбөр хуудаснаас үзнэ үү.
Хэрэв таны endpoint түр унтарсан үед event ирвэл та төлбөрийн төлвийг GET /payments/{id}-аар хүссэн үедээ дахин асуун шалгах боломжтой. Webhook бол хурдан мэдэгдэх хэрэгсэл; эцсийн үнэн нь үргэлж API-аас авсан төлөв.