ngrok daje lokalnemu endpointowi tymczasowy publiczny URL HTTPS. Jest dobry w developmentcie, lecz każdy request pozostaje wrogi do czasu weryfikacji podpisu dostawcy. URL callbacku rejestruj w konsoli providera jako zwykłe zdanie, nie H1 ukryte w środku prozy.
php artisan serve
ngrok http 8000
Dla Stripe zweryfikuj surowe body oficjalnym signing secretem. md5, $request->all() i event type od klienta niczego nie dowodzą, a nawet mogą zmienić payload przed weryfikacją.
<?php
declare(strict_types=1);
use Illuminate\Http\Request;
use Stripe\Webhook;
public function __invoke(Request $request): Response
{
$event = Webhook::constructEvent($request->getContent(), (string) $request->header('Stripe-Signature'), config('services.stripe.webhook_secret'));
ProcessStripeEvent::dispatch($event->id, $event->type, $event->data->object);
return response()->noContent();
}
Przed nieodwracalną pracą zapisz ID eventu dostawcy z unikalnym ograniczeniem bazy; retry są normalne. Po walidacji szybko zwróć 2xx, a wolną pracę kolejkij. Usuń dd() przed testem: powoduje failed delivery i może ujawnić dane płatności. Inspector ngrok służy do porównania surowego payloadu oraz nagłówków, nigdy do autoryzacji.
Tunel, idempotencja i test
Darmowy URL zmienia się po sesji, a zarezerwowana domena stabilizuje callback deweloperski. Herd i Valet potrzebują zachowania hosta aplikacji.
ngrok config add-authtoken "$NGROK_AUTHTOKEN"
ngrok http --domain=webhooks-dev.example.ngrok.app 8000
ngrok http --host-header=app.test 80
Podpis weryfikuj na surowym body przed parsowaniem. Zapisz ID zdarzenia z unikalnym ograniczeniem przed kolejką, aby retry nie wykonało skutku dwa razy.
<?php
declare(strict_types=1);
use Illuminate\\Http\\Request;
use Illuminate\\Http\\Response;
use Stripe\\Exception\\SignatureVerificationException;
use Stripe\\Webhook;
final class StripeWebhookController
{
public function __invoke(Request $request): Response
{
try {
Webhook::constructEvent($request->getContent(), (string) $request->header('Stripe-Signature'), (string) config('services.stripe.webhook_secret'));
} catch (SignatureVerificationException) {
return response()->noContent(400);
}
return response()->noContent();
}
}
Inspector porównuje raw payload i odpowiedź, ale nie jest mechanizmem zaufania. Większość gałęzi szybciej zabezpiecza feature test z podpisanym fixture; tunel zostaw dla konfiguracji dostawcy i retry.
Kiedy tego NIE używać
Nie traktuj tymczasowego tunelu jako production ingress i nie używaj go, gdy oficjalne CLI providera dokładniej forwarduje podpisane eventy. Testy automatyczne powinny używać podpisanych fixture oraz ścieżki replay bez zewnętrznego tunelu.