Menangani webhook terlambat berarti memastikan layanan internal tetap konsisten walaupun pihak ketiga mengirim payload lama, tidak tertangani, atau gagal berulang. Dalam artikel ini, langsung akan dibahas cara mendesain kontrak webhook yang kuat, mengatur retry dengan batas waktu acknowledgments, serta menjaga idempotensi payload melalui deteksi duplikasi dan observability.

1. Mendesain Kontrak Webhook Tahan Kegagalan

Kontrak webhook adalah janji bersama antara layanan pengirim dan penerima. Kontrak yang tidak eksplisit menyebabkan ambiguity saat webhook telat atau gagal.

1.1 Payload dan Schema Versioning

Pastikan payload dideskripsikan dalam dokumen resmi (misalnya OpenAPI). Sertakan metadata berikut:

  • event_type: string tetap untuk mengidentifikasi aksi.
  • event_id: UUID unik untuk setiap event.
  • timestamp: waktu pembuatan event di pengirim.
  • schema_version: versi schema untuk evolusi payload.

Versi schema membolehkan perubahan struktural tanpa mematahkan konsumen. Konsumen harus mendukung fallback atau menolak payload dengan schema_version yang tidak dikenali, lalu mengirim respons 422 untuk memicu observability.

1.2 Otentikasi Signature

Tambahkan header seperti X-Hub-Signature yang dihitung menggunakan secret yang dibagi kedua pihak. Penerima menghitung ulang HMAC terhadap body untuk memastikan integritas payload.

Implementasi sample:

HMAC_SHA256(secret, body) == request.headers['X-Hub-Signature']

Hindari menyimpan secret dalam kode: gunakan vault atau secrets manager. Pastikan timestamp tidak kedaluwarsa lebih dari batas (misal 5 menit) untuk memitigasi replay attack.

2. Strategi Retry dan Acknowledgment

Mendefinisikan batas timeout untuk acknowledgement (ACK) membantu pengirim mengetahui kapan harus retry.

2.1 Timeout dan Acknowledgment

Penerima harus membalas dalam waktu yang wajar (misalnya 10 detik). Bila pemrosesan memakan waktu lebih lama, kirimkan 202 Accepted dengan header Retry-After agar pengirim tahu tidak perlu retry saat ini.

2.2 Retry dengan Backoff

Pengirim menerapkan retry berjenjang:

  • Initial delay: 5 detik.
  • Backoff: eksponensial (2x) hingga batas maksimum (misal 5 menit).
  • Max attempts: tentukan sebelum memberikan status GREY/FAILED.

Backoff mencegah ping pong antara sistem ketika downstream terganggu. Bila status masih belum ACK setelah batas, kirim alert atau simpan state untuk manual recovery.

2.3 Tabel Status Retry

StatusDeskripsiTindakan
NEWEvent diterima, belum diprosesTrigger worker async
PROCESSINGWorker sedang menjalankan bisnis logikaLocking untuk idempotensi
ACKEDResponse sukses, tidak perlu retryHapus atau arsip state
RETRYINGFailure transient, akan dicoba ulangCatat jumlah, backoff
FAILEDBatas retry tercapaiAlert, manual review

3. Idempotensi dan Deteksi Duplikasi

Untuk webhook terlambat, idempotensi memastikan operasi tidak dijalankan dua kali walau payload sama diproses lebih dari sekali.

3.1 Idempotensi Key dan Penyimpanan State

Gunakan event_id sebagai idempotensi key. Saat menerima webhook, lakukan:

  1. Cek cache/DB (misal Redis) apakah event_id sudah berhasil diproses.
  2. Jika belum, lock (optimistic locking) lalu proses.
  3. Set status sukses dengan TTL (sesuaikan retensi) sehingga event lama bisa diabaikan bila datang lagi.

Contoh pseudo HTTP request/response:

POST /webhooks/events HTTP/1.1
Host: api.terima.id
Content-Type: application/json
X-Hub-Signature: sha256=abcdef123456

{ "event_id": "c10b5678", "event_type": "order.paid", ... }

HTTP/1.1 200 OK
Content-Type: application/json
{ "status": "ack", "processed_at": "2024-01-15T10:05:00Z" }

Jika event diterima duplikat sebelum timeout:

HTTP/1.1 409 Conflict
Content-Type: application/json
{ "status": "duplicate", "event_id": "c10b5678", "first_processed_at": "2024-01-15T10:05:00Z" }

Respons 409 memberi sinyal ke pengirim bahwa mereka tidak perlu retry lagi.

3.2 Logging dan Observability

Catat metadata event: event_id, status, latency, retry_count, response_code. Gunakan observability stack (misal Prometheus + Grafana) untuk:

  • Memantau rate retry tinggi yang bisa mengindikasikan downstream bermasalah.
  • Menampilkan distribusi latency ACK.
  • Menginformasikan hubungan antara failure rate dan jumlah webhook terlambat.

Integrasikan tracing untuk melihat path request yang memicu webhook agar debugging delay lebih mudah.

4. Pengujian dan Simulasi Keterlambatan

Uji sistem dengan simulasi delay atau pemadaman downstream guna memverifikasi kontrak dan retry.

4.1 Simulasi Delay dan Pemadaman

Gunakan HTTP mocking/server proxy yang bisa menunda respons (misal WireMock atau httpbin) lalu ukur reaksi pengirim:

  • Set timeout client lebih pendek untuk memicu retry.
  • Simulasikan kode 5xx dan 429 untuk memastikan backoff berjalan.
  • Periksa apakah status RETRYING maupun FAILED tercatat dengan benar.

4.2 Checklist Validasi Kontrak

  • Apakah payload divalidasi terhadap schema dan versi didokumentasikan?
  • Apakah signature HMAC diverifikasi dan waktu valid?
  • Sudahkah status ACK/RETRY/FAIL distandarkan dan dipantau?
  • Apakah idempotensi key disimpan dengan TTL untuk menghindari duplikasi?
  • Sudahkah logging/metric mencakup retry_count dan latency?
  • Apakah ada tes integrasi untuk kondisi timeout dan pemadaman?

5. Rangkuman dan Praktik Terbaik

Kontrak webhook yang jelas (schema versioning, signature), retry dengan backoff yang bijak, serta idempotensi kuat akan mengurangi kegagalan saat webhook terlambat. Pastikan observability dan pengujian mencakup simulasi delay agar tidak terkejut saat downtime nyata terjadi.

Dengan pendekatan ini, sistem dapat menerima webhook terlambat tanpa risiko duplikasi atau kehilangan data, sehingga reliability layanan tetap terjaga walau pihak ketiga dalam keadaan tidak stabil.