Tunas Akara
Kembali ke Blog

Idempotensi API Pembayaran: Cegah Tagihan Ganda

Idempotency mencegah percobaan ulang pembayaran membuat transaksi ganda. Periksa kunci transaksi, masa berlaku, dan callback.

oleh RayhanDiperbarui 8 menit baca

Untuk Tim Teknis

paymentsidempotencyapi-designfintech
Idempotensi API Pembayaran: Cegah Tagihan Ganda
Daftar isi

Ketika pembayaran tidak memberi respons, mencoba ulang bisa menagih pelanggan dua kali. Idempotensi menjaga agar permintaan yang sama menghasilkan satu transaksi meski dikirim berulang. Kuncinya adalah identitas transaksi yang tetap, catatan hasil, dan pemeriksaan status saat respons hilang. Perlindungan ini perlu mencakup panggilan pembayaran serta konfirmasi yang datang belakangan.

Masalah pengiriman ulang

Permintaan pembayaran melewati server Anda, gateway, dan sering kali bank penerima transaksi (acquiring bank). Pada salah satu tahap, batas waktu tunggu dapat habis (timeout) meskipun penagihan sudah berhasil. Klien hanya melihat ketiadaan respons, sehingga belum dapat membedakan kegagalan dari keberhasilan yang tidak terkonfirmasi.

Bayangkan transfer lewat ATM, layarnya macet sebelum struk keluar. Tidak ada yang tahu apakah uangnya sudah terpotong atau belum — itulah persis posisi API pembayaran saat timeout. Klien tidak bisa membedakan "ini belum pernah terjadi" dari "ini sudah terjadi, saya cuma tidak dengar kabarnya."

Pengiriman ulang diperlukan untuk mengatasi gangguan sementara. Namun, sistem tidak boleh menganggap percobaan pertama pasti gagal. Ketidakpastian status itulah yang dapat menyebabkan pelanggan ditagih dua kali.

Penjelasan Stripe soal kenapa idempotensi dibangun ke dalam API-nya menyebut kegagalan ini langsung. Untuk endpoint yang mengenakan penagihan ke pelanggan, "accidentally calling it twice would lead to the customer being double-charged, which is very bad".

Celah semantik HTTP

Masalah ini terus berulang sebagian karena kosakata HTTP sendiri tidak membantu. Definisi metode idempoten dari MDN: metode yang membuat beberapa permintaan identik menghasilkan efek yang sama seperti satu permintaan. MDN menyebut tegas bahwa "the POST and PATCH methods are not guaranteed to be idempotent."

Contohnya: POST /add_row yang diulang membuat baris baru setiap kali, bukan konvergen ke satu status. Endpoint penagihan adalah POST; tidak ada jaminan di HTTP bahwa memanggilnya dua kali itu aman. Draf standar dari IETF HTTPAPI working group menyatakan hal yang sama secara formal. POST dan PATCH tidak idempoten di bawah RFC 9110, berbeda dari GET, PUT, DELETE, HEAD, dan OPTIONS.

Header Idempotency-Key memberi identitas tetap pada permintaan yang mengubah data. Ketika klien mengirim ulang permintaan dengan identitas itu, server mengenalinya sebagai percobaan ulang transaksi yang sama.

Bagaimana penyedia besar menerapkannya

Konsepnya sama; detailnya tidak.

PenyediaHeaderFormat / batas kunciTTL (masa berlaku — lewat itu kunci dianggap kedaluwarsa)Perilaku permintaan duplikat
StripeIdempotency-KeyDisarankan V4 UUID, maks 255 karakterMinimal 24 jam; dapat dihapus setelah ituMengulang kode status dan isi yang tersimpan, termasuk 500 yang tersimpan
Adyenidempotency-keyDisarankan UUID, maks 64 karakter7–14 hariRespons pertama setelah selesai; HTTP 409/422 dengan kode 704 selagi masih diproses
MidtransIdempotency-KeyMaks 46 karakter (lebih dari itu diabaikan)5 menitHTTP 202 selagi permintaan awal masih diproses
Xenditidempotency-keyString bebas dari klienDidokumentasikan per endpointDUPLICATE_ERROR dapat menunjukkan kunci sudah dipakai; cocokkan status pencairan sebelum mengganti kunci

Stripe membatasi kunci idempotensi hanya untuk POST — GET dan DELETE sudah idempoten sesuai definisi. Masa retensi minimum Adyen satu minggu, tujuh kali lipat batas Stripe. Jendela lima menit Midtrans cukup singkat untuk memengaruhi cara jeda pengiriman ulang dirancang, dibahas di bawah.

Membangun lapisan idempotensi sendiri

Kalau tim sendiri yang menyediakan endpoint pembayaran, keputusan desain yang sama harus dibuat — persis seperti yang sudah dibuat vendor-vendor di atas.

Simpan respons pertama. Kunci idempotensi tidak sekadar memblokir duplikat — permintaan duplikat dijawab dengan hasil yang sama seperti permintaan pertama. Simpan isi respons dan status berdasarkan nilai kunci, lalu kembalikan tanpa perubahan saat permintaan itu diulang.

Tentukan masa berlaku kunci. Jika terlalu singkat, klien dapat mengirim ulang setelah kunci dihapus, padahal pembayaran pertama sudah berhasil. Jika terlalu lama, sistem menyimpan muatan data untuk kunci yang mungkin tidak dipakai lagi. Batas 24 jam Stripe dan 7 hari Adyen mencerminkan asumsi berbeda tentang lamanya klien menunggu.

