Webhook feldolgozás: hogyan legyen idempotens és újrapróbálható?
A modern weboldalak és webalkalmazások egyre gyakrabban kommunikálnak egymással. Amikor ügyfeleink WordPress weboldalt kérnek, vagy egy komplex webfejlesztési projektben részt veszünk, gyakran találkozunk olyan helyzetekkel, amikor egy harmadik fél szolgáltatásnak (pl. fizetési átjáró, email marketing rendszer, szállítási API) értesítenie kell a mi rendszerünket egy eseményről. Erre szolgálnak a webhookok – lényegében visszahívások az interneten, értesítések, amikor valami történik. De mi történik, ha ez az értesítés többször érkezik? Vagy ha elsőre nem sikerül feldolgozni? Ez vezet minket az idempotencia és az újrapróbálható megvalósítás világába.
Miért fontos ez neked, akár kisvállalkozó vagy?
Képzeld el, hogy webshopodban beállítod, hogy a Stripe fizetési átjáró küldjön egy webhookot, amikor egy vásárló sikeresen fizet. Ez a webhook felelős azért, hogy a rendszerben „teljesített” státuszra állítsa a megrendelést. Ha ez a webhook véletlenül kétszer fut le (ami gyakori!), akkor nem szeretnéd, hogy dupla levonás történjen, vagy hogy a rendszered kétszer küldjön értesítő emailt az ügyfélnek. Vagy rosszabb: ha elsőre nem sikerül feldolgozni, szeretnéd, hogy legyen esély a helyreállításra anélkül, hogy adatvesztés történne. Pontosan ezeket a problémákat oldja meg a megfelelő architektúra.
Az alapok: Mi az a webhook és miért lehet problémás?
Egy webhook egyszerűen egy HTTP POST kérés, amit egy külső szolgáltatás küld a te szerveredre egy előre meghatározott URL-címre. A backend feladata ezt a kérést fogadni, ellenőrizni (pl. aláírás) és feldolgozni.
Gyakori buktatók: 1. Dupla feldolgozás: A küldő szolgáltatás újrapróbálkozhat, vagy hálózati fluktuáció miatt ugyanaz a kérés többször érkezhet. 2. Nem idempotens műveletek: Ha a feldolgozás olyan műveletet végez, ami nem idempotens (pl. „növeld a pontokat 10-zel”), akkor a dupla hívás katasztrofális. 3. Silent failure: A feldolgozás elbukhat, de a küldő szolgáltatás erről nem kap értesítést, így az adat elveszik.
A megoldás kulcsa: Idempotencia
Egy művelet akkor idempotens, ha többszöri végrehajtása ugyanazt az eredményt produkálja, mint az egyszeri végrehajtás. Visszatérve a webshop példájára: „Állítsd a #1234-es rendelést ‘teljesítettre'” egy idempotens művelet. Akárhányszor lefuttatod, a végeredmény ugyanaz: a rendelés státusza ‘teljesített’ lesz. „Adj hozzá 10 pontot a felhasználóhoz” viszont nem az.
A célunk, hogy a webhook feldolgozásunkat idempotenssé tegyük. Ennek egyik legegyszerűbb és leghatékonyabb módja az idempotencia kulcs (idempotency key) használata.
Hogyan működik a gyakorlatban? Egy PHP példa
Amikor a külső szolgáltatás (pl. Stripe) küldi a webhookot, általában küld egy egyedi azonosítót is minden egyes eseményhez (pl. event_id). Ezt felhasználhatjuk idempotencia kulcsként. A feldolgozás logikája így néz ki:
1. Ellenőrzés: Megnézzük, hogy ez az event_id már szerepel-e az adatbázisunkban.
2. Ha új: Feldolgozzuk a kérést, és elmentjük az event_id-t a feldolgozott események közé.
3. Ha ismétlődő: Visszaadjuk az előző feldolgozás eredményét (sikeres választ) anélkül, hogy ismét végrehajtanánk a műveleteket.
id; // Ez lesz az idempotencia kulcsunk
$event_type = $event->type;
// 3. Kapcsolódás az adatbázishoz (WordPress esetén a globális $wpdb használható)
global $wpdb;
$table_name = $wpdb->prefix . 'processed_webhooks';
// 4. Idempotencia ellenőrzése
$already_processed = $wpdb->get_var(
$wpdb->prepare("SELECT id FROM $table_name WHERE event_id = %s", $event_id)
);
if ($already_processed) {
// Már feldolgoztuk, visszatérünk azonos válasszal
http_response_code(200);
echo json_encode(['status' => 'success', 'message' => 'Event already processed.']);
exit;
}
// 5. FELDOLGOZÁS (csak egyszer fut le)
try {
switch ($event_type) {
case 'payment_intent.succeeded':
$order_id = $event->data->object->metadata->order_id;
// Itt hívjuk meg a függvényt, ami frissíti a rendelést
update_order_status($order_id, 'completed');
break;
// ... további eseménytípusok ...
}
// 6. Sikeres feldolgozás után rögzítjük az eseményt
$wpdb->insert(
$table_name,
array(
'event_id' => $event_id,
'event_type' => $event_type,
'processed_at' => current_time('mysql')
)
);
http_response_code(200);
echo json_encode(['status' => 'success']);
} catch (Exception $e) {
// 7. Hibakezelés: fontos, hogy sikertelen esetben NEM mentjük el az event_id-t!
http_response_code(500);
// Lehet naplózni a hibát
error_log("Webhook processing failed for {$event_id}: " . $e->getMessage());
// A küldő újrapróbálhatja a hibát, mert nincs elmentve az ID
}
exit;
}Ahhoz, hogy ez a kód működjön, létre kell hoznunk egy egyszerű adatbázis táblát, ami nyilvántartja a feldolgozott eseményeket. Ez egy tipikus webfejlesztési feladat, amit akár WordPress weboldal esetén is egy egyedi pluginban vagy a functions.php fájlban kezelhetünk.
Az újrapróbálható rendszer kialakítása
Az idempotencia megoldja a duplikációt. De mi van, ha elsőre egy időszaki adatbázis-kapcsolati hiba miatt nem sikerül? Ideális, ha a küldő szolgáltatás (pl. Stripe, SendGrid) automatikusan újrapróbálkozik bizonyos hibakódok (pl. 5xx) esetén. A mi felelősségünk, hogy egyértelmű státuszkódokkal válaszoljunk: – 200 OK: Feldolgozva (akár most, akár korábban). – 4xx (pl. 400): Ügyfél hiba (pl. hibás aláírás). Ne próbálja újra. – 5xx: Szerverhiba. Igen, próbálja újra később.
A fenti PHP példában a try-catch blokk biztosítja, hogy belső hiba esetén 500-as kódot küldjünk, ezzel jelezve az újrapróbálható állapotot. A frontend felületeken (pl. admin panel) pedig érdemes lehet egy manuális újrapróbálkozási lehetőséget is biztosítani a sikertelen webhookok számára – itt jöhet képbe egy kis JavaScript vagy jQuery a felhasználói élmény javítására.
// Példa: Admin felületen webhook újrapróbálás (jQuery Bootstrap modal kontextusban)
$('#retry-webhook-btn').on('click', function() {
let webhookId = $(this).data('id');
$('#statusModal').modal('show');
$('#modalBody').html('<div class="text-center"><div class="spinner-border"></div><p>Újrapróbálkozás...</p></div>');
$.ajax({
url: '/wp-admin/admin-ajax.php',
method: 'POST',
data: {
action: 'retry_webhook',
webhook_id: webhookId
},
success: function(response) {
$('#modalBody').html('<p class="text-success">Sikeres újrafeldolgozás!</p>');
setTimeout(() => { $('#statusModal').modal('hide'); location.reload(); }, 1500);
},
error: function() {
$('#modalBody').html('<p class="text-danger">Hiba történt. Próbáld újra később.</p>');
}
});
});Összegzés: Miért érdemes ezzel foglalkozni?
Akár kisvállalkozó vagy, akár nagyvállalati döntéshozó, egy megbízható webhook feldolgozás közvetlen üzleti hatással jár:
1. Megbízhatóság: Nem vesztek el rendelések vagy ügyféladatok. 2. Ügyfélélmény: Elkerülitek a zavaros dupla emailes értesítéseket vagy ismétlődő tranzakciók látszatát. 3. Fejlesztési hatékonyság: Könnyebb debugolni, naplózni és karbantartani a rendszert. 4. Partneri integrációk: Profibb megjelenés más szolgáltatások felé, akikkel integrálódtok.
A webhook feldolgozás idempotens és újrapróbálható megvalósítása nem csak „jó fejlesztői gyakorlat”. Az üzleti folyamataid integritásának alapvető védőfala. Érdemes minden weboldal készítési vagy honlapkészítési projektnél, ahol külső API-kat használtok, erre a kérdésre időt és erőforrást szánni – hosszú távon biztosan megtérül.
A weboldalon megjelenő szöveges és vizuális tartalmak előállításához mesterséges intelligenciát (AI) használunk.