Stripe webhookok PHP-ben: megbízható kezelő, amelyben megbízhat
A laptopodon működő Stripe webhook még nem olyan webhook, amelyre pénzzel rábízhatsz. Ez az útmutató bemutatja, hogyan ellenőrizheti az aláírásokat, hogyan lehet gyorsan nyugtázni, deduplikálni az eseményeket, és hogyan kezelheti az újrapróbálkozásokat egyszerű PHP-ben és Laravel, a projektbe beilleszthető kóddal.
Röviden
- Kezelje a webhookot, ne a böngésző átirányítását a fizetési állapot igazságforrásaként.
- Olvassa el a nyers kérés törzsét, és ellenőrizze a
Stripe-Signaturefejlécet, mielőtt megérinti a hasznos adatot. - Gyorsan küldjön vissza egy 2xx választ, és végezze el az igazi munkát a sorban vagy az esemény tárolása után.
- Tárolja az eseményazonosítót egyedi indexszel: Stripe többször is kézbesítheti ugyanazt az eseményt, és soron kívül.
- Élő adás előtt tesztelje a Stripe CLI (
stripe listenésstripe trigger).
Ha elfogad fizetést Stripe segítségével, a PHP-ben Stripe webhookok az a része, amely eldönti, hogy a rendelések fizetettként vannak-e megjelölve, az előfizetések aktiválva vannak-e, és a visszatérítések megjelennek-e az adatbázisban. A pénztár csak a bejárati ajtó: az ügyfelek bezárják a lapokat, a bankok késve igazolnak vissza, és egyes fizetési módok percekkel vagy napokkal később rendeznek. A webhook a Stripe üzenete, amely elmondja, mi történt valójában.
Fizetési integrációkat építünk és tartunk fenn ügyfeleink és saját termékeink számára (például Bill, amely nyilvános fizetési oldalon fogadja Stripe és PayPal fizetéseket). Ez a cikk az az ellenőrző lista, amelyet követünk, amikor egy Stripe webhook végpontot írunk PHP-ben.
Hogyan működnek Stripe webhookok egy perc alatt
Regisztráljon egy HTTPS URL-t a Stripe Dashboardban (vagy az API-n keresztül), és válassza ki, hogy milyen eseménytípusokat szeretne fogadni. Ha valami történik – a Checkout Session befejeződik, a számlát kifizetik, a díjat visszatérítik –, Stripe egy JSON- esemény objektumot tartalmazó POST kérést küld az adott URL-re. A szervernek 2xx állapotkóddal kell válaszolnia. Ha nem, akkor Stripe exponenciális visszalépéssel próbálkozik újra, akár három napig élő módban.
Három tény határozza meg az egész tervet:
- Az események többször is megérkezhetnek. Az újrapróbálkozások és a hálózati problémák normálissá teszik a duplikációkat.
- Az események soron kívül is érkezhetnek. Előfordulhat, hogy
invoice.paidcustomer.subscription.createdelőtt látható. - Bárki közzé teheti az Ön URL-címére. Aláírás ellenőrzése nélkül a támadó megjelölheti saját megrendelését fizetettnek.
1. lépés Telepítse az SDK-t, és hozza létre a végpontot
Használja a hivatalos könyvtárat, stripe/stripe-php, és tartsa titokban a végpontot (whsec_ karakterrel kezdődik) egy környezeti változóban, soha ne a tárolóban.
composer require stripe/stripe-php
# .env
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
Az irányítópult minden webhook-végpontjának megvan a maga aláírási titka, és a fejlesztés során a stripe listen titka ismét más. Egy nagyon gyakori "aláírás-ellenőrzés sikertelen" hiba egyszerűen rosszat használ.
2. lépés: Ellenőrizze az aláírást a nyers törzsön
Stripe minden kérést aláír. A Stripe-Signature fejléc tartalmaz egy időbélyeget és egy vagy több aláírást, amely timestamp.payload HMAC-SHA256-jaként van kiszámítva a végpont titkával. A könyvtár ellenőrzi az aláírást, és alapértelmezés szerint elutasítja az öt percnél régebbi eseményeket.
A fontos részlet: az ellenőrzéshez pontosan a Stripe által küldött nyers bájtokra van szükség. Ha egy keretrendszer elemzi a JSON-t, és újrakódolja azt, a szóköz megváltozik, és az aláírás már nem egyezik.
<?php
// public/webhooks/stripe.php (plain PHP)
require __DIR__ . '/../../vendor/autoload.php';
$payload = file_get_contents('php://input');
$sigHeader = $_SERVER['HTTP_STRIPE_SIGNATURE'] ?? '';
$secret = getenv('STRIPE_WEBHOOK_SECRET');
try {
$event = \Stripe\Webhook::constructEvent($payload, $sigHeader, $secret);
} catch (\UnexpectedValueException $e) {
http_response_code(400); // invalid JSON
exit;
} catch (\Stripe\Exception\SignatureVerificationException $e) {
http_response_code(400); // bad or missing signature
exit;
}
Laravel olvassa el $request->getContent() $request->all() helyett, és zárja ki az útvonalat a CSRF védelemből (a webhookok nem hordoznak CSRF tokent, az aláírás a védelem). Ha teheti, helyezze az útvonalat a web munkamenet köztesszoftver-csoportján kívülre.
3. lépés: Tegye idempotenssé a kezelőt
Idempotens jelentése: ugyanazon esemény kétszeri feldolgozása ugyanazt az eredményt adja, mint egyszeri feldolgozása. A legegyszerűbb megbízható megközelítés a feldolgozott eseményazonosítók táblázata egyedi indexszel. Először helyezze be; ha a beillesztés sikertelen, mert az azonosító létezik, akkor már látta az eseményt.
CREATE TABLE stripe_events (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
event_id VARCHAR(64) NOT NULL,
type VARCHAR(100) NOT NULL,
payload JSON NOT NULL,
status ENUM('received','processed','failed') NOT NULL DEFAULT 'received',
received_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
processed_at TIMESTAMP NULL,
UNIQUE KEY uniq_event (event_id)
);
try {
$pdo->prepare('INSERT INTO stripe_events (event_id, type, payload) VALUES (?, ?, ?)')
->execute([$event->id, $event->type, $payload]);
} catch (PDOException $e) {
if ($e->getCode() === '23000') { // duplicate key: already received
http_response_code(200);
exit;
}
throw $e;
}
A nyers rakomány tárolása ellenőrzési nyomvonalat biztosít: amikor egy ügyfél megkérdezi, hogy "miért fizettek kétszer?", pontosan lejátszhatja azt, amit Stripe mondott. Lehetővé teszi a sikertelen események újrafeldolgozását a hiba kijavítása után.
4. lépés: Gyorsan nyugtázza, és külön dolgozza fel
Stripe néhány másodpercen belül választ vár. Ha a kezelő e-maileket küld, más API-kat hív meg, vagy inline PDF-eket generál, akkor az időtúllépés letelik, Stripe újra megpróbálja, és Ön ismét elvégezheti ugyanazt a munkát. Végezze el a minimumot a kérésben – ellenőrizze, tárolja, válaszoljon 200-at – és futtassa a valódi logikát egy sormunkásban ( Laravel sorok, Symfony Messenger, cron-feladat olvasási status = 'received', vagy felügyelő által felügyelt dolgozó).
Ha az esemény egyáltalán nem tárolható (az adatbázis lefelé), akkor adjon vissza 5xx állapotot. Ez az az eset, amikor azt szeretné, hogy Stripe később újra próbálkozzon.
5. lépés: Kezelje a fontos eseményeket
Nincs szükség minden eseménytípusra. Csak arra iratkozzon fel, amit üzleti logikája használ; kevesebb előfizetés kevesebb zajt és kisebb támadási felületet jelent. Egy tipikus készlet:
checkout.session.completed– az ügyfél befejezte a Checkoutot. Keresse meg rendelését a munkamenet létrehozásakor beállítottclient_reference_idvagy metaadatok használatával.payment_intent.succeededéspayment_intent.payment_failed– egyéni fizetési folyamatokhoz.invoice.paidésinvoice.payment_failed– előfizetési számlázás.customer.subscription.updatedéscustomer.subscription.deleted– tervmódosítások és törlések.charge.refundedéscharge.dispute.created– a pénz visszamegy.
switch ($event->type) {
case 'checkout.session.completed':
$session = $event->data->object;
$orderId = $session->client_reference_id;
if ($session->payment_status === 'paid') {
markOrderPaid($orderId, $session->payment_intent);
}
break;
case 'invoice.payment_failed':
notifyBillingProblem($event->data->object->customer);
break;
case 'charge.refunded':
markOrderRefunded($event->data->object->payment_intent);
break;
default:
// unknown or unsubscribed type: acknowledge and ignore
break;
}
Két szokás akadályozza meg a legtöbb hibát. Először is, soha ne bízzon a böngészőből származó összegekben: hasonlítsa össze a kifizetett összeget és a pénznemet az adatbázisában lévő megrendeléssel. Másodszor, mivel az események rendellenesek lehetnek, tegye monoton állapotba az állapotváltozásokat: ne helyezze át a rendelést a „kifizetett” állapotból a „függőben” állapotba, mert egy régebbi esemény későn érkezett. Ha kétségei vannak, kérje le az aktuális objektumot az API-ból ahelyett, hogy az esemény pillanatképére hagyatkozna.
6. lépés: Helyi tesztelés a Stripe CLI segítségével
A Stripe CLI valós tesztmódú eseményeket továbbít a gépére, így nincs szüksége nyilvános alagútra:
stripe login
stripe listen --forward-to http://localhost:8000/webhooks/stripe
# in another terminal:
stripe trigger checkout.session.completed
stripe trigger invoice.payment_failed
stripe listen kiír egy ideiglenes whsec_ secretet. Helyezze be a helyi .env fájlba. Szándékosan is tesztelje a boldogtalan útvonalakat: küldjön egy kérést rossz aláírással (várhatóan 400), küldje el kétszer ugyanazt az eseményt (egy adatbázis-effektusra számítson), és állítsa le a sorkezelőt egy percre, hogy lássa, az események felhalmozódnak ahelyett, hogy eltűnnének.
Gyakori hibák, amelyeket az ellenőrzések során tapasztalunk
- Az átirányítás használata fizetési igazolásként. A
success_urloldalt bárki megnyithatja; A webhook megérkezéséig mutasd a „befizetés megerősítése” feliratot. - JSON elemzése az ellenőrzés előtt. Az újrakódolt testek megtörik az aláírást; A kerettörzs elemzői gyakori bűnösök.
- Egy titok teszteléshez és élőben. A teszt és az élő mód külön kulcsokkal és külön webhook-végpontokkal rendelkezik.
- Nincs naplózás. Tárolt események és látható hibacsatorna nélkül az ügyfelek megtalálják a fizetési hibákat.
- Lassú kezelők. Bármi, ami néhány másodpercnél hosszabb, újrapróbálkozást és ismétlődő munkát indít el.
- Stripe blokkolása a tűzfalnál vagy a WAF-nál. Ha Cloudflare vagy más WAF-ot használ, engedélyezze a webhook elérési útját, és ellenőrizze, hogy a botszabályok nem küldik-e vissza a 403-as kódot Stripe szervereinek.
Monitoring: honnan tudod, hogy továbbra is működik
A webhook hónapokkal az indítás után megszakadhat: a telepített módosítás átnevez egy oszlopot, a titkosság elforgatásra kerül, a tanúsítvány lejár. A Stripe Dashboard végpontonként megjeleníti a kézbesítési kísérleteket és a hibákat, és Stripe e-mailt küld Önnek, ha egy végpont folyamatosan meghibásodik. Az Ön oldalán három dologra figyeljen: received vagy failed események növekvő számára, az óránkénti események hirtelen csökkenése, valamint a webhook útvonalon előforduló 4xx vagy 5xx sebesség. Egy kis napi egyeztetési munka, amely összehasonlítja az adatbázisban lévő kifizetett rendeléseket Stripe sikeres kifizetéseinek listájával, megragadja a többit.
Hogyan segíthetünk
Tervezünk és építünk gyártási fizetési és webhook-integrációkat aláírás-ellenőrzéssel, idempotenciával, újrapróbálkozásokkal és a csapata által olvasható naplókkal. Ha már rendelkezik Stripe flow-val, és szeretne egy második szempárt, egy építészeti áttekintés vagy konzultáció a leggyorsabb módja annak, hogy megtalálja a gyenge pontokat. A kritikus események Zapier ből vagy Make-ból való kihelyezésének tágabb képéért olvassa el a kód nélküli kódok megszakadását, más szolgáltatókért pedig tekintse meg WayForPay PHP-be történő integrációjáról szóló útmutatónkat.
Gyakori kérdések
Miért sikertelen Stripe webhook aláírás ellenőrzése?
A szokásos okok a rossz aláírási titok használata (teszt vagy élő, CLI vs. irányítópult végpont), egy elemzett vagy újrakódolt törzs ellenőrzése a nyers kéréstörzs helyett, vagy egy proxy, amely módosítja a hasznos terhelést. Ellenőrizze a pontos nyers bájtokat a végpont saját whsec_ secret segítségével.
Hogyan akadályozhatom meg a Stripe esemény kétszeri feldolgozását?
Minden eseményazonosítót tároljon egy egyedi indexszel rendelkező táblázatban, és helyezze be, mielőtt bármilyen munkát végezne. Ha a beszúrás nem sikerül duplikált kulccsal, adja vissza a 200-at, és hagyja ki a feldolgozást. Tegye biztonságossá magát az üzleti művelet megismétlését is.
Milyen válaszkódot kell visszaadnia a Stripe webhook-nak?
Az esemény ellenőrzése és tárolása után azonnal küldjön vissza egy 2xx kódot (200 vagy 204). Érvénytelen aláírás esetén adjon vissza 400-at, és csak akkor adja vissza az 5xx-et, ha nem tudta tárolni az eseményt, és azt szeretné, hogy Stripe újra próbálkozzon.
Meddig próbálkozik újra Stripe a sikertelen webhookkal?
Élő módban Stripe újrapróbálkozik exponenciális visszalépéssel akár három napig. Teszt módban az újrapróbálkozási időszak sokkal rövidebb, ezért ne hagyatkozzon a tesztmódú újrapróbálkozásokra a termelési viselkedés megítéléséhez.
Tesztelhetem Stripe webhookat nyilvános URL nélkül?
Igen. Futtassa a Stripe listen --forward-to parancsot a Stripe CLI vel a tesztesemények továbbításához a localhost felé, és használja a stripe triggert a mintaesemények generálásához.
Ingyenes felfedező hívás
Hozzon kaotikus stacket vagy elakadt integrációt - az Integrono feltérképezi a legokosabb első lépést.
Senior rálátást kap a költségvetési csapdákra, a függőségekre és arra, hogy vásároljon, ragasszon vagy építsen - mielőtt a fizetett munka elindul. A CEO a körben marad.