DEV Community

Kiell Tampubolon
Kiell Tampubolon

Posted on

Bayaran Sudah Masuk tapi Status Masih Belum Dibayar: Kenapa Webhook Pembayaran Gagal Diam-diam

Cerita ini terjadi di hampir setiap toko online kecil yang baru pasang pembayaran otomatis.

Pelanggan bayar. M-banking bilang masuk. Admin cek saldo, iya, uangnya sudah ada. Tapi di dashboard toko, status pesanan masih "menunggu pembayaran". Staf yang sabar mengubah status manual satu per satu. Yang kurang beruntung: sistem mengirim tagihan kedua ke pelanggan yang sudah bayar. Kolom WhatsApp penuh, "Kak, saya sudah transfer kemarin."

Uangnya tidak hilang. Penyedia pembayaran sudah mengabari sistem kamu. Sistem kamu yang gagal menerimanya. Dan kegagalan ini tidak ribut, tidak error di layar. Dia bekerja diam-diam, satu pesanan per satu pesanan.

Apa yang sebenarnya terjadi di belakang layar

Pas pembayaran berhasil, penyedia pembayaran (Midtrans, Tripay, Xendit, dan kawan-kawannya) mengirim notifikasi ke server kamu. Namanya webhook. Bisa dibayangkan seperti kurir yang membawa surat kabar pembayaran: "Pesanan 1042 sudah lunas." Server kamu yang menerima surat itu baru update status pesanan.

Masalahnya, kurir ini punya beberapa kebiasaan aneh yang semuanya normal menurut dia:

  • Surat bisa datang dua kali. Kalau server kamu lambat menjawab "sudah diterima", penyedia anggap suratnya hilang, lalu kirim ulang. Dua surat, satu pembayaran.
  • Surat bisa telat. Server kamu sedang restart atau internetnya drop. Suratnya datang lima menit kemudian, atau kadang lima belas menit.
  • Surat bisa gagal datang. Server kamu mati pas suratnya tiba. Penyedia mencoba ulang beberapa kali, lalu menyerah. Pembayaran masuk, status tidak pernah berubah.

Tidak satu pun dari ini adalah bug penyedia. Mereka memang bekerja seperti itu. Yang jadi masalah adalah kode penerima surat yang menganggap semua surat datang tepat satu kali, selalu tepat waktu.

Tiga kata yang perlu kamu tanya ke developer kamu

1. Idempotency: satu nota, satu stempel

Idempotency itu cuma aturan sederhana: pesanan yang sama diproses sekali, sisanya diabaikan.

Analoginya nota toko. Setiap nota punya nomor. Kalau kurir datang dua kali membawa nota nomor sama, kamu tidak menulis pembukuan dua kali. Kamu lihat "oh, nomor 1042 sudah ada stempelnya", lalu lewati. Selesai. Toko kamu tidak pernah mencatat dua pembayaran dari satu transfer.

Tanjanya cuma satu kalimat: sebelum memproses, cek dulu "apakah nomor event ini sudah pernah saya stempel". Banyak integrasi tidak melakukannya sama sekali.

2. Verifikasi: cek tanda tangan surat, dari aslinya

Webhook yang sah membawa tanda tangan (signature). Server kamu wajib memastikan surat itu benar dari penyedia, bukan dari orang yang iseng kirim "lunas" palsu ke endpoint kamu.

Dan satu jebakan klasik: verifikasi harus dilakukan terhadap isi mentah request, bukan hasil olahan framework. Kalau isi surat dirombak sedikit sebelum dicek, tanda tangannya tidak cocok lagi. Developer yang bingung akhirnya mematikan verifikasi. Itu seperti menerima surat tanpa melihat pengirimnya.

3. Retry yang dibatasi: surat gagal masuk rak tinjauan, bukan dibuang

Kalau surat gagal diproses, harus diulang. Tapi ada batasnya. Tiga kali, lima kali, terserah. Setelah itu event pindah ke daftar tunggu yang bisa dilihat manusia, bukan hilang begitu saja.

