Cara Mendesain Webhook yang Andal: Panduan Lengkap

Ashley Innocent

Ashley Innocent

13 March 2026

Cara Mendesain Webhook yang Andal: Panduan Lengkap

Ringkasan

Rancang webhook yang andal dengan percobaan ulang backoff eksponensial (5-10 kali percobaan), kunci idempoten, verifikasi tanda tangan HMAC, dan batas waktu 5 detik. Segera kembalikan respons 2xx, proses secara asinkron. Modern PetstoreAPI mengimplementasikan webhook untuk pembaruan pesanan, adopsi hewan peliharaan, dan notifikasi pembayaran dengan fitur percobaan ulang dan keamanan penuh.

Pendahuluan

Anda mengirim webhook untuk memberi tahu klien bahwa hewan peliharaan mereka telah diadopsi. Server klien sedang mati. Webhook Anda gagal. Apakah Anda mencoba lagi? Berapa kali? Bagaimana jika klien menerima webhook dua kali dan menagih pelanggan dua kali?

Webhook adalah panggilan balik HTTP yang mendorong peristiwa ke URL klien. Secara teori sederhana, tetapi dalam praktiknya kompleks. Jaringan bisa gagal, server bisa crash, dan klien bisa memiliki bug. Webhook produksi memerlukan logika percobaan ulang, idempoten, keamanan, dan pemantauan.

Modern PetstoreAPI mengimplementasikan webhook yang siap produksi untuk pembaruan pesanan, adopsi hewan peliharaan, dan notifikasi pembayaran. Setiap webhook mencakup logika percobaan ulang, verifikasi tanda tangan, dan idempoten.

💡
Jika Anda membuat atau menguji webhook, Apidog membantu Anda menguji pengiriman webhook, memvalidasi tanda tangan, dan mensimulasikan skenario kegagalan. Anda dapat menguji logika percobaan ulang dan memverifikasi penanganan idempoten.
tombol

Dalam panduan ini, Anda akan mempelajari cara merancang webhook yang andal menggunakan pola Modern PetstoreAPI.

Dasar-dasar Webhook

Webhook adalah permintaan HTTP POST yang dikirim ke URL yang disediakan klien saat suatu peristiwa terjadi.

Bagaimana Webhook Bekerja

1. Klien mendaftarkan URL webhook:

POST /webhooks
{
  "url": "https://client.com/webhooks/petstore",
  "events": ["pet.adopted", "order.completed"]
}

2. Peristiwa terjadi (hewan peliharaan diadopsi)

3. Server mengirim webhook:

POST https://client.com/webhooks/petstore
Content-Type: application/json
X-Webhook-Signature: sha256=abc123...

{
  "event": "pet.adopted",
  "data": {
    "petId": "019b4132",
    "userId": "user-456",
    "timestamp": "2026-03-13T10:30:00Z"
  }
}

4. Klien merespons:

200 OK

Masalah Keandalan

Webhook bisa gagal karena banyak alasan:

Tanpa logika percobaan ulang, peristiwa akan hilang. Tanpa idempoten, webhook duplikat menyebabkan tindakan duplikat.

Logika Percobaan Ulang dengan Backoff Eksponensial

Coba lagi webhook yang gagal dengan penundaan yang meningkat.

Strategi Backoff Eksponensial

Percobaan 1: Segera
Percobaan 2: 1 detik kemudian
Percobaan 3: 2 detik kemudian
Percobaan 4: 4 detik kemudian
Percobaan 5: 8 detik kemudian
Percobaan 6: 16 detik kemudian

Mengapa eksponensial? Jika klien sedang mati, membanjirinya dengan percobaan ulang tidak akan membantu. Backoff eksponensial memberikan waktu untuk pemulihan.

Implementasi

async function sendWebhook(url, payload, attempt = 1, maxAttempts = 6) {
  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Webhook-Signature': generateSignature(payload)
      },
      body: JSON.stringify(payload),
      timeout: 5000 // batas waktu 5 detik
    });

    if (response.ok) {
      return { success: true, attempt };
    }

    // Coba lagi pada kesalahan 5xx
    if (response.status >= 500 && attempt < maxAttempts) {
      const delay = Math.pow(2, attempt - 1) * 1000;
      await sleep(delay);
      return sendWebhook(url, payload, attempt + 1, maxAttempts);
    }

    // Jangan coba lagi pada kesalahan 4xx (kesalahan klien)
    return { success: false, status: response.status };

  } catch (error) {
    // Kesalahan jaringan atau batas waktu - coba lagi
    if (attempt < maxAttempts) {
      const delay = Math.pow(2, attempt - 1) * 1000;
      await sleep(delay);
      return sendWebhook(url, payload, attempt + 1, maxAttempts);
    }
    return { success: false, error: error.message };
  }
}

