Setiap toko yang sudah berjalan lebih dari sebulan pernah mengalaminya. Satu pelanggan, satu niat, dua pesanan. Daftar pengambilan barang bilang dua, pelanggannya bilang satu, dan siapa pun yang berdiri di antara keduanya harus memutuskan mana yang benar.
Hampir tidak pernah karena pelanggannya ceroboh. Halaman pembayaran yang butuh delapan detik di koneksi buruk terlihat rusak. Orang melakukan hal yang masuk akal terhadap apa pun yang terlihat rusak: mencoba lagi.
Permintaan bukanlah peristiwa
Kesalahan di balik pesanan ganda adalah menganggap datangnya sebuah permintaan sebagai peristiwanya. Bukan. Peristiwanya adalah seorang pelanggan memutuskan untuk membeli. Permintaan hanyalah satu upaya memberi tahu Anda soal itu, dan upaya bisa berulang.
Jadi pesanan butuh identitas sebelum dikirim, bukan sesudah diterima. Halaman checkout membawa satu kunci milik upaya membeli tersebut — dibuat sekali, saat keranjang berubah menjadi pesanan, dan ikut terkirim pada setiap pengiriman. Permintaan kedua datang membawa kunci yang sama, server mengenalinya, lalu mengembalikan hasil yang pertama alih-alih mengerjakannya lagi.
Pelanggan tetap melihat konfirmasi. Ia tidak pernah tahu ada masalah, dan justru itu tujuannya.
Aturan yang sama, satu tingkat di bawah
Payment gateway mengirim webhook yang sama lebih dari sekali dengan sengaja. Itu bukan cacat: begitulah cara mereka menjamin pengiriman ketika server Anda sempat tidak terjangkau. Kalau webhook pembayaran menandai pesanan lunas dengan menambah sebuah total, pengiriman ulang menambahnya dua kali. Kalau ia menandai lunas dengan mencatat referensi pembayaran tertentu, pengiriman ulang tidak mengubah apa pun, karena referensinya sudah ada.
Polanya sama di kedua tempat. Jangan bertanya “sudah pernahkah saya melihat permintaan ini?” Tanyakan “apakah saya sudah tahu peristiwa ini?” Yang satu soal jaringan, yang tidak Anda kendalikan. Yang lain soal catatan Anda sendiri, yang Anda kendalikan.
Berapa ongkosnya
Tidak banyak, kalau diputuskan sejak awal. Satu kunci pada pesanan, satu indeks unik di belakangnya, dan aturan bahwa webhook menulis satu baris alih-alih menaikkan sebuah angka.
Memasangnya setelah toko berjalan adalah pekerjaan lain — merapikan duplikat yang sudah terlanjur ada biasanya lebih lama daripada menulis kodenya. Karena itu tempatnya di versi pertama, walaupun di hari pertama, tanpa trafik, ia terlihat seperti menyelesaikan masalah yang belum dimiliki siapa pun.