Idempotency
Idempotency = jaminan bahwa retry request yang sama tidak menghasilkan side-effect tambahan. Critical untuk payment — lo gak mau customer ke-charge double karena network blip.
external_id
Saat create invoice, kirim external_id unik:
curl -X POST https://gateway.dehuyz.example.com/api/v1/invoices \ -H "Authorization: Bearer sk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "amount": 50000, "external_id": "order-12345-attempt1" }'
Kalau lo POST request yang sama (same external_id + same merchant) lagi, gateway return invoice yang sama tanpa generate QR baru:
import httpx # First call — creates new invoice r1 = httpx.post(API, json={"amount": 50000, "external_id": "ord-1"}, headers=H) # Network timeout, retry r2 = httpx.post(API, json={"amount": 50000, "external_id": "ord-1"}, headers=H) assert r1.json()["invoice_id"] == r2.json()["invoice_id"] # SAME invoice
Format yang baik
✅ Pakai ID dari sistem internal lo: order-{order_id}, txn-{uuid}, dst.
❌ Jangan pakai timestamp doang: 1746789 — bisa collide.
❌ Jangan kirim random string baru tiap retry — efektif jadi gak idempotent.
Tanpa external_id
Kalau lo skip external_id, gateway selalu create invoice baru per request. Aman kalau:
- Lo control retry sendiri (mis. queue dengan dedupe layer di sisi lo)
- Lo gak peduli duplicate invoice (mis. balance top-up dengan unique amount)
Tapi default-nya: selalu kirim external_id.
Constraint
- Unique per
(merchant_id, external_id)— beda merchant boleh pakaiexternal_idsama. - Maksimum 128 karakter.
- Setelah invoice exists, lo gak bisa overwrite — request baru dengan same external_id always returns existing invoice (status, amount, dll).
Edge case: amount different
r1 = httpx.post(API, json={"amount": 50000, "external_id": "ord-1"}) # ok r2 = httpx.post(API, json={"amount": 99999, "external_id": "ord-1"}) # ???
Saat ini sistem return invoice asli tanpa error. Bug? Tergantung interpretasi:
- Stripe-style: error 409 conflict
- Dehuyz Gateway: silently return original (current behavior)
Phase 6+ akan tambah strict mode (error kalau payload beda).
Best practice: pastikan external_id unique per (amount, customer) combo, atau hash payload jadi external_id biar otomatis distinct kalau berubah.