Tanpa ini, dua bencana sama-sama mungkin: retry tanpa batas yang memakan server kamu, atau event gagal yang dibuang diam-diam dan tidak pernah ada yang tahu.

Buktinya, bukan katanya

Semua pola di atas saya bangun jadi lab kecil yang bisa dijalankan siapa saja di laptop, tanpa akun penyedia pembayaran, tanpa uang sungguhan, mode mock penuh. Satu event dobel dikirim, dan inilah yang terjadi:

$ ./scripts/emit_event.sh
{
  "status": "processed",
  "event_id": "evt_001",
  "request_id": "req_f432043f6260812a",
  "http_status": 202,
  "attempts": 1,
  "message": "transitioned"
}

$ ./scripts/replay_event.sh evt_001
{
  "status": "duplicate",
  "event_id": "evt_001",
  "request_id": "req_replay_9b6c0ca92c60",
  "http_status": 202,
  "attempts": 1,
  "message": "already processed"
}
Enter fullscreen mode Exit fullscreen mode

Satu pembayaran, satu pencatatan. Duplikatnya dikenali, dijawab sopan, lalu diabaikan.

Lab yang sama juga menguji signature palsu (ditolak), payload rusak (ditolak), event telat yang datang setelah pesanan dikirim (tidak menurunkan status balik), dan retry yang melewati batas (masuk antrean tinjauan). Semua skenario ini ada test otomatisnya, 18 test, semuanya hijau.

Yang penting bukan labnya. Yang penting: pertanyaan-pertanyaan ini bisa dan harus dijawab developer kamu sebelum uang pelanggan lewat sistemnya.

Checklist sebelum kamu bayar developer

Kalau kamu sedang mengajak developer bikin sistem pembayaran, toko online, atau bot order WhatsApp, ajukan lima pertanyaan ini. Semua dijawab tanpa mikir lama-lama kalau memang dia paham:

  1. Kalau notifikasi pembayaran datang dua kali untuk satu transaksi, apa yang terjadi di database?
  2. Kalau server sedang mati pas notifikasi tiba, siapa yang mengulang, berapa kali, lalu setelah itu kemana eventnya pergi?
  3. Bagaimana server memastikan notifikasi itu benar dari penyedia pembayaran, bukan dari orang luar?
  4. Kalau status pesanan sudah "dikirim" lalu notifikasi lama datang, apakah status bisa mundur lagi jadi "menunggu"?
  5. Saat notifikasi gagal diproses, apa yang kelihatan? Ada tempat saya bisa lihat event yang gagal, atau diam-diam hilang?

Jawaban bagus pendek-pendek dan spesifik: ada idempotency, ada retry terbatas, ada antrean tinjauan, ada verifikasi tanda tangan, ada status yang tidak bisa mundur. Jawaban yang bikin kamu harus waspada: "sudah aman kok", "sistemnya standar", atau kalau dia bingung dengan istilah idempotency.

Bukan soal developer-nya jelek. Ini bagian kerja yang jarang diajarkan dan tidak kelihatan saat demo, karena demo selalu pakai jalur senang: bayar sekali, notifikasi sekali, sukses. Petaka selalu datang belakangan, saat toko sudah jalan dan pelanggan sudah bayar.

Penutup

Saya membangun otomasi order dan pembayaran WhatsApp yang menangani semua kasus di atas sejak awal: notifikasi dobel, server mati sesaat, retry, sampai rak tinjauan untuk event gagal. Semua dites pakai mode mock dulu, bukan eksperimen di pembayaran sungguhan.

Kalau kamu penasaran sistem kamu sekarang sudah benar atau belum, kirim saja pesan. Saya kasih review singkat jalur pembayaran kamu: mana yang sudah aman, mana yang bisa bocor diam-diam. Gratis, tanpa komitmen apa pun.

Top comments (0)