5.7 KiB
5.7 KiB
Fase 1 — Step 2: Transaction Engine + Webhook Callback (Spesifikasi Implementasi)
Dokumen ini merinci implementasi callback QRIS, state machine transaksi, dan eventing dasar untuk alur static flow.
1) Tujuan Step 2
- Menerima callback pembayaran dari partner secara aman dan idempotent
- Menyimpan state transaksi dari
initiatedsampaipaid/failed/expired - Menyimpan jejak event untuk audit dan observability
- Menyiapkan output event agar notifikasi MQTT dan pelaporan ops bisa dipicu
2) Alur State Transaksi (Static Flow)
initiatedawaiting_paymentpaidfailedexpiredreversed(opsional untuk fase awal)
Transisi Utama
initiated->awaiting_payment- terjadi saat transaksi static dibuat dari mapping merchant/outlet/terminal
- saat ini boleh dilakukan di Step 2 sebagai fallback/manual test
awaiting_payment->paid- terjadi saat callback partner valid dengan status success
awaiting_payment->failed- status partner gagal/declined/rejected
awaiting_payment->expired- expiry waktu lewat atau event timeout dari partner
failed/paidadalah terminal state untuk Step 1
3) API Endpoint: Webhook Callback
Endpoint
POST /integrations/qris/callback
Request Headers
X-Partner-Signature: signature HMAC (mandatory)X-Partner-Event: jenis eventIdempotency-Key(jika tersedia dari partner)X-Request-Id(opsional, gunakan untuk trace)
Behavior
- Validasi signature sebelum payload diproses
- Jika signature invalid: response
401dan tidak membuat transaksi - Parsing event_type:
- sukses -> candidate
paid - gagal -> candidate
failed - expired / timeout -> candidate
expired
- sukses -> candidate
- Kunci idempotency:
- key = kombinasi partner_reference + signature_reference/transaction_reference + status event
- Jika sudah diproses:
- kembalikan response sukses idempotent (tanpa mengubah state lagi)
Request/Response Format
- Response sukses:
status:acceptedrequest_idevent_idtimestamp
- Response error:
- envelope dari keputusan
D-003
- envelope dari keputusan
4) Validasi Callback
Required Fields (minimum)
partner_referenceamountcurrency(defaultIDR)status/payment_statuspaid_atmerchant_id/terminal_id/ mapping key lain dari payload partnersignature
Validasi Data
amount > 0- transaksi ditemukan: mapping
partner_referenceketransactions.partner_reference(atau lookupmerchant_reference) - status yang tidak dikenali -> log warning dan set ke
faileddengan reasonUNKNOWN_STATUS - cek expiry jika ada (
expired_at) saat status sukses masuk -> jika expired, setfailed/expired
5) Idempotency Strategy
Tabel Idempotency
- gunakan
idempotency_keysyang sudah didefinisikan di Step 1 - scope:
callback_processing - key value: hash dari
(partner_reference + payment_status + partner_txn_id) - jika key sudah ada:
- return hasil callback yang sama (replay safe)
6) Transaction Store yang Diperlukan
transactions
- fields yang harus dipopulasi di Step 2:
merchant_id,outlet_id,terminal_id,device_id(jika static device direct binding)qr_mode=staticinitiation_mode=staticpartner_referenceamount,currency,statuscreated_at,paid_at,expired_at,updated_at
transaction_events
- event wajib untuk setiap perubahan state:
event_type:INITIATED,STATE_CHANGED,CALLBACK_RECEIVED,CALLBACK_REJECTED,CALLBACK_DUPLICATE,PUSH_QUEUEDsource:webhook,system,adminpayload_json: raw payload callback + context
- urutan event dipakai untuk debugging urut transaksi
7) Integrasi Ke Step 3 (Notifikasi)
- ketika state transaksi berubah ke
paid, emit event internal:transaction.paid- payload ringan:
transaction_id,merchant_id,device_id,amount,currency,partner_reference,paid_at
- event ini menjadi trigger awal untuk notification orchestrator Step 3
- jika transaction tidak punya binding aktif:
- event tetap tercatat dengan reason
NO_ACTIVE_BINDING
- event tetap tercatat dengan reason
8) Error Code (saran)
WEBHOOK_SIGNATURE_INVALIDTRANSACTION_NOT_FOUNDPAYMENT_STATUS_INVALIDDUPLICATE_WEBHOOKCALLBACK_PARTNER_DATA_INVALIDIDEMPOTENCY_MISSING_KEY
9) Contoh Pseudocode Alur Handler
- terima payload
- validasi signature
- normalisasi
request_iddanevent_id - ambil
partner_reference-> cari transaksi - cek idempotency callback
- jika duplikat: simpan
transaction_eventsduplicate dan return success - validasi transaksi + status
- update status + paid_at jika sukses
- simpan event (
CALLBACK_RECEIVED,STATE_CHANGED) - emit
transaction.paid(hanya saat sukses) - commit
- return response success
10) API/Query untuk Operasional (minimal)
GET /admin/transactionstetap support filterstatus,merchant_id,from,to,partner_referenceGET /admin/transactions/{transactionId}menampilkan event timelineGET /admin/transactions/{transactionId}/eventsopsional di Step 2 untuk debug
11) Acceptance Criteria Step 2
- webhook menerima callback valid dan mengubah state transaksi ke
paid - callback duplicate tidak menambah perubahan state tambahan
- signature invalid ditolak
401 - jika partner reference tidak ditemukan, response tetap deterministik dan tidak crash
- setiap perubahan state menghasilkan
transaction_eventsminimal satu baris - setiap perubahan ke
paidmenghasilkan event internaltransaction.paiduntuk Step 3
12) Keberhasilan Handoff ke Step 3
- state machine stabil dan bisa dipakai unit/integration smoke
- transaksi berhasil dan gagal terekam lengkap
- callback replay aman
- notifikasi orchestrator dapat subscribe pada event
transaction.paid