diff --git a/packages/stripe/README.md b/packages/stripe/README.md index 74bc0fd0c..7ddfb7751 100644 --- a/packages/stripe/README.md +++ b/packages/stripe/README.md @@ -107,7 +107,7 @@ Failure to give Stripe access to the raw body will result in nasty runtime error ### Decorate Methods For Processing Webhook Events -Exposing provider/service methods to be used for processing Stripe events is easy! Simply use the provided decorator and indiciate the event type that the handler should receive. +Exposing provider/service methods to be used for processing Stripe events is easy! Simply use the provided decorator and indicate the event type that the handler should receive. [Review the Stripe documentation](https://stripe.com/docs/api/events/types) for more information about the types of events available. @@ -121,6 +121,20 @@ class PaymentCreatedService { } ``` +### Webhook Controller Decorators + +You can also pass any class decorator to the `decorators` property of the `webhookConfig` object as a part of the module configuration. This could be used in situations like when using the `@nestjs/throttler` package and needing to apply the `@ThrottlerSkip()` decorator, or when you have a global guard but need to skip routes with certain metadata. + +````typescript +StripeModule.forRoot(StripeModule, { + apiKey: '123', + webhookConfig: { + stripeWebhookSecret: 'super-secret', + decorators: [ThrottlerSkip()], + }, +}), +``` + ### Configure Webhooks in the Stripe Dashboard Follow the instructions from the [Stripe Documentation](https://stripe.com/docs/webhooks) for remaining integration steps such as testing your integration with the CLI before you go live and properly configuring the endpoint from the Stripe dashboard so that the correct events are sent to your NestJS app. @@ -132,3 +146,4 @@ Contributions welcome! Read the [contribution guidelines](../../CONTRIBUTING.md) ## License [MIT License](../../LICENSE) +```` diff --git a/packages/stripe/src/stripe.interfaces.ts b/packages/stripe/src/stripe.interfaces.ts index 88b995770..20a057462 100644 --- a/packages/stripe/src/stripe.interfaces.ts +++ b/packages/stripe/src/stripe.interfaces.ts @@ -22,6 +22,13 @@ export interface StripeModuleConfig extends Partial { */ controllerPrefix?: string; + /** + * Any metadata specific decorators you want to apply to the webhook handling controller. + * + * Note: these decorators must only set metadata that will be read at request time. Decorators like Nest's `@UsePipes()` or `@UseInterceptors()` wll not work, due to the time at which Nest reads the metadata for those, but something that uses `SetMetadata` will be fine, because that metadata is read at request time. + */ + decorators?: ClassDecorator[]; + /** * Logging configuration */ diff --git a/packages/stripe/src/stripe.module.ts b/packages/stripe/src/stripe.module.ts index 8131bcb01..dbb87428c 100644 --- a/packages/stripe/src/stripe.module.ts +++ b/packages/stripe/src/stripe.module.ts @@ -37,6 +37,9 @@ export class StripeModule controllerPrefix, StripeWebhookController ); + config.webhookConfig?.decorators?.forEach((deco) => { + deco(StripeWebhookController); + }); }, inject: [STRIPE_MODULE_CONFIG_TOKEN], }, diff --git a/packages/stripe/src/tests/stripe.module.spec.ts b/packages/stripe/src/tests/stripe.module.spec.ts index 6b7ac2895..0076d63e3 100644 --- a/packages/stripe/src/tests/stripe.module.spec.ts +++ b/packages/stripe/src/tests/stripe.module.spec.ts @@ -1,11 +1,14 @@ -import { INestApplication, Injectable } from '@nestjs/common'; +import { INestApplication, Injectable, SetMetadata } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import Stripe from 'stripe'; import { InjectStripeClient } from '../stripe.decorators'; +import { StripeWebhookController } from '../stripe.webhook.controller'; import { StripeModule } from './../stripe.module'; const testReceiveStripeFn = jest.fn(); +const TestDecorator = () => SetMetadata('TEST:METADATA', 'metadata'); + @Injectable() class TestService { constructor(@InjectStripeClient() private readonly stripeClient: Stripe) { @@ -36,4 +39,20 @@ describe('Stripe Module', () => { const client = testReceiveStripeFn.mock.calls[0][0]; expect(client).toBeInstanceOf(Stripe); }); + it('should apply the decorator to the controller', async () => { + await Test.createTestingModule({ + imports: [ + StripeModule.forRoot(StripeModule, { + apiKey: '123', + webhookConfig: { + stripeWebhookSecret: 'super-secret', + decorators: [TestDecorator()], + }, + }), + ], + }).compile(); + expect(Reflect.getMetadata('TEST:METADATA', StripeWebhookController)).toBe( + 'metadata' + ); + }); });