Tangani permintaan bersamaan selagi proses awal berjalan. Kunci yang sama bisa datang dua kali selagi permintaan pertama masih berjalan. Permintaan duplikat harus menunggu atau ditolak tanpa menjalankan operasi kedua. Menjalankan keduanya dapat menimbulkan penagihan ganda meskipun kunci sudah dipasang. Draf IETF merekomendasikan respons 409 conflict di sini; 409/422 milik Adyen dan 202 milik Midtrans adalah dua jawaban produksi untuk masalah yang sama:

Memuat diagram…

Membatasi ruang kunci per akun

Pada platform yang melayani banyak akun (multi-tenant), dua pedagang dapat menghasilkan kunci yang sama. Jika ruang kuncinya berlaku global, permintaan ulang satu akun dapat mengarah ke transaksi akun lain. Draf IETF menyarankan kunci pencarian gabungan: nilai dari klien ditambah atribut yang ditentukan server, seperti identitas akun atau pedagang.

Validasi juga harus mencakup isi permintaan. Stripe menolak penggunaan kunci yang sama dengan parameter berbeda. Xendit mendokumentasikan DUPLICATE_ERROR untuk kunci pencairan yang sudah dipakai; kesalahan itu saja tidak membuktikan bahwa parameternya berubah. Cocokkan status pencairan yang ada sebelum membuat kunci lain. Jika nominal atau penerima berubah, perlakukan permintaan itu sebagai kesalahan yang perlu diperiksa, bukan pengiriman ulang biasa.

Batasan penyedia pembayaran di Indonesia

Rel pembayaran lokal menambah batasan yang tidak dimiliki penyedia global. Jendela 5 menit Midtrans pendek dibanding batas seminggu Adyen, dan tidak didukung untuk Permata Virtual Account, CIMB Clicks, KlikBCA, dan Indomaret. Jaring pengaman pengiriman ulang punya lubang per kanal, bukan hanya soal waktu. Kalau checkout menawarkan metode-metode itu, referensi order idempoten sendiri harus menutup celah itu.

Panduan payout Xendit sederhana: gunakan ulang nilai kunci yang sama pada pengiriman ulang mana pun untuk payout yang gagal atau timeout. Layak diuji sebelum peluncuran.

Pada QRIS dan virtual account, konfirmasi akhir biasanya datang asinkron melalui callback, yaitu pemberitahuan yang dikirim penyedia setelah pemrosesan. Kunci gateway melindungi permintaan awal, sehingga callback tetap perlu perlindungan tersendiri. Gunakan ID pesanan dan batasan unik di lapisan pedagang agar satu pemberitahuan tidak diproses dua kali. Aturan serupa mencegah dua konfirmasi reservasi mengambil kamar yang sama.

Yang harus Anda minta dari gateway sebelum integrasi

Minta penyedia mendokumentasikan idempotensi pada setiap endpoint yang mengubah data, masa berlaku serta ruang kunci, dan respons saat permintaan duplikat masih diproses. Minta pula cara memantau tingkat konflik HTTP 409 pada sistem Anda. Gunakan daftar berikut untuk memeriksa integrasi sendiri:

  1. Pastikan setiap endpoint pembayaran yang mengubah data yang dipanggil atau disediakan menerima kunci idempotensi.
  2. Pastikan semua pengiriman ulang untuk satu operasi memakai kunci yang sama; buat kunci baru hanya untuk operasi baru.
  3. Pastikan ruang kunci menyertakan identitas pedagang atau akun, bukan hanya string mentah dari klien.
  4. Untuk kanal mana pun yang dikecualikan gateway, pastikan ada order ID idempoten sendiri di lapisan pedagang.
  5. Tarik tingkat konflik 409/422 selama 30 hari terakhir — tafsirkan berdasarkan perilaku penyedia. Permintaan duplikat yang berhasil dapat mengembalikan respons awal tanpa konflik.

Kunci idempotensi hanya satu bagian dari rancangan API pembayaran. Namun, konsistensi identitas transaksi, pencatatan hasil, dan pencocokan status dapat mencegah kesalahan penagihan yang mahal.

Apa yang perlu diuji pemilik bisnis?

Minta vendor mendemokan pembayaran yang responsnya hilang, tombol bayar yang ditekan dua kali, dan callback yang terkirim ulang. Hasilnya harus satu transaksi yang dapat ditelusuri. Tanyakan juga kapan kunci kedaluwarsa dan bagaimana tim mencocokkan status dengan penyedia sebelum mencoba lagi.

Pencarian dan pencatatan kunci pada diagram adalah alur konsep, bukan dua langkah bebas yang aman. Klaim kunci secara atomik dengan batas unik serta identitas akun, simpan sidik jari muatan data, dan catat status belum pasti bila penyedia memproses sebelum koneksi terputus. Cocokkan status ke penyedia sebelum membuat upaya baru.

Artikel Terkait

Perlu bantuan menerapkan solusi ini?

Integrasi Pembayaran & API

Hubungkan layanan pembayaran dengan sistem bisnis Anda. Susun pelacakan transaksi, penanganan pembayaran bermasalah, dan pencocokan pencatatan.

Lihat layanan terkait

Ceritakan kendala operasional Anda agar kita dapat membahas langkah yang sesuai.

Bagikan artikel
Diskusi Awal Gratis