Banyak aplikasi terlihat bekerja secara otomatis karena ada webhook di belakangnya. Ketika pembayaran berhasil, layanan pembayaran mengirim notifikasi ke website. Ketika pengguna mengisi formulir, aplikasi lain menerima data tanpa harus memeriksa setiap beberapa detik. Praktis, tetapi webhook bukan sekadar URL yang bisa menerima request.
Endpoint webhook berhadapan dengan data dari sistem lain, jaringan yang tidak selalu stabil, serta kemungkinan request dikirim ulang. Jika dirancang terlalu sederhana, aplikasi bisa memproses data palsu, menyimpan transaksi ganda, atau kehilangan informasi penting saat server sedang bermasalah.
Apa sebenarnya webhook?
Webhook adalah mekanisme komunikasi ketika sebuah layanan mengirim HTTP request ke URL aplikasi Anda setelah suatu peristiwa terjadi. Berbeda dari pola polling—ketika aplikasi Anda berulang kali bertanya apakah ada data baru—webhook membuat pihak pengirim mengambil inisiatif.
Contohnya, sebuah layanan pembayaran dapat mengirim request seperti berikut setelah transaksi selesai:
POST /webhooks/payment HTTP/1.1
Content-Type: application/json
{
"event": "payment.completed",
"transaction_id": "TRX-12345",
"amount": 150000,
"status": "paid"
}Website Anda kemudian membaca data tersebut, memperbarui status pesanan, dan mungkin mengirim email konfirmasi kepada pelanggan.
Masalah yang sering muncul pada webhook
1. Request palsu
Jika URL webhook dapat diakses publik tanpa verifikasi, siapa pun yang mengetahui alamatnya dapat mengirim request seolah-olah berasal dari layanan resmi. Dampaknya bisa serius: status pesanan berubah, saldo bertambah secara tidak sah, atau proses internal berjalan tanpa izin.
2. Request yang sama diproses lebih dari sekali
Pengirim webhook biasanya akan mencoba mengirim ulang request jika respons dari server tidak diterima dengan baik. Ini perilaku yang wajar karena jaringan bisa terputus setelah aplikasi memproses data, tetapi sebelum respons sampai ke pengirim.
Tanpa perlindungan, satu pembayaran dapat tercatat dua kali, satu email terkirim berkali-kali, atau stok berkurang lebih dari seharusnya.
3. Payload tidak sesuai harapan
Payload adalah isi data yang dikirim dalam request. Jangan menganggap semua field selalu tersedia atau memiliki tipe data yang benar. Kesalahan konfigurasi, perubahan versi API, atau data yang tidak lengkap dapat membuat kode gagal di tengah proses.
4. Server terlalu lama merespons
Endpoint webhook sebaiknya tidak menjalankan terlalu banyak pekerjaan sebelum mengirim respons. Jika prosesnya mencakup pembuatan laporan, pengiriman email, dan pemanggilan beberapa API lain, pengirim bisa menganggap request gagal lalu mengirim ulang.
Lapisan pertama: verifikasi bahwa pengirimnya benar
Metode yang umum digunakan adalah signature atau tanda tangan digital. Pengirim membuat hash dari isi request menggunakan secret key yang hanya diketahui oleh kedua sistem. Aplikasi penerima menghitung hash yang sama, lalu membandingkannya dengan signature yang dikirim di header.
Secara sederhana, alurnya seperti ini:
- Ambil body request mentah, bukan hasil decode yang sudah diubah formatnya.
- Gabungkan body dengan secret key menggunakan algoritma yang disepakati, misalnya HMAC-SHA256.
- Bandingkan hasil perhitungan dengan signature dari header.
- Tolak request jika signature tidak cocok.
Contoh pemeriksaan di PHP dapat terlihat seperti berikut:
$payload = file_get_contents('php://input');
$receivedSignature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$secret = $_ENV['WEBHOOK_SECRET'];
$expectedSignature = hash_hmac('sha256', $payload, $secret);
if (!hash_equals($expectedSignature, $receivedSignature)) {
http_response_code(401);
exit('Invalid signature');
}Nama header dan format signature tentu bergantung pada layanan yang digunakan. Dokumentasi penyedia webhook harus menjadi acuan utama.
Validasi data sebelum menyentuh database
Signature yang valid hanya membuktikan bahwa request dibuat oleh pihak yang memiliki secret. Itu belum berarti isi datanya aman untuk langsung diproses.
Periksa beberapa hal penting:
- Apakah event yang diterima termasuk jenis yang memang Anda dukung?
- Apakah field wajib tersedia?
- Apakah tipe datanya benar, misalnya nominal berupa angka?
- Apakah status transaksi sesuai dengan alur bisnis?
- Apakah ID transaksi memiliki format yang masuk akal?
Validasi juga harus dilakukan di sisi server. Jangan bergantung pada validasi JavaScript atau asumsi bahwa layanan pengirim selalu mengirim data sempurna.
Gunakan ID event untuk mencegah pemrosesan ganda
Setiap event webhook idealnya memiliki ID unik. Simpan ID tersebut di database sebelum atau bersamaan dengan proses bisnis yang terkait. Saat event dengan ID sama datang lagi, aplikasi dapat mengenalinya sebagai request yang sudah pernah diproses.
Misalnya, buat tabel sederhana:
CREATE TABLE webhook_events (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
event_id VARCHAR(100) NOT NULL UNIQUE,
event_type VARCHAR(100) NOT NULL,
received_at DATETIME NOT NULL,
processed_at DATETIME NULL
);Constraint UNIQUE penting karena pemeriksaan di level aplikasi saja dapat mengalami kondisi balapan. Dua request yang datang hampir bersamaan masih bisa sama-sama lolos pemeriksaan jika database tidak ikut menjaga keunikan.
Respons cepat, proses berat belakangan
Setelah signature dan struktur dasar valid, endpoint sebaiknya mencatat event lalu memberikan respons sukses secepat mungkin. Pekerjaan yang lebih berat dapat dipindahkan ke antrean atau proses latar belakang.
Pola sederhananya:
- Terima request dan baca body mentah.
- Verifikasi signature.
- Validasi field penting.
- Simpan event ke database dengan ID unik.
- Kirim respons HTTP yang sesuai.
- Proses perubahan pesanan, email, atau sinkronisasi melalui worker.
Dengan pola ini, webhook tidak terlalu mudah timeout. Namun, jangan mengembalikan status sukses jika event belum disimpan atau belum dijamin dapat diproses. Respons sukses seharusnya berarti aplikasi sudah menerima tanggung jawab atas data tersebut.
Bagaimana menangani kegagalan?
Catat setiap event yang diterima, termasuk waktu, jenis event, ID transaksi, status pemrosesan, dan pesan error yang aman. Hindari menyimpan secret key atau data sensitif secara sembarangan di log.
Sediakan mekanisme retry internal untuk kegagalan sementara, misalnya ketika database atau layanan eksternal sedang tidak tersedia. Gunakan jeda bertahap agar sistem tidak terus membanjiri layanan yang sedang bermasalah.
Untuk kasus tertentu, sediakan halaman atau perintah administratif untuk memproses ulang event yang gagal. Fitur ini jauh lebih aman daripada meminta tim teknis mengubah data langsung di database produksi.
Apa artinya bagi kita?
Webhook memang dapat membuat integrasi API lebih efisien, tetapi ia juga menambah pintu masuk ke aplikasi. Karena itu, endpoint webhook perlu diperlakukan seperti bagian penting dari sistem, bukan sekadar file PHP kecil yang menerima JSON.
Checklist minimal yang bisa diterapkan sekarang:
- Gunakan HTTPS.
- Verifikasi signature menggunakan secret yang disimpan di environment variable.
- Validasi struktur dan nilai payload.
- Simpan ID event yang unik.
- Jangan memproses pekerjaan berat sebelum mengirim respons.
- Catat event gagal tanpa membocorkan data rahasia.
- Uji request duplikat, payload rusak, signature salah, dan timeout.
Mulailah dari satu webhook yang paling penting, misalnya notifikasi pembayaran atau perubahan status pesanan. Setelah alurnya aman dan mudah diamati, pola yang sama dapat digunakan untuk integrasi lain. Tujuannya bukan membuat kode terlihat rumit, melainkan memastikan sistem tetap dapat dipercaya ketika dunia nyata mulai mengirim data yang tidak selalu datang tepat waktu dan tidak selalu hanya sekali.
– Rio Yotto @rioyotto
