mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 15:20:34 +00:00
fix: stripe payment webhooks
This commit is contained in:
@@ -15,7 +15,7 @@ global.__views_dirname = path.join(global.__static_dirname, '/views');
|
|||||||
global.__images_dirname = path.join(global.__static_dirname, '/images');
|
global.__images_dirname = path.join(global.__static_dirname, '/images');
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule, { rawBody: true });
|
||||||
app.setGlobalPrefix('/api');
|
app.setGlobalPrefix('/api');
|
||||||
|
|
||||||
// create and mount the middleware manually here
|
// create and mount the middleware manually here
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Knex } from 'knex';
|
||||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||||
import { SaleInvoice } from '../models/SaleInvoice';
|
import { SaleInvoice } from '../models/SaleInvoice';
|
||||||
import { SaleInvoiceTransformer } from './SaleInvoice.transformer';
|
import { SaleInvoiceTransformer } from './SaleInvoice.transformer';
|
||||||
@@ -17,7 +18,7 @@ export class GetSaleInvoice {
|
|||||||
|
|
||||||
@Inject(SaleInvoice.name)
|
@Inject(SaleInvoice.name)
|
||||||
private saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
private saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve sale invoice with associated entries.
|
* Retrieve sale invoice with associated entries.
|
||||||
@@ -27,9 +28,10 @@ export class GetSaleInvoice {
|
|||||||
*/
|
*/
|
||||||
public async getSaleInvoice(
|
public async getSaleInvoice(
|
||||||
saleInvoiceId: number,
|
saleInvoiceId: number,
|
||||||
|
trx?: Knex.Transaction,
|
||||||
): Promise<SaleInvoiceResponseDto> {
|
): Promise<SaleInvoiceResponseDto> {
|
||||||
const saleInvoice = await this.saleInvoiceModel()
|
const saleInvoice = await this.saleInvoiceModel()
|
||||||
.query()
|
.query(trx)
|
||||||
.findById(saleInvoiceId)
|
.findById(saleInvoiceId)
|
||||||
.withGraphFetched('entries.item')
|
.withGraphFetched('entries.item')
|
||||||
.withGraphFetched('entries.tax')
|
.withGraphFetched('entries.tax')
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export class CreatePaymentReceiveStripePayment {
|
|||||||
private readonly createPaymentReceivedService: CreatePaymentReceivedService,
|
private readonly createPaymentReceivedService: CreatePaymentReceivedService,
|
||||||
private readonly uow: UnitOfWork,
|
private readonly uow: UnitOfWork,
|
||||||
private readonly accountRepository: AccountRepository,
|
private readonly accountRepository: AccountRepository,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a payment received transaction associated to the given invoice.
|
* Creates a payment received transaction associated to the given invoice.
|
||||||
@@ -28,7 +28,7 @@ export class CreatePaymentReceiveStripePayment {
|
|||||||
|
|
||||||
// Retrieves the given invoice to create payment transaction associated to it.
|
// Retrieves the given invoice to create payment transaction associated to it.
|
||||||
const invoice =
|
const invoice =
|
||||||
await this.getSaleInvoiceService.getSaleInvoice(saleInvoiceId);
|
await this.getSaleInvoiceService.getSaleInvoice(saleInvoiceId, trx);
|
||||||
|
|
||||||
const paymentReceivedDTO = {
|
const paymentReceivedDTO = {
|
||||||
customerId: invoice.customerId,
|
customerId: invoice.customerId,
|
||||||
@@ -38,6 +38,7 @@ export class CreatePaymentReceiveStripePayment {
|
|||||||
referenceNo: '',
|
referenceNo: '',
|
||||||
statement: '',
|
statement: '',
|
||||||
depositAccountId: stripeClearingAccount.id,
|
depositAccountId: stripeClearingAccount.id,
|
||||||
|
branchId: invoice.branchId,
|
||||||
entries: [{ invoiceId: saleInvoiceId, paymentAmount: paidAmount }],
|
entries: [{ invoiceId: saleInvoiceId, paymentAmount: paidAmount }],
|
||||||
};
|
};
|
||||||
// Create a payment received transaction associated to the given invoice.
|
// Create a payment received transaction associated to the given invoice.
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import {
|
|||||||
HttpException,
|
HttpException,
|
||||||
HttpStatus,
|
HttpStatus,
|
||||||
Post,
|
Post,
|
||||||
|
RawBodyRequest,
|
||||||
Req,
|
Req,
|
||||||
Res,
|
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
@@ -42,13 +42,10 @@ export class StripePaymentWebhooksController {
|
|||||||
@HttpCode(200)
|
@HttpCode(200)
|
||||||
@ApiOperation({ summary: 'Listen to Stripe webhooks' })
|
@ApiOperation({ summary: 'Listen to Stripe webhooks' })
|
||||||
async handleWebhook(
|
async handleWebhook(
|
||||||
@Req() req: Request,
|
@Req() req: RawBodyRequest<Request>,
|
||||||
@Res() res: Response,
|
|
||||||
@Headers('stripe-signature') signature: string,
|
@Headers('stripe-signature') signature: string,
|
||||||
) {
|
) {
|
||||||
console.log(signature, 'signature');
|
|
||||||
try {
|
try {
|
||||||
// @ts-ignore - rawBody is set by middleware
|
|
||||||
const rawBody = req.rawBody || req.body;
|
const rawBody = req.rawBody || req.body;
|
||||||
const webhooksSecret = this.configService.get(
|
const webhooksSecret = this.configService.get(
|
||||||
'stripePayment.webhooksSecret',
|
'stripePayment.webhooksSecret',
|
||||||
@@ -82,7 +79,6 @@ export class StripePaymentWebhooksController {
|
|||||||
HttpStatus.BAD_REQUEST,
|
HttpStatus.BAD_REQUEST,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
console.log(event.type, 'event.type');
|
|
||||||
// Handle the event based on its type
|
// Handle the event based on its type
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case 'checkout.session.completed':
|
case 'checkout.session.completed':
|
||||||
@@ -94,7 +90,6 @@ export class StripePaymentWebhooksController {
|
|||||||
} as StripeCheckoutSessionCompletedEventPayload,
|
} as StripeCheckoutSessionCompletedEventPayload,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'account.updated':
|
case 'account.updated':
|
||||||
// Triggers `onStripeAccountUpdated` event.
|
// Triggers `onStripeAccountUpdated` event.
|
||||||
await this.eventEmitter.emitAsync(
|
await this.eventEmitter.emitAsync(
|
||||||
@@ -109,8 +104,7 @@ export class StripePaymentWebhooksController {
|
|||||||
default:
|
default:
|
||||||
console.log(`Unhandled event type ${event.type}`);
|
console.log(`Unhandled event type ${event.type}`);
|
||||||
}
|
}
|
||||||
|
return { received: true };
|
||||||
return res.status(200).json({ received: true });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof HttpException) {
|
if (error instanceof HttpException) {
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
import { ClsService } from 'nestjs-cls';
|
||||||
import { CreatePaymentReceiveStripePayment } from '../CreatePaymentReceivedStripePayment';
|
import { CreatePaymentReceiveStripePayment } from '../CreatePaymentReceivedStripePayment';
|
||||||
import {
|
import {
|
||||||
StripeCheckoutSessionCompletedEventPayload,
|
StripeCheckoutSessionCompletedEventPayload,
|
||||||
StripeWebhookEventPayload,
|
StripeWebhookEventPayload,
|
||||||
} from '../StripePayment.types';
|
} from '../StripePayment.types';
|
||||||
// import { initalizeTenantServices } from '@/api/middleware/TenantDependencyInjection';
|
|
||||||
// import { initializeTenantSettings } from '@/api/middleware/SettingsMiddleware';
|
|
||||||
import { OnEvent } from '@nestjs/event-emitter';
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
|
||||||
import { events } from '@/common/events/events';
|
import { events } from '@/common/events/events';
|
||||||
import { PaymentIntegration } from '../models/PaymentIntegration.model';
|
import { PaymentIntegration } from '../models/PaymentIntegration.model';
|
||||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
import { TenantModel } from '@/modules/System/models/TenantModel';
|
import { TenantModel } from '@/modules/System/models/TenantModel';
|
||||||
|
import { SystemUser } from '@/modules/System/models/SystemUser';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StripeWebhooksSubscriber {
|
export class StripeWebhooksSubscriber {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly createPaymentReceiveStripePayment: CreatePaymentReceiveStripePayment,
|
private readonly createPaymentReceiveStripePayment: CreatePaymentReceiveStripePayment,
|
||||||
|
private readonly clsService: ClsService,
|
||||||
|
|
||||||
|
@Inject(SystemUser.name)
|
||||||
|
private readonly systemUserModel: typeof SystemUser,
|
||||||
|
|
||||||
@Inject(PaymentIntegration.name)
|
@Inject(PaymentIntegration.name)
|
||||||
private readonly paymentIntegrationModel: TenantModelProxy<
|
private readonly paymentIntegrationModel: TenantModelProxy<
|
||||||
@@ -23,8 +27,8 @@ export class StripeWebhooksSubscriber {
|
|||||||
>,
|
>,
|
||||||
|
|
||||||
@Inject(TenantModel.name)
|
@Inject(TenantModel.name)
|
||||||
private readonly tenantModel: typeof TenantModel
|
private readonly tenantModel: typeof TenantModel,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the checkout session completed webhook event.
|
* Handles the checkout session completed webhook event.
|
||||||
@@ -38,10 +42,22 @@ export class StripeWebhooksSubscriber {
|
|||||||
const tenantId = parseInt(metadata.tenantId, 10);
|
const tenantId = parseInt(metadata.tenantId, 10);
|
||||||
const saleInvoiceId = parseInt(metadata.saleInvoiceId, 10);
|
const saleInvoiceId = parseInt(metadata.saleInvoiceId, 10);
|
||||||
|
|
||||||
|
const tenant = await this.tenantModel
|
||||||
|
.query()
|
||||||
|
.findOne({ id: tenantId })
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
const user = await this.systemUserModel
|
||||||
|
.query()
|
||||||
|
.findOne({
|
||||||
|
tenantId: tenant.id,
|
||||||
|
})
|
||||||
|
.modify('active')
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
// await initalizeTenantServices(tenantId);
|
this.clsService.set('organizationId', tenant.organizationId);
|
||||||
// await initializeTenantSettings(tenantId);
|
this.clsService.set('userId', user.id);
|
||||||
|
this.clsService.set('tenantId', tenant.id);
|
||||||
|
|
||||||
// Get the amount from the event
|
// Get the amount from the event
|
||||||
const amount = event.data.object.amount_total;
|
const amount = event.data.object.amount_total;
|
||||||
|
|||||||
Reference in New Issue
Block a user