𝗔 𝗦𝘂𝗰𝗰𝗲𝘀𝘀𝗳𝘂𝗹 𝗣𝗮𝘆𝗺𝗲𝗻𝘁 𝗧𝗵𝗮𝘁 𝗡𝗲𝘃𝗲𝗿 𝗕𝗲𝗰𝗮𝗺𝗲 𝗮 𝗕𝗼𝗼𝗸𝗶𝗻𝗴
Seorang pelanggan telah membayar. Razorpay menunjukkan status sukses. Webhook mengirimkan HTTP 200. Pembayaran telah di-capture.
Namun, booking tetap tertahan pada status "confirming."
Tidak ada error yang muncul. Tidak ada exception yang merusak kode. Tidak ada alert yang berbunyi. Setiap metrik menunjukkan sistem yang sehat.
Namun pelanggan tidak mendapatkan apa-apa. Kreator tidak mendapatkan booking.
Menerima uang itu mudah. Memastikan setiap pembayaran berujung pada booking adalah tantangan yang sebenarnya.
Kebanyakan tutorial menyarankan alur ini:
- Webhook menerima event
- Webhook memperbarui booking
Ini berbahaya. Jika logika bisnis berada di dalam webhook, Anda sepenuhnya bergantung pada keberhasilan pengiriman. Webhook menghadapi retry, duplikasi, dan kegagalan parsial.
Kami mengubah arsitektur kami untuk memisahkan tugas-tugas ini. Webhook sekarang hanya mencatat event. Mereka tidak menjalankan logika bisnis.
Kami memperkenalkan event ledger dengan tiga tabel:
- payment_orders: Sumber kebenaran dari penyedia
- payment_events: Ledger event yang immutable
- bookings: Sumber kebenaran bisnis
Webhook sekarang hanya memiliki satu tugas:
- Verifikasi signature
- Simpan event
- Kembalikan 200
Ini melindungi sistem. Jika webhook gagal, event tetap aman.
Kami juga mempelajari bahwa status pembayaran dan status booking itu berbeda. Pembayaran yang di-capture adalah input. Booking yang terkonfirmasi adalah hasilnya. Memisahkannya memungkinkan adanya rekonsiliasi.
Selama investigasi, kami menemukan bug. Event-event tersebut ada di database. Processor berjalan dengan normal. Webhook berjalan dengan normal.
Namun, processor tidak pernah berjalan. Tidak ada yang memicu fungsi untuk memproses event yang tertunda.
Memisahkan (decoupling) proses ingest dari pemrosesan adalah desain yang baik. Namun, hal ini menciptakan kebutuhan baru: sesuatu harus memicu pemrosesan tersebut.
Kami mengimplementasikan scheduler untuk menjalankan beberapa job:
- Memproses event pembayaran
- Memulihkan webhook yang terlewat
- Memvalidasi konsistensi sistem
Untuk mencegah error selama retry, kami menggunakan logika ini:
- Pilih event yang belum diproses
- Gunakan "SKIP LOCKED" untuk memungkinkan banyak worker
- Pastikan pengiriman duplikat tidak melakukan apa-apa
Sistem yang hanya bekerja saat setiap webhook tiba tepat waktu adalah sistem yang rapuh. Jika antrean (queue) Anda tidak ada yang mengosongkannya, pekerjaan akan menunggu selamanya.
Keandalan (reliability) berarti membangun sistem untuk menghadapi kegagalan.