NestJS 和 Stripe 未定义 rawBody

问题描述 投票:0回答:1

我正在 Nest.js 应用程序中设置 Stripe Webhook,但在访问原始请求正文 (req.rawBody) 时遇到问题。 Stripe 需要此原始正文来进行 Webhook 签名验证。尽管按照 Nest.js 文档 (https://docs.nestjs.com/faq/raw-body) 配置 rawBody,req.rawBody 返回未定义,导致 Stripe 出现“Webhook 签名验证失败”错误。

这是我的 webhook 处理程序的片段:

@Controller('panier')
export class PanierController {
  private stripe: Stripe;

  constructor(private config: ConfigService) {
    this.stripe = new Stripe(this.config.get('STRIPE_SECRET'));
  }

  @Post('webhook')
  async incomingEvents(@Headers('stripe-signature') signature: string, @Req() req: RawBodyRequest<Request>) {
    const webhookSecret = this.config.get('STRIPE_WEBHOOK_SECRET');
    if (!signature) {
      throw new BadRequestException('Missing stripe signature');
    }

    console.log(req.rawBody); // undefined
    let event;
    try {
      // Construct the event using raw request body, signature, and webhook secret
      event = this.stripe.webhooks.constructEvent(req.rawBody, signature, webhookSecret);
    } catch (err) {
      console.error(err.message);
      throw new Error('Webhook signature verification failed.');
    }
    console.log(event);
  }
}

在 main.ts 中,我已使用 rawBody: true:

配置了 Nest.js 应用程序
app = await NestFactory.create<NestExpressApplication>(AppModule, {
    rawBody: true,
  });

尽管在我的应用程序配置中设置了 rawBody: true ,但我的控制器中的 req.rawBody 仍然未定义。如何正确访问原始请求正文以解决 Stripe webhook 签名验证错误?

node.js request nestjs stripe-payments webhooks
1个回答
0
投票

对我有用的几乎与你写的一样 - with "@nestjs/core": "10.3.9":

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { rawBody: true });
}

// Controller
@Controller('billing')
export class BillingController {
  @Post('webhook') webhook(@Req() request: RawBodyRequest<Request>) {
    return this.billingService.webhook(request);
  }
}

// Service
@Injectable()
export class BillingService {
  constructor(private readonly stripe: StripeService) {}

  webhook(request: RawBodyRequest<Request>) {
    Logger.log(`Received Stripe Webhook.`, 'BillingService.webhook');
    const header = request.header('Stripe-Signature') ?? '';
    return from(
      this.stripe.stripe.webhooks.constructEventAsync(
        request.rawBody,
        header,
        process.env.STRIPE_WEBHOOK_SECRET,
      ),
    ).pipe(
      catchError((err) => {
        Logger.error(err, 'BillingService.webhook');
      }),
      concatMap((event) => {
        // Extract the object from the event.
        const dataObject = event.data.object;
        
        // ...

        return of({});
      }),
    );
  }
}

@Injectable()
export class StripeService {
  public readonly stripe: Stripe;
  constructor(
    @Inject(MODULE_OPTIONS_TOKEN) private options: StripeModuleOptions,
  ) {
    this.stripe = new Stripe(this.options.apiKey, this.options.options);
  }
}


export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
  new ConfigurableModuleBuilder<StripeModuleOptions>()
    .setClassMethodName('forRoot')
    .build();
© www.soinside.com 2019 - 2024. All rights reserved.