Handler webhook NestJS hỏng một cách tinh vi. ValidationPipe toàn cục loại field trước guard và JSON parse mặc định phá byte provider đã ký. Test cục bộ cần chứng minh rawBody: true, signature guard và validation theo route trước khi dán URL tunnel vào Stripe hoặc GitHub.
Bật raw body lúc bootstrap
Truyền rawBody: true khi tạo app Nest để Express giữ buffer nguyên vẹn:
async function bootstrap() {
const app = await NestFactory.create(AppModule, { rawBody: true });
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
await app.listen(3000);
}
Không có flag này, req.rawBody undefined và mọi HMAC đều fail.
@Body() vs req.rawBody cho chữ ký
Đừng xác minh chữ ký bằng @Body() — Nest đã parse object. Đọc buffer từ raw-body middleware:
@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 };
}
Parse JSON chỉ sau khi guard xác nhận. Thứ tự: guard trước, deserialize sau.
Signature guard trước hiệu ứng ValidationPipe
Gói xác minh provider trong guard đọc header và so sánh 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);
}
}
Áp guard bằng @UseGuards chỉ trên route webhook.
Tránh bẫy ValidationPipe toàn cục
ValidationPipe toàn cục với transform: true có thể ép kiểu trước controller. Route webhook cần rawBody + JSON.parse thủ công sau xác minh.
Test cục bộ nest start + PortPreview
- Chạy:
npm run start:devhoặcnest start --watchcổng 3000. - Expose:
npx portpreview 3000. - Đăng ký URL tunnel + path webhook.
- Kích hoạt giao hàng thử.
- Phát lại event ID trùng — xem mẫu retry.
Bẫy thường gặp
ValidationPipe trước chữ ký
Nếu validation chạy trước, Nest có thể biến đổi body và để rawBody rỗng. Guard phải chạy trên raw buffer.
Thứ tự middleware JSON toàn cục
Express json() trước raw-body tiêu thụ stream. Giữ rawBody: true khi tạo factory.
Handler chậm và retry
Trả 200 nhanh sau xác minh, xếp hàng việc nặng, dedupe theo event ID.
Đọc thêm
Đọc thêm về cơ bản về localhost tunneling và gỡ lỗi webhook cục bộ. Cơ chế chữ ký xem hướng dẫn xác minh chữ ký. Handler chống trùng lặp xem mẫu retry và idempotency. bắt đầu PortPreview miễn phí.