Kapan Harus Mencoba Lagi

Coba lagi pada:

Jangan coba lagi pada:

Antrean Pesan Gagal (Dead Letter Queue)

Setelah percobaan ulang maksimum, pindahkan webhook yang gagal ke antrean pesan gagal untuk ditinjau secara manual:

if (!result.success) {
  await deadLetterQueue.add({
    url,
    payload,
    attempts: maxAttempts,
    lastError: result.error,
    timestamp: new Date()
  });
}

Idempoten untuk Pencegahan Duplikasi

Klien mungkin menerima webhook yang sama berkali-kali. Idempoten mencegah pemrosesan duplikat.

Kunci Idempoten

Sertakan ID unik dengan setiap webhook:

{
  "id": "webhook_019b4132",
  "event": "pet.adopted",
  "data": {...}
}

Klien menyimpan ID yang telah diproses:

app.post('/webhooks/petstore', async (req, res) => {
  const webhookId = req.body.id;

  // Periksa apakah sudah diproses
  const processed = await db.webhooks.findOne({ id: webhookId });
  if (processed) {
    return res.status(200).json({ message: 'Already processed' });
  }

  // Proses webhook
  await processPetAdoption(req.body.data);

  // Tandai sebagai diproses
  await db.webhooks.insert({ id: webhookId, processedAt: new Date() });

  res.status(200).json({ message: 'Processed' });
});

Operasi Idempoten

Rancang operasi agar idempoten:

Buruk (tidak idempoten):

// Penagihan dua kali menyebabkan penagihan ganda
await chargeCustomer(userId, amount);

Baik (idempoten):

// Penagihan dengan kunci idempoten mencegah penagihan ganda
await chargeCustomer(userId, amount, { idempotencyKey: webhookId });

Verifikasi Tanda Tangan untuk Keamanan

Verifikasi webhook berasal dari API Anda, bukan dari penyerang.

Tanda Tangan HMAC

Buat tanda tangan menggunakan kunci rahasia bersama:

// Server menghasilkan tanda tangan
const crypto = require('crypto');

function generateSignature(payload, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(JSON.stringify(payload));
  return hmac.digest('hex');
}

// Sertakan dalam header
headers['X-Webhook-Signature'] = `sha256=${generateSignature(payload, webhookSecret)}`;

Klien memverifikasi tanda tangan:

function verifySignature(payload, signature, secret) {
  const expected = generateSignature(payload, secret);
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expected}`)
  );
}

app.post('/webhooks/petstore', (req, res) => {
  const signature = req.headers['x-webhook-signature'];

  if (!verifySignature(req.body, signature, WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Proses webhook
  ...
});

Validasi Cap Waktu

Sertakan cap waktu untuk mencegah serangan replay:

{
  "id": "webhook_019b4132",
  "timestamp": "2026-03-13T10:30:00Z",
  "event": "pet.adopted",
  "data": {...}
}

Tolak webhook lama:

const webhookAge = Date.now() - new Date(req.body.timestamp);
if (webhookAge > 5 * 60 * 1000) { // 5 menit
  return res.status(400).json({ error: 'Webhook too old' });
}

Penanganan Batas Waktu

Atur batas waktu yang agresif untuk mencegah klien yang lambat memblokir sistem Anda.

Batas Waktu 5 Detik

const response = await fetch(url, {
  method: 'POST',
  body: JSON.stringify(payload),
  timeout: 5000 // 5 detik
});

Mengapa 5 detik? Webhook harus segera mengembalikan respons. Jika klien membutuhkan waktu lebih lama, mereka melakukan pemrosesan sinkron (pola yang salah).

Pola Pemrosesan Asinkron

Buruk (sinkron):

app.post('/webhooks/petstore', async (req, res) => {
  // Ini membutuhkan 30 detik - webhook akan mencapai batas waktu
  await processOrder(req.body.data);
  await sendEmail(req.body.data);
  await updateInventory(req.body.data);

  res.status(200).json({ message: 'Processed' });
});

Baik (asinkron):

app.post('/webhooks/petstore', async (req, res) => {
  // Segera kembalikan respons
  res.status(200).json({ message: 'Received' });

  // Proses secara asinkron
  queue.add('process-webhook', req.body);
});

Bagaimana Modern PetstoreAPI Mengimplementasikan Webhook

Modern PetstoreAPI mengimplementasikan webhook yang siap produksi.

Peristiwa Webhook

pet.adopted - Hewan peliharaan diadopsi
pet.status_changed - Status hewan peliharaan berubah
order.created - Pesanan dibuat
order.completed - Pesanan selesai
payment.succeeded - Pembayaran berhasil
payment.failed - Pembayaran gagal

Payload Webhook

{
  "id": "webhook_019b4132-70aa-764f-b315-e2803d882a24",
  "event": "pet.adopted",
  "timestamp": "2026-03-13T10:30:00Z",
  "data": {
    "petId": "019b4132-70aa-764f-b315-e2803d882a24",
    "userId": "user-456",
    "orderId": "order-789",
    "adoptionDate": "2026-03-13"
  },
  "apiVersion": "v1"
}

Konfigurasi Percobaan Ulang

Keamanan

Menguji Webhook dengan Apidog

Apidog mendukung pengujian webhook.

Menguji Pengiriman Webhook

  1. Buat mock endpoint webhook di Apidog
  2. Daftarkan endpoint dengan PetstoreAPI
  3. Picuan peristiwa (adopsi hewan peliharaan)
  4. Verifikasi webhook diterima
  5. Periksa format payload

Menguji Verifikasi Tanda Tangan

// Skrip pengujian Apidog
const signature = pm.request.headers.get('X-Webhook-Signature');
const payload = pm.request.body.raw;
const secret = pm.environment.get('WEBHOOK_SECRET');

const expected = generateSignature(payload, secret);
pm.test('Tanda tangan valid', () => {
  pm.expect(signature).to.equal(`sha256=${expected}`);
});

Menguji Logika Percobaan Ulang

  1. Kembalikan kesalahan 500 dari mock endpoint
  2. Verifikasi upaya percobaan ulang dengan backoff eksponensial
  3. Periksa antrean pesan gagal setelah percobaan ulang maksimum

Menguji Idempoten

  1. Terima webhook
  2. Kembalikan 200
  3. Terima webhook yang sama lagi (percobaan ulang yang disimulasikan)
  4. Verifikasi tidak ada pemrosesan duplikat

Kesimpulan

Webhook yang andal memerlukan:

Modern PetstoreAPI mengimplementasikan semua pola ini. Periksa dokumentasi webhook untuk contoh lengkap.

Uji webhook Anda dengan Apidog untuk memverifikasi logika percobaan ulang, tanda tangan, dan idempoten sebelum masuk ke produksi.

tombol

FAQ

Berapa banyak upaya percobaan ulang yang harus dimiliki webhook?

5-10 kali percobaan dengan backoff eksponensial. Ini mencakup pemadaman sementara (5-17 menit) tanpa membebani klien.

Haruskah webhook mencoba ulang pada kesalahan 4xx?

Tidak. Kesalahan 4xx menunjukkan masalah klien (URL salah, kegagalan autentikasi). Mencoba ulang tidak akan memperbaikinya. Hanya coba ulang kesalahan 5xx dan kegagalan jaringan.

Berapa lama batas waktu webhook seharusnya?

Maksimal 5 detik. Klien harus segera mengembalikan respons 200 dan memproses secara asinkron. Batas waktu yang lebih lama menunjukkan bahwa klien melakukan pemrosesan sinkron.

Bagaimana jika klien tidak pernah merespons webhook?

Setelah percobaan ulang maksimum, pindahkan ke antrean pesan gagal. Beri tahu klien melalui email. Pertimbangkan untuk menonaktifkan webhook untuk klien tersebut setelah kegagalan berulang.

Haruskah URL webhook menggunakan HTTPS?

Ya, selalu wajib menggunakan HTTPS. Webhook HTTP dapat dicegat dan dimodifikasi. Modern PetstoreAPI menolak URL webhook HTTP.

Bagaimana cara mencegah serangan replay?

Sertakan cap waktu dalam payload dan tolak webhook yang berusia lebih dari 5 menit. Gabungkan dengan verifikasi tanda tangan.

Bisakah klien meminta pengiriman ulang webhook?

Ya. Modern PetstoreAPI menyediakan endpoint untuk mengirim ulang webhook tertentu: POST /webhooks/{id}/redeliver

Bagaimana cara menguji webhook secara lokal?

Gunakan alat seperti ngrok untuk mengekspos localhost ke internet, atau gunakan server mock Apidog untuk mensimulasikan endpoint webhook selama pengembangan.

Mengembangkan API dengan Apidog

Apidog adalah alat pengembangan API yang membantu Anda mengembangkan API dengan lebih mudah dan efisien.