mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 04:10:32 +00:00
feat: add stripe payment webhooks controller
This commit is contained in:
@@ -15,7 +15,7 @@ export class GetPaymentMethodsStateService {
|
||||
private readonly paymentIntegrationModel: TenantModelProxy<
|
||||
typeof PaymentIntegration
|
||||
>,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Retrieves the payment state provising state.
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ExchangeStripeOAuthTokenService } from './ExchangeStripeOauthToken';
|
||||
import { SeedStripeAccountsOnOAuthGrantedSubscriber } from './subscribers/SeedStripeAccounts';
|
||||
import { StripeWebhooksSubscriber } from './subscribers/StripeWebhooksSubscriber';
|
||||
import { StripeIntegrationController } from './StripePayment.controller';
|
||||
import { StripePaymentWebhooksController } from './StripePaymentWebhooks.controller';
|
||||
import { StripePaymentService } from './StripePaymentService';
|
||||
import { GetStripeAuthorizationLinkService } from './GetStripeAuthorizationLink';
|
||||
import { AccountsModule } from '../Accounts/Accounts.module';
|
||||
@@ -33,6 +34,6 @@ import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||
TenancyContext,
|
||||
],
|
||||
exports: [StripePaymentService, GetStripeAuthorizationLinkService],
|
||||
controllers: [StripeIntegrationController],
|
||||
controllers: [StripeIntegrationController, StripePaymentWebhooksController],
|
||||
})
|
||||
export class StripePaymentModule {}
|
||||
export class StripePaymentModule { }
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import {
|
||||
Controller,
|
||||
Headers,
|
||||
HttpCode,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Post,
|
||||
Req,
|
||||
Res,
|
||||
} from '@nestjs/common';
|
||||
import { Request, Response } from 'express';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { StripePaymentService } from './StripePaymentService';
|
||||
import { events } from '@/common/events/events';
|
||||
import {
|
||||
StripeCheckoutSessionCompletedEventPayload,
|
||||
StripeWebhookEventPayload,
|
||||
} from './StripePayment.types';
|
||||
import { PublicRoute } from '../Auth/guards/jwt.guard';
|
||||
|
||||
@Controller('/webhooks/stripe')
|
||||
@ApiTags('stripe')
|
||||
@PublicRoute()
|
||||
export class StripePaymentWebhooksController {
|
||||
constructor(
|
||||
private readonly stripePaymentService: StripePaymentService,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly configService: ConfigService,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Handles incoming Stripe webhook events.
|
||||
* Verifies the webhook signature, processes the event based on its type,
|
||||
* and triggers appropriate actions or events in the system.
|
||||
* @param {Request} req - The Express request object containing the webhook payload.
|
||||
* @param {Response} res - The Express response object.
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
@Post('/')
|
||||
@HttpCode(200)
|
||||
@ApiOperation({ summary: 'Listen to Stripe webhooks' })
|
||||
async handleWebhook(
|
||||
@Req() req: Request,
|
||||
@Res() res: Response,
|
||||
@Headers('stripe-signature') signature: string,
|
||||
) {
|
||||
console.log(signature, 'signature');
|
||||
try {
|
||||
// @ts-ignore - rawBody is set by middleware
|
||||
const rawBody = req.rawBody || req.body;
|
||||
const webhooksSecret = this.configService.get(
|
||||
'stripePayment.webhooksSecret',
|
||||
);
|
||||
if (!webhooksSecret) {
|
||||
throw new HttpException(
|
||||
'Stripe webhook secret is not configured',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
if (!signature) {
|
||||
throw new HttpException(
|
||||
'Stripe signature header is missing',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
let event;
|
||||
|
||||
// Verify webhook signature and extract the event.
|
||||
// See https://stripe.com/docs/webhooks#verify-events for more information.
|
||||
try {
|
||||
event = this.stripePaymentService.stripe.webhooks.constructEvent(
|
||||
rawBody,
|
||||
signature,
|
||||
webhooksSecret,
|
||||
);
|
||||
} catch (err) {
|
||||
throw new HttpException(
|
||||
`Webhook Error: ${err.message}`,
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
console.log(event.type, 'event.type');
|
||||
// Handle the event based on its type
|
||||
switch (event.type) {
|
||||
case 'checkout.session.completed':
|
||||
// Triggers `onStripeCheckoutSessionCompleted` event.
|
||||
await this.eventEmitter.emitAsync(
|
||||
events.stripeWebhooks.onCheckoutSessionCompleted,
|
||||
{
|
||||
event,
|
||||
} as StripeCheckoutSessionCompletedEventPayload,
|
||||
);
|
||||
break;
|
||||
|
||||
case 'account.updated':
|
||||
// Triggers `onStripeAccountUpdated` event.
|
||||
await this.eventEmitter.emitAsync(
|
||||
events.stripeWebhooks.onAccountUpdated,
|
||||
{
|
||||
event,
|
||||
} as StripeWebhookEventPayload,
|
||||
);
|
||||
break;
|
||||
|
||||
// Add more cases as needed
|
||||
default:
|
||||
console.log(`Unhandled event type ${event.type}`);
|
||||
}
|
||||
|
||||
return res.status(200).json({ received: true });
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
throw new HttpException(
|
||||
error.message || 'Internal server error',
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user