Contoh laporan
Contoh laporan diagnosis: callback Midtrans yang dobel dan order yang tertahan pending
CONTOH — Sistem, nama, log, dan angka di laporan ini fiktif. Laporan untuk klien mengikuti struktur dan tingkat detail yang sama.
| Klien (fiktif) | PT Contoh Niaga Digital, toko online dengan backend Laravel 10 dan MySQL |
| Masalah yang dilaporkan | Sebagian order kartu kredit diproses dua kali; sebagian order virtual account tetap pending walaupun pembayarannya sukses |
| Akses yang dipakai | Repository (read-only), log aplikasi 14 hari terakhir, dashboard Midtrans sandbox, environment staging |
| Waktu terpakai | 7 dari 8 jam |
1. Ringkasan
Kedua masalah terkonfirmasi dan berasal dari satu tempat: handler notifikasi Midtrans. Handler ini memperlakukan setiap notifikasi sebagai pembayaran baru, dan mengerjakan semua pekerjaan berat (reservasi stok ke API gudang, email konfirmasi) sebelum membalas Midtrans. Rekomendasinya adalah opsi B: membuat pemrosesan pembayaran idempotent, memindahkan pekerjaan berat ke queue, dan menambahkan rekonsiliasi berkala ke API status Midtrans. Harga tetap Rp8.400.000, sekitar 1,5 minggu kalender.
2. Gejala dan dampak
- Order diproses dua kali: 37 order kartu kredit dalam 30 hari terakhir. Stok terpotong dua kali dan pelanggan menerima dua email konfirmasi.
- Order tertahan pending: 112 order virtual account dalam 30 hari terakhir, padahal dashboard Midtrans menunjukkan pembayaran sukses. Tim CS mengubah statusnya secara manual, sekitar 2 jam per hari.
3. Scope investigasi
- Diperiksa:
PaymentNotificationController,OrderService, konfigurasi queue, log aplikasi, riwayat notifikasi di dashboard Midtrans, dan URL notifikasi yang terdaftar. - Di luar scope: frontend checkout, integrasi kurir, dan performa database secara umum.
4. Temuan
Penyebab 1 (terkonfirmasi): status capture dan settlement sama-sama diproses sebagai “lunas”
Untuk pembayaran kartu kredit, Midtrans mengirim notifikasi capture saat pembayaran berhasil, lalu notifikasi lain saat statusnya berubah menjadi settlement. Handler memanggil markPaid() untuk kedua status, dan markPaid() tidak memeriksa apakah order sudah lunas.
// PaymentNotificationController.php (disederhanakan)
if (in_array($notif->transaction_status, ['capture', 'settlement'])) {
$this->orders->markPaid($order); // memotong stok dan mengirim email, setiap kali dipanggil
}
Bukti (log disanitasi):
2026-02-11 10:02:14 midtrans.notify order_id=INV-20481 transaction_status=capture fraud_status=accept
2026-02-11 10:02:15 order.paid order_id=INV-20481 stock_reserved=3
2026-02-12 09:15:40 midtrans.notify order_id=INV-20481 transaction_status=settlement
2026-02-12 09:15:41 order.paid order_id=INV-20481 stock_reserved=3
Reproduksi: dua notifikasi di atas dikirim ulang ke staging. Test baru test_paid_order_is_processed_once gagal di kode saat ini.
Penyebab 2 (terkonfirmasi): handler memanggil API gudang sebelum membalas Midtrans
Handler memanggil API reservasi stok secara sinkron. Saat API gudang lambat atau error, handler membalas 500. Menurut dokumentasi Midtrans, notifikasi yang dibalas 500 hanya dicoba ulang satu kali. Jika percobaan ulang itu juga gagal, notifikasi tidak dikirim lagi, dan tidak ada mekanisme lain yang mengecek status pembayaran. Order pun tertahan pending.
2026-02-18 13:40:02 midtrans.notify order_id=INV-21007 transaction_status=settlement
2026-02-18 13:40:12 warehouse.reserve order_id=INV-21007 error="timeout after 10s"
2026-02-18 13:40:12 http.response path=/payment/notify status=500
2026-02-18 13:42:13 midtrans.notify order_id=INV-21007 transaction_status=settlement
2026-02-18 13:42:23 warehouse.reserve order_id=INV-21007 error="timeout after 10s"
2026-02-18 13:42:23 http.response path=/payment/notify status=500
Setelah dua kegagalan itu, tidak ada notifikasi lain untuk INV-21007. Pola yang sama ditemukan di 104 dari 112 order pending.
Yang sudah dikesampingkan
- Signature key tidak valid: 40 sampel notifikasi lolos verifikasi
signature_key. - URL notifikasi salah: URL di dashboard Midtrans sesuai, dan notifikasi untuk order lain diterima normal.
- Gangguan di sisi Midtrans: riwayat pengiriman notifikasi di dashboard tercatat lengkap, termasuk percobaan ulangnya.
5. Opsi perbaikan
| Opsi | Pendekatan | Estimasi | Risiko | Yang tidak diselesaikan |
|---|---|---|---|---|
| A | markPaid() dibuat idempotent (status pembayaran per order dan kunci unik per transaksi); handler langsung membalas 200, reservasi stok dan email dipindah ke queue dengan retry |
12 jam | Rendah | Order yang notifikasinya gagal sebelum perbaikan ini tetap pending |
| B | Opsi A, ditambah job rekonsiliasi setiap 15 menit yang mengecek order pending lebih dari 30 menit ke API status Midtrans, dan alert jika pemrosesan notifikasi gagal berulang | 20 jam | Rendah | — |
| C | Menulis ulang modul pembayaran | 80+ jam | Tinggi | Tidak diperlukan: masalahnya ada di dua titik yang spesifik |
6. Rekomendasi
Opsi B, harga tetap Rp8.400.000, sekitar 1,5 minggu kalender. Jika Anda lanjut dalam 30 hari, biaya diagnosis Rp2.500.000 dipotong penuh, sehingga yang dibayar Rp5.900.000: DP Rp2.950.000, pelunasan Rp2.950.000 saat acceptance.
Draf acceptance criteria:
- Notifikasi
capturelalusettlementuntuk order yang sama membuat order diproses satu kali (test otomatis). - Notifikasi yang sama dikirim dua kali tidak menimbulkan efek ganda (test otomatis).
- Handler membalas
200dalam waktu di bawah 1 detik walaupun API gudang lambat; reservasi stok berjalan di queue dengan retry (test dan demo di staging). - Order yang pending lebih dari 30 menit dan sudah sukses di Midtrans otomatis menjadi lunas lewat job rekonsiliasi (test dan demo di staging).
- Alert terkirim jika ada 3 atau lebih kegagalan pemrosesan notifikasi dalam 15 menit.
7. Risiko lain yang teramati
Di luar scope dan tidak diinvestigasi lebih jauh:
- Server key Midtrans pernah ter-commit di riwayat repository lama. Sebaiknya key dirotasi.
- Tidak ada monitoring untuk queue yang menumpuk atau macet.
8. Langkah selanjutnya
- Setujui opsi dan acceptance criteria di atas.
- SOW perbaikan dikirim dalam 1 hari kerja setelah persetujuan.
- Biaya diagnosis dipotong penuh dari harga perbaikan jika Anda lanjut dalam 30 hari.
Untuk menyimpan halaman ini sebagai PDF, gunakan tombol "Cetak / simpan PDF" di atas atau menu Cetak di browser Anda.