Kesalahan paling umum dalam integrasi pembayaran: menganggap status pembayaran diketahui saat pelanggan menekan 'bayar'. Kenyataannya pembayaran itu async — statusnya baru pasti saat penyedia mengonfirmasi lewat webhook. Ini yang saya pelajari saat mengintegrasikan QRIS Midtrans ke POS Dexova.
Webhook adalah sumber kebenaran, bukan respons frontend
Frontend yang menampilkan 'pembayaran berhasil' berdasarkan respons langsung adalah bug yang menunggu terjadi. Pelanggan bisa menutup tab, sinyal bisa putus, dan pembayaran tetap settle beberapa detik kemudian.
Kebenaran datang dari webhook (notifikasi server-to-server) yang mengubah status transaksi dari pending ke settled. Frontend cukup menampilkan status yang tercatat, bukan menebaknya.
Verifikasi tanda tangan, selalu
Endpoint webhook publik: siapa pun bisa mengirim POST berpura-pura jadi Midtrans. Karena itu setiap notifikasi harus diverifikasi tanda tangannya (signature key) sebelum dipercaya. Notifikasi yang gagal verifikasi dibuang tanpa mengubah apa pun.
Ini bukan opsional. Menerima status pembayaran tanpa verifikasi berarti membiarkan siapa pun menandai pesanan sebagai 'lunas' secara gratis.
Idempotency: notifikasi yang sama boleh datang dua kali
Penyedia pembayaran menjamin at-least-once, bukan exactly-once: webhook yang sama bisa terkirim ulang. Handler harus idempoten — memproses notifikasi kedua tidak boleh menggandakan pembayaran atau memicu efek ganda.
Praktiknya: kunci transaksi berdasarkan ID pesanan, cek status saat ini, dan hanya lakukan transisi yang valid. Rekonsiliasi harian dengan dashboard Midtrans menutup celah kalau ada webhook yang terlewat sama sekali.
Ringkasan
Integrasi pembayaran yang benar berpusat pada tiga hal: perlakukan webhook sebagai kebenaran (bukan respons frontend), verifikasi setiap tanda tangan, dan buat handler idempoten. Tambah rekonsiliasi untuk tidur nyenyak.