Laravel увімкнює CSRF на кожному web-маршруті. Провайдери вебхуків надсилають POST без cookie сесії та CSRF-токена — ви отримуєте 419 до контролера. Рішення: точкове виключення CSRF і сирий body через $request->getContent().
Виключити webhook-маршрути з CSRF
Додайте URI до масиву $except у App\Http\Middleware\VerifyCsrfToken (Laravel 11+: middleware у bootstrap/app.php):
protected $except = [
'webhooks/stripe',
'webhooks/*',
];
Виключайте лише server-to-server шляхи — не вимикайте CSRF глобально.
Читати сирий body для перевірки підпису
Після обходу CSRF читайте незмінені байти:
public function handle(Request $request)
{
$payload = $request->getContent(); // raw string
$signature = $request->header('Stripe-Signature', '');
if (!$this->verifySignature($payload, $signature)) {
return response('', 401);
}
$event = json_decode($payload, true, 512, JSON_THROW_ON_ERROR);
$this->processEvent($event);
return response('', 200);
}
Використовуйте getContent(), не all() чи input() — хелпери Laravel не дають підписані байти.
Перевіряти підпис до бізнес-логіки
Витягніть заголовки, обчисліть digest із сирого payload, порівняйте через hash_equals. При помилці — 401. Див. посібник перевірки підпису.
Налаштувати тунель
- Запустіть Laravel:
php artisan serve --port=8000. - Другий термінал:
npx portpreview 8000. - Вставте URL тунnel + шлях у dashboard.
- З
TrustProxiesдовіряйте forwarded-заголовкам тунnelю. - Надішліть тестову подію.
Типові пастки
Подвійне читання body
getContent() після json() порожній. Читайте раз, перевіряйте, парсіть.
Порядок middleware
Глобальний JSON-парсер до контролера змінює байти. Ізолюйте webhook-маршрути.
Повтори та ідемпотентність
Зберігайте ID подій — див. патерни повторів та ідемпотентності.
Далі
Основи — у основах тунелювання localhost та практичному локальному налагодженні вебхуків. Механіка підписів — у посібнику з перевірки підпису. Обробники без дублікатів — у патернах повторів та ідемпотентності. почніть PortPreview безкоштовно.
