D
Dehuyz Gateway
Docs Idempotency

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 pakai external_id sama.
  • 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.