Untuk memastikan webhook yang dipanggil dari sistem eksternal tetap idempoten di CodeIgniter 3, mulailah dengan menentukan kontrak API yang eksplisit. Kontrak ini harus menjelaskan payload minimal (misalnya request_id, resource_id, versi payload), serta status code yang dikembalikan untuk setiap situasi. Sebagai contoh, balas 200 OK ketika payload valid dan diproses; 202 Accepted untuk proses latar belakang yang masih berlanjut; 4xx untuk kesalahan klien; dan 5xx hanya jika sistem Anda tidak bisa menerima permintaan sama sekali.
Dengan pendekatan seperti ini, sistem pemanggil dapat tahu apakah perlu retry, menyesuaikan backoff, atau menghentikan percobaan. Seluruh artikel ini fokus pada developer legacy yang masih menggunakan CI3 tanpa mengupgrade framework.
Kontrak Payload dan Status Code untuk Idempoten
Kontrak API idempoten paling sederhana terdiri dari tiga bagian:
- Payload eksplisit: sertakan
request_idpermanen dari pengirim,resource_idyang berkaitan, tipe event, serta signature atau timestamp jika perlu. - Status code deterministik: pastikan CI3 merespons sesuai kategori hasil. Jangan pernah mengandalkan
200hanya karena callback diterima; gunakan4xxuntuk validasi payload yang gagal. - Respons standar: sertakan JSON sederhana seperti
{"status":"accepted","request_id":"..."}agar integrasi dapat memverifikasi bahwa callback diakui.
Pastikan semua permintaan webhook wajib menyertakan header X-Request-Id atau field di body. Bila tidak ada, tanggapi dengan 400 Bad Request dan catat log untuk debugging.
Autentikasi Token/Session di CodeIgniter 3
Karena CI3 tidak menyediakan middleware modern, manfaatkan post-controller hook atau base controller yang memeriksa token sebelum logika webhook dijalankan.
Contoh hook sederhana di application/config/hooks.php:
$hook['pre_controller'] = array(
'class' => 'WebhookAuthHook',
'function' => 'validate',
'filename' => 'WebhookAuthHook.php',
'filepath' => 'hooks'
);
Isi WebhookAuthHook:
class WebhookAuthHook {
public function validate() {
$CI =& get_instance();
$token = $CI->input->get_request_header('X-Webhook-Token', TRUE);
if (!$token || $token !== $CI->config->item('webhook_shared_token')) {
log_message('error', 'Webhook token invalid');
show_error('Unauthorized', 401);
}
}
}
Token disimpan di application/config/config.php atau .env agar bisa diganti tanpa kode ulang. Pendekatan ini berfungsi baik untuk token statis maupun session berbasis DB dengan pengecekan tambahan.
Penjamin Idempoten di Controller Webhook
Buat controller yang responsif terhadap request_id dan memakai model untuk mencatat permintaan. Gunakan tabel sederhana seperti webhook_events(request_id VARCHAR unique, status ENUM, payload JSON, updated_at).
class Webhook extends CI_Controller {
public function receive() {
$requestId = $this->input->post('request_id');
if (!$requestId) {
show_error('request_id wajib', 400);
return;
}
$this->load->model('Webhook_event_model');
if ($this->Webhook_event_model->is_processed($requestId)) {
$this->output->set_status_header(200);
$this->output->set_output(json_encode(['status' => 'duplicate', 'request_id' => $requestId]));
return;
}
$payload = $this->input->raw_input_stream;
$this->db->trans_start();
$this->Webhook_event_model->mark_processing($requestId, $payload);
// Logika pemrosesan utama di sini
$this->Webhook_event_model->mark_done($requestId);
$this->db->trans_complete();
if ($this->db->trans_status()) {
$this->output->set_header('Content-Type: application/json');
$this->output->set_status_header(200);
$this->output->set_output(json_encode(['status' => 'ok', 'request_id' => $requestId]));
} else {
$this->output->set_status_header(500);
$this->output->set_output(json_encode(['status' => 'failed', 'request_id' => $requestId]));
}
}
}
Model Webhook_event_model menggunakan ON DUPLICATE KEY atau pemeriksaan row dengan locking (`SELECT ... FOR UPDATE`) agar status tidak tertutup. Ini mencegah pemrosesan ganda saat banyak request_id sama masuk bersamaan.
Logger dan Kunci Sederhana untuk Menghindari Pemrosesan Ganda
Untuk menjaga idempoten tanpa arsitektur baru, Anda bisa mengkombinasikan tabel webhook_locks(request_id VARCHAR, locked_at DATETIME) dengan log file khusus. Strateginya:
- Setiap request memeriksa apakah
request_idsedang dikunci; jika iya, kembalikan202 Acceptedsebagai indikasi sedang diproses. - Gunakan transaction dan
INSERT ... ON DUPLICATE KEYpada tabel lock; hapus kunci setelah selesai. - Gunakan
log_message('info', ...)untuk mencatatrequest_id, status, dan waktu secara konsisten.
Jika Anda tidak ingin menulis ke tabel, cukup gunakan flock() pada file dengan nama berdasarkan request_id. Kendati pendekatan ini tidak bergaya, ia efektif ketika deployment tidak mendukung shared storage.
Strategi Retry dan Backoff yang Cocok untuk CI3
Karena CI3 tidak memiliki queue built-in, atur retry di sisi pemanggil dengan memperhatikan status code dari webhook Anda. Panduan sederhana:
- 200/202: sistem penerima berhasil menerima. Tidak perlu retry.
- 4xx: terjadi kesalahan validasi—perbaiki payload sebelum ulangi.
- 5xx: retry otomatis dengan exponential backoff, mulai 2 detik dan maksimum 1 menit.
Anda bisa menambahkan header seperti X-Next-Retry: 30 untuk memberi tahu pengirim kapan memulai ulang pengiriman. Karena CI3 tidak punya queue worker, rekomendasikan pengirim menggunakan pola retry berbasis HTTP, bukan menunggu callback yang gagal dikerjakan secara otomatis.
Catatan debugging: ketika webhook gagal, periksa log application/logs untuk request_id, status, dan trace lengkap. Pastikan konfigurasi $config['log_threshold'] cukup tinggi saat sedang menyelesaikan integrasi. Kesalahan umum lainnya adalah tidak mengirim header token atau mengabaikan status response, sehingga pengirim terus retry tanpa batas.
Kesimpulan: Dengan kontrak API yang jelas, validasi token melalui hook, pencatatan request_id di controller, dan mekanisme logging/lock sederhana, Anda bisa menjamin idempoten webhook di CodeIgniter 3 tanpa upgrade framework. Sertakan dokumentasi kontrak bagi tim pengirim webhook agar retry/backoff dan status code dipahami bersama.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!