Handlery webhook NestJS psują się subtelnie. Globalny ValidationPipe usuwa pola przed guardem, a domyślne parsowanie JSON niszczy podpisane bajty. Test lokalny powinien potwierdzić rawBody: true, guardy podpisu i walidację na trasie przed wklejeniem URL tunelu do Stripe lub GitHub.
Włącz raw body przy bootstrap
Przekaż rawBody: true przy tworzeniu aplikacji Nest, aby Express zachował nietknięty buffer:
async function bootstrap() {
const app = await NestFactory.create(AppModule, { rawBody: true });
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
await app.listen(3000);
}
Bez tej flagi req.rawBody jest undefined i każdy HMAC pada.
@Body() vs req.rawBody dla podpisów
Nigdy nie weryfikuj podpisu przez @Body() — Nest już sparsował obiekt. Czytaj buffer z middleware raw-body:
@Post('stripe')
@UseGuards(StripeSignatureGuard)
handleStripe(@Req() req: RawBodyRequest) {
const payload = req.rawBody;
const event = JSON.parse(payload.toString('utf8'));
this.events.process(event);
return { received: true };
}
Parsuj JSON dopiero po guardzie. Kolejność: guard, potem deserializacja.
Guard podpisu przed efektami ValidationPipe
Enkapsuluj weryfikację providera w guardzie czytającym nagłówki i porównującym digest:
@Injectable()
export class StripeSignatureGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const req = context.switchToHttp().getRequest>();
const sig = req.headers['stripe-signature'] as string;
return verify(req.rawBody, sig, process.env.STRIPE_WEBHOOK_SECRET);
}
}
Stosuj guard przez @UseGuards tylko na trasie webhook.
Unikaj pułapki globalnego ValidationPipe
Globalny ValidationPipe z transform: true może wymuszać typy przed kontrolerem. Trasy webhook potrzebują rawBody + ręcznego JSON.parse po weryfikacji.
Test lokalny nest start + PortPreview
- Start:
npm run start:devlubnest start --watchna porcie 3000. - Wystaw:
npx portpreview 3000. - Zarejestruj URL tunelu + ścieżkę webhook.
- Wywołaj testowe dostawy.
- Powtórz zduplikowane event ID — zobacz wzorce retry.
Typowe pułapki
ValidationPipe przed podpisem
Jeśli walidacja idzie pierwsza, Nest może przekształcić body i zostawić pusty rawBody. Guardy muszą działać na raw buffer.
Kolejność globalnego middleware JSON
Express json() przed raw-body zużywa stream. Zachowaj rawBody: true przy tworzeniu factory.
Wolne handlery i retry
Szybko zwracaj 200 po weryfikacji, kolejkuj ciężką pracę, deduplikuj po event ID.
Więcej
Podstawy znajdziesz w podstawach tunelowania localhost oraz praktycznym lokalnym debugowaniu webhooków. Mechanika podpisu — w przewodniku weryfikacji podpisu. Obsługa bez duplikatów — w wzorach retry i idempotencji. zacznij PortPreview za darmo.
