Polling
Polling = lo cek status invoice secara berkala via GET /api/v1/invoices/{id}. Cocok kalau:
- Lo gak punya endpoint publik (mis. ngerjain di laptop lokal, di belakang NAT)
- Mau backup dari webhook (defense in depth)
- Server lo sering crash / restart, takut webhook lost
Endpoint
curl https://gateway.dehuyz.example.com/api/v1/invoices/a1b2c3d4 \ -H "Authorization: Bearer sk_live_xxx"
Response:
{ "invoice_id": "a1b2c3d4e5f67890", "external_id": "order-001", "status": "paid", "amount": 50000, "paid_at": "2026-05-22T12:05:30+00:00", "qr_string": "...", "qr_image": "...", "metadata": {} }
Pattern: polling sederhana
import httpx, time API = "https://gateway.dehuyz.example.com" KEY = "sk_live_xxx" def wait_for_payment(invoice_id: str, max_wait: int = 300): """Poll invoice tiap 3 detik sampai paid/expired.""" headers = {"Authorization": f"Bearer {KEY}"} deadline = time.time() + max_wait while time.time() < deadline: r = httpx.get(f"{API}/api/v1/invoices/{invoice_id}", headers=headers) r.raise_for_status() status = r.json()["status"] if status == "paid": return "paid" elif status in ("expired", "cancelled", "failed"): return status time.sleep(3) return "timeout"
Pattern: jangan hammer
❌ Jangan polling tiap 100ms. Server lo gak butuh sub-second update — pembayaran QRIS minimum delay 1-2 detik dari sisi BRI poll lo.
✅ Recommended interval: 2-5 detik per invoice aktif.
✅ Stop polling setelah expires_at invoice — gak akan ada perubahan setelahnya.
✅ Backoff kalau dapat error 500/503: tunggu 5-10 detik sebelum retry.
Pattern: bulk polling
Kalau lo punya banyak invoice pending bareng (mis. ada 20 customer di kasir), jangan polling tiap invoice satu-satu — saat ini sistem belum support batch endpoint, jadi lo akan hit rate limit (60 req/min default).
Solusi:
- Gunakan webhook (lebih efisien)
- Atau prioritize: hanya polling invoice yang paling baru aktif
Phase 6+ akan tambah GET /api/v1/invoices?status=pending (list) supaya 1 call cek banyak.
Polling vs Webhook — kapan pakai apa?
| Kondisi | Polling | Webhook |
|---|---|---|
| Lo gak punya public endpoint | ✅ | ❌ |
| Latensi notifikasi penting (<1s) | ❌ | ✅ |
| Cuma <10 invoice/hari | ✅ OK | ✅ OK |
| 100+ invoice/jam | ❌ kena rate limit | ✅ |
| Server lo sering restart / unstable | ✅ (state di gateway) | ❌ butuh outbox di sisi lo |
| Mau stack-bersih, no infra public | ✅ | ❌ |
Best practice: pakai keduanya. Webhook sebagai primary, polling sebagai fallback (cek tiap N detik untuk invoice yang lebih dari T detik tanpa webhook fired).
Rate limits
- Default: 60 req/min per merchant (lewat header tunggal
Authorization). - Limit terpisah per IP: 5 req/min untuk endpoint signup/login.
- Hit rate limit -> response 429 dengan header
Retry-After: <seconds>.
r = httpx.get(...) if r.status_code == 429: retry_after = int(r.headers.get("retry-after", 60)) time.sleep(retry_after)