Stripe webhooky v PHP: spoľahlivý handler, ktorému môžete dôverovať
Webhook Stripe, ktorý funguje na vašom notebooku, ešte nie je webhook, ktorému môžete dôverovať peniazmi. Táto príručka ukazuje, ako overiť podpisy, rýchlo potvrdiť, odstrániť duplicitné udalosti a zvládnuť opakované pokusy v jednoduchom PHP a Laravel s kódom, ktorý môžete vložiť do projektu.
V skratke
- Za zdroj pravdivosti stavu platby považujte webhook, nie presmerovanie prehliadača.
- Prečítajte si nespracované telo požiadavky a overte hlavičku
Stripe-Signaturepredtým, ako sa dotknete užitočného obsahu. - Rýchlo vráťte odpoveď 2xx a urobte skutočnú prácu vo fronte alebo po uložení udalosti.
- Uložte ID udalosti s jedinečným indexom: Stripe môže doručiť rovnakú udalosť viac ako raz a môže byť mimo poradia.
- Pred spustením otestujte pomocou Stripe CLI (
stripe listenastripe trigger).
Ak prijímate platby pomocou Stripe, Stripe webhooky v PHP sú tou časťou, ktorá rozhoduje o tom, či sú objednávky označené ako platené, či sú aktivované predplatné a či sa refundácie prejavia vo vašej databáze. Stránka pokladne je len predné dvere: zákazníci zatvárajú karty, banky potvrdia neskoro a niektoré spôsoby platby sa vyrovnávajú o niekoľko minút alebo dní neskôr. Webhook je správa od Stripe, ktorá hovorí, čo sa skutočne stalo.
Vytvárame a udržiavame platobné integrácie pre klientov a pre naše vlastné produkty (napríklad Bill, ktorý prijíma platby Stripe a PayPal na verejnej platobnej stránke). Tento článok je kontrolný zoznam, ktorý dodržiavame pri písaní koncového bodu webhooku Stripe v PHP.
Ako fungujú webhooky Stripe za jednu minútu
Zaregistrujete si HTTPS URL v Stripe Dashboard (alebo cez API) a zvolíte, ktoré typy udalostí chcete prijímať. Keď sa niečo stane – dokončí sa relácia Checkout, zaplatí sa faktúra, vráti sa poplatok – Stripe odošle požiadavku POST s objektom udalosti JSON na túto adresu URL. Váš server musí odpovedať stavovým kódom 2xx. Ak sa tak nestane, Stripe to zopakuje s exponenciálnym stiahnutím až na tri dni v živom režime.
Celý dizajn formujú tri fakty:
- Udalosti môžu prísť viackrát. Opakované pokusy a problémy so sieťou spôsobujú, že duplikáty sú normálne.
- Udalosti môžu prísť mimo poradia. Môže sa vám zobraziť
invoice.paidpredcustomer.subscription.created. - Na vašu adresu URL môže ZVEREJŇOVAŤ ktokoľvek. Bez overenia podpisu môže útočník označiť svoju vlastnú objednávku ako zaplatenú.
Krok 1. Nainštalujte SDK a vytvorte koncový bod
Použite oficiálnu knižnicu stripe/stripe-php a udržujte koncový bod v tajnosti (začína sa whsec_) v premennej prostredia, nikdy nie v úložisku.
composer require stripe/stripe-php
# .env
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
Každý koncový bod webhooku v Dashboarde má svoje vlastné podpisové tajomstvo a tajomstvo z stripe listen vo vývoji je opäť iné. Veľmi častou chybou „overenie podpisu zlyhalo“ je jednoducho použitie nesprávneho.
Krok 2. Overte podpis na surovom tele
Stripe podpíše každú požiadavku. Hlavička Stripe-Signature obsahuje časovú pečiatku a jeden alebo viac podpisov vypočítaných ako HMAC-SHA256 timestamp.payload s vaším tajným kľúčom koncového bodu. Knižnica kontroluje podpis a štandardne odmieta udalosti staršie ako päť minút.
Dôležitý detail: overenie vyžaduje presné nespracované bajty odoslané Stripe. Ak framework analyzuje JSON a vy ho znova zakódujete, medzery sa zmenia a podpis sa už nezhoduje.
<?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;
}
V Laravel si prečítajte $request->getContent() namiesto $request->all() a vylúčte trasu z ochrany CSRF (webhooky nenesú token CSRF, ochrana je podpis). Ak môžete, umiestnite trasu mimo skupiny middlewaru web relácie.
Krok 3. Urobte psovoda idempotentným
Idempotentný znamená: spracovanie tej istej udalosti dvakrát dáva rovnaký výsledok ako jej spracovanie raz. Najjednoduchším spoľahlivým prístupom je tabuľka spracovaných ID udalostí s jedinečným indexom. Najprv vložte; ak vloženie zlyhá, pretože ID existuje, udalosť ste už videli.
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;
}
Uloženie nespracovaného užitočného zaťaženia vám poskytne auditnú stopu: keď sa zákazník spýta „prečo mi bolo účtované dvakrát?“, môžete si prehrať presne to, čo vám povedal Stripe. Umožňuje tiež opätovné spracovanie neúspešných udalostí po oprave chyby.
Krok 4. Potvrďte rýchlo, spracujte samostatne
Stripe očakáva odpoveď v priebehu niekoľkých sekúnd. Ak váš obslužný program posiela e-maily, volá iné rozhrania API alebo generuje vložené súbory PDF, vyprší časový limit, Stripe to zopakuje a vy budete robiť rovnakú prácu znova. Urobte v požiadavke minimum – overte, uložte, odpovedzte 200 – a spustite skutočnú logiku v pracovníkovi frontu ( fronty Laravel, Symfony Messenger, status = 'received' alebo pracovník riadený supervízorom).
Ak sa udalosť nedá vôbec uložiť (databáza je vypnutá), vráťte stav 5xx. Toto je jediný prípad, keď chcete, aby Stripe zopakoval neskôr.
Krok 5. Zvládnite udalosti, na ktorých záleží
Nepotrebujete každý typ udalosti. Predplaťte si iba to, čo používa vaša obchodná logika; menej odberov znamená menej hluku a menšiu útočnú plochu. Typická sada:
checkout.session.completed– zákazník dokončil platbu. Vyhľadajte svoju objednávku pomocouclient_reference_idalebo metadát, ktoré ste nastavili pri vytváraní relácie.payment_intent.succeededapayment_intent.payment_failed– pre vlastné platobné toky.invoice.paidainvoice.payment_failed- fakturacia predplatneho.customer.subscription.updatedacustomer.subscription.deleted- zmeny a zrušenia plánu.charge.refundedacharge.dispute.created- peniaze idú späť.
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;
}
Dva zvyky zabraňujú väčšine chýb. Po prvé, nikdy neverte sumám z prehliadača: porovnajte zaplatenú sumu a menu v prípade s objednávkou vo vašej databáze. Po druhé, pretože udalosti môžu byť mimo poradia, urobte zmeny stavu monotónne: nepresúvajte objednávku z „zaplatenej“ späť do „nevybavenej“, pretože staršia udalosť prišla neskoro. Ak máte pochybnosti, načítajte aktuálny objekt z API namiesto spoliehania sa na snímku udalosti.
Krok 6. Otestujte lokálne pomocou Stripe CLI
Stripe CLI preposiela skutočné udalosti testovacieho režimu do vášho počítača, takže nepotrebujete verejný tunel:
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 vypíše dočasné tajomstvo whsec_. Vložte to do svojho lokálneho .env. Zámerne tiež otestujte nešťastné cesty: pošlite požiadavku s nesprávnym podpisom (očakávajte 400), pošlite tú istú udalosť dvakrát (očakávajte jeden databázový efekt) a zastavte na minútu svojho pracovníka vo fronte, aby ste videli, že udalosti sa namiesto zmiznutia hromadia.
Časté chyby, s ktorými sa stretávame pri auditoch
- Použitie presmerovania ako dôkaz o platbe. Stránku
success_urlmôže otvoriť ktokoľvek; kým webhook nepríde, zobrazovať „potvrdzovanie platby“. - Analýza JSON pred overením. Znovu zakódované telá porušujú podpis; Častým vinníkom sú analyzátory kostrového tela.
- Jedno tajomstvo na skúšku a naživo. Testovací a živý režim majú samostatné klávesy a samostatné koncové body webhooku.
- Žiadne protokolovanie. Bez uložených udalostí a viditeľného kanála zlyhania nájdu zákazníci chyby pri platbe.
- Pomalí manipulanti. Čokoľvek v priebehu niekoľkých sekúnd spustí opakovanie a duplicitnú prácu.
- Blocking Stripe na firewalle alebo WAF. Ak používate Cloudflare alebo iný WAF, povoľte cestu webhooku a skontrolujte, či pravidlá robotov nevracajú 403 na servery Stripe.
Monitorovanie: ako viete, že stále funguje
Webhook sa môže prerušiť mesiace po spustení: nasadená zmena premenuje stĺpec, tajomstvo sa otočí, platnosť certifikátu vyprší. Panel Stripe Dashboard zobrazuje pokusy o doručenie a zlyhania pre každý koncový bod a Stripe vám pošle e-mail, keď koncový bod stále zlyháva. Na svojej strane upozornite na tri veci: rastúci počet udalostí v received alebo failed, náhly pokles udalostí za hodinu a akúkoľvek frekvenciu 4xx alebo 5xx na trase webhooku. Malá denná úloha zosúlaďovania, ktorá porovnáva zaplatené objednávky vo vašej databáze so zoznamom úspešných platieb Stripe, zachytí zvyšok.
Ako môžeme pomôcť
Navrhujeme a vytvárame produkčné integrácie platieb a webhooku s kontrolami podpisov, idempotenciou, opakovanými pokusmi a protokolmi, ktoré môže váš tím čítať. Ak už máte Stripe tok a chcete druhý pár očí, preskúmanie architektúry alebo konzultácie sú najrýchlejším spôsobom, ako nájsť slabé miesta. Ak chcete získať širší obraz o presune kritických udalostí zo Zapier alebo Make, prečítajte si, keď sa neporuší žiadny kód a pre iného poskytovateľa si pozrite nášho sprievodcu integráciou WayForPay v PHP.
Časté otázky
Prečo overenie podpisu webhooku Stripe zlyhá?
Zvyčajnými príčinami je použitie nesprávneho tajného podpisu (test vs. živé, CLI verzus koncový bod Dashboard), overenie analyzovaného alebo znovu zakódovaného tela namiesto nespracovaného tela požiadavky alebo proxy, ktorý upravuje užitočné zaťaženie. Overte proti presným nespracovaným bajtom pomocou vlastného tajomstva whsec_ koncového bodu.
Ako zabránim spracovaniu udalosti Stripe dvakrát?
Uložte každé ID udalosti do tabuľky s jedinečným indexom a vložte ho pred vykonaním akejkoľvek práce. Ak vloženie zlyhá s duplicitným kľúčom, vráťte 200 a preskočte spracovanie. Zabezpečte tiež, aby sa samotná obchodná akcia mohla bezpečne opakovať.
Aký kód odpovede by mal vrátiť webhook Stripe ?
Po overení a uložení udalosti vráťte kód 2xx (200 alebo 204). Vráťte 400 za neplatný podpis a 5xx len vtedy, keď nemôžete uložiť udalosť a chcete, aby to Stripe zopakoval.
Ako dlho Stripe skúša neúspešné webhooky?
V živom režime Stripe opakuje pokusy s exponenciálnym stiahnutím až na tri dni. V testovacom režime je okno opakovania oveľa kratšie, takže sa pri posudzovaní produkčného správania nespoliehajte na opakované pokusy v testovacom režime.
Môžem testovať webhooky Stripe bez verejnej adresy URL?
áno. Spustite stripe listen --forward-to s Stripe CLI, aby ste preposlali testovacie udalosti na localhost, a použite stripe trigger na generovanie vzorových udalostí.
Bezplatný objavovací hovor
Prineste chaotický stack alebo zaseknutú integráciu - Integrono namapuje najmúdrejší prvý krok.
Dostanete seniorský pohľad na rozpočtové pasce, závislosti a na to, či kúpiť, zlepiť alebo postaviť - kým nezačne platená práca. CEO ostáva v okruhu.