Stripe webhooks - "undefined" is not valid JSON
JustDenP opened this issue · comments
Hello, everytime I see the same error after a stripe webhooks are sent to my nest server with local stripe cli. Anyone knows what causes the error and maybe how to fix it? Thank you!
ERROR [ExceptionsHandler] "undefined" is not valid JSON
SyntaxError: "undefined" is not valid JSON
at JSON.parse (<anonymous>)
at StripePayloadService.tryHydratePayload (/Users/denispenkov/Warehouse/dragon/node_modules/.pnpm/@golevelup+nestjs-stripe@0.8.0_@nestjs+common@10.3.8_@nestjs+core@10.3.8_rxjs@7.8.1_stripe@15.5.0/node_modules/@golevelup/nestjs-stripe/src/stripe.payload.service.ts:33:33)
at StripeWebhookController.handleWebhook (/Users/denispenkov/Warehouse/dragon/node_modules/.pnpm/@golevelup+nestjs-stripe@0.8.0_@nestjs+common@10.3.8_@nestjs+core@10.3.8_rxjs@7.8.1_stripe@15.5.0/node_modules/@golevelup/nestjs-stripe/src/stripe.webhook.controller.ts:31:45)
at /Users/denispenkov/Warehouse/dragon/node_modules/.pnpm/@nestjs+core@10.3.8_@nestjs+common@10.3.8_@nestjs+platform-express@10.3.8_reflect-metadata@0.2.2_rxjs@7.8.1/node_modules/@nestjs/core/router/router-execution-context.js:38:29
I have the default webhook without any logic
@Injectable()
export class PaymentWebhookService {
constructor(
@InjectStripeClient() private stripe: Stripe,
private readonly paymentService: PaymentService,
) {}
@StripeWebhookHandler('charge.succeeded')
async handlePayment(event: Stripe.Event): Promise<void> {
const dataObject = event.data.object as Stripe.Charge;
}
}
@JustDenP
Hey, I encountered the same issue as well. Could you please let me know how you resolved it?
@Sky-FE Hi, the issue is related to undefined rawBody in request, make sure you set rawBody for these webhooks properly.
I use middleware for the specific endpoints:
raw-body.middleware.ts
import { RawBodyRequest } from '@nestjs/common';
import { json, Request, Response } from 'express';
export interface RawBodyMiddlewareOptions {
limit: string;
rawBodyUrls: string[];
}
export function rawBodyMiddleware(options: RawBodyMiddlewareOptions): unknown {
const rawBodyUrls = new Set(options.rawBodyUrls);
return json({
...options,
verify: (request: RawBodyRequest<Request>, _: Response, buffer: Buffer) => {
if (rawBodyUrls.has(request.url) && Buffer.isBuffer(buffer)) {
request.rawBody = Buffer.from(buffer);
}
return true;
},
});
}
Then in your main.ts file you can use it before body parser if you have it
app.use(
rawBodyMiddleware({
limit: '5mb',
rawBodyUrls: ['/stripe/webhook', '/payment/test'],
}),
);
app.use(bodyParser.json({ limit: '5mb' }));
app.use(bodyParser.urlencoded({ limit: '5mb', extended: true }));
You can create any post endpoint to test it and make sure it is working and rawBody is there.
After that, if you will face another error "No signatures found matching the expected signature for payload", make sure to set up accountTest if you are using local stripe cli. I have it like this
webhookConfig: {
decorators: [SkipThrottle()],
requestBodyProperty: 'rawBody',
stripeSecrets: {
account: configService.get('stripe.account', { infer: true }),
accountTest: configService.get('stripe.account', { infer: true }),
},
},
If you still have issues, you can write here, I also spent the whole day so set it right haha