mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
feat(nestjs): migrate to NestJS
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import { Knex } from 'knex';
|
||||
import { GetSaleInvoice } from '../SaleInvoices/queries/GetSaleInvoice.service';
|
||||
import { CreatePaymentReceivedService } from '../PaymentReceived/commands/CreatePaymentReceived.serivce';
|
||||
import { UnitOfWork } from '../Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { AccountRepository } from '../Accounts/repositories/Account.repository';
|
||||
|
||||
@Injectable()
|
||||
export class CreatePaymentReceiveStripePayment {
|
||||
constructor(
|
||||
private readonly getSaleInvoiceService: GetSaleInvoice,
|
||||
private readonly createPaymentReceivedService: CreatePaymentReceivedService,
|
||||
private readonly uow: UnitOfWork,
|
||||
private readonly accountRepository: AccountRepository,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Creates a payment received transaction associated to the given invoice.
|
||||
* @param {number} saleInvoiceId - Sale invoice id.
|
||||
* @param {number} paidAmount - Paid amount.
|
||||
*/
|
||||
async createPaymentReceived(saleInvoiceId: number, paidAmount: number) {
|
||||
// Create a payment received transaction under UOW envirement.
|
||||
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||
// Finds or creates a new stripe payment clearing account (current asset).
|
||||
const stripeClearingAccount =
|
||||
await this.accountRepository.findOrCreateStripeClearing({}, trx);
|
||||
|
||||
// Retrieves the given invoice to create payment transaction associated to it.
|
||||
const invoice =
|
||||
await this.getSaleInvoiceService.getSaleInvoice(saleInvoiceId);
|
||||
|
||||
const paymentReceivedDTO = {
|
||||
customerId: invoice.customerId,
|
||||
paymentDate: new Date(),
|
||||
amount: paidAmount,
|
||||
exchangeRate: 1,
|
||||
referenceNo: '',
|
||||
statement: '',
|
||||
depositAccountId: stripeClearingAccount.id,
|
||||
entries: [{ invoiceId: saleInvoiceId, paymentAmount: paidAmount }],
|
||||
};
|
||||
// Create a payment received transaction associated to the given invoice.
|
||||
await this.createPaymentReceivedService.createPaymentReceived(
|
||||
paymentReceivedDTO,
|
||||
trx,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { StripePaymentService } from './StripePaymentService';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class CreateStripeAccountLinkService {
|
||||
constructor(private readonly stripePaymentService: StripePaymentService) {}
|
||||
|
||||
/**
|
||||
* Creates a new Stripe account id.
|
||||
* @param {string} stripeAccountId - Stripe account id.
|
||||
*/
|
||||
public createAccountLink(stripeAccountId: string) {
|
||||
return this.stripePaymentService.createAccountLink(stripeAccountId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { CreateStripeAccountDTO } from './types';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { StripePaymentService } from './StripePaymentService';
|
||||
import { events } from '@/common/events/events';
|
||||
import { PaymentIntegration } from './models/PaymentIntegration.model';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class CreateStripeAccountService {
|
||||
constructor(
|
||||
private readonly stripePaymentService: StripePaymentService,
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
|
||||
@Inject(PaymentIntegration.name)
|
||||
private readonly paymentIntegrationModel: TenantModelProxy<
|
||||
typeof PaymentIntegration
|
||||
>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Creates a new Stripe account.
|
||||
* @param {CreateStripeAccountDTO} stripeAccountDTO
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async createStripeAccount(
|
||||
stripeAccountDTO?: CreateStripeAccountDTO,
|
||||
): Promise<string> {
|
||||
const stripeAccount = await this.stripePaymentService.createAccount();
|
||||
const stripeAccountId = stripeAccount.id;
|
||||
|
||||
const parsedStripeAccountDTO = {
|
||||
name: 'Stripe',
|
||||
...stripeAccountDTO,
|
||||
};
|
||||
// Stores the details of the Stripe account.
|
||||
await this.paymentIntegrationModel().query().insert({
|
||||
name: parsedStripeAccountDTO.name,
|
||||
accountId: stripeAccountId,
|
||||
active: false, // Active will turn true after onboarding.
|
||||
service: 'Stripe',
|
||||
});
|
||||
// Triggers `onStripeIntegrationAccountCreated` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.stripeIntegration.onAccountCreated,
|
||||
{
|
||||
stripeAccountDTO,
|
||||
stripeAccountId,
|
||||
},
|
||||
);
|
||||
return stripeAccountId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { StripePaymentService } from './StripePaymentService';
|
||||
import { Knex } from 'knex';
|
||||
import { StripeOAuthCodeGrantedEventPayload } from './types';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { UnitOfWork } from '../Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { PaymentIntegration } from './models/PaymentIntegration.model';
|
||||
import { events } from '@/common/events/events';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class ExchangeStripeOAuthTokenService {
|
||||
constructor(
|
||||
private readonly stripePaymentService: StripePaymentService,
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
private readonly uow: UnitOfWork,
|
||||
|
||||
@Inject(PaymentIntegration.name)
|
||||
private readonly paymentIntegrationModel: TenantModelProxy<
|
||||
typeof PaymentIntegration
|
||||
>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Exchange stripe oauth authorization code to access token and user id.
|
||||
* @param {string} authorizationCode
|
||||
*/
|
||||
public async excahngeStripeOAuthToken(authorizationCode: string) {
|
||||
const stripe = this.stripePaymentService.stripe;
|
||||
|
||||
const response = await stripe.oauth.token({
|
||||
grant_type: 'authorization_code',
|
||||
code: authorizationCode,
|
||||
});
|
||||
// const accessToken = response.access_token;
|
||||
// const refreshToken = response.refresh_token;
|
||||
const stripeUserId = response.stripe_user_id;
|
||||
|
||||
// Retrieves details of the Stripe account.
|
||||
const account = await stripe.accounts.retrieve(stripeUserId, {
|
||||
expand: ['business_profile'],
|
||||
});
|
||||
const companyName = account.business_profile?.name || 'Unknown name';
|
||||
const paymentEnabled = account.charges_enabled;
|
||||
const payoutEnabled = account.payouts_enabled;
|
||||
|
||||
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||
// Stores the details of the Stripe account.
|
||||
const paymentIntegration = await this.paymentIntegrationModel()
|
||||
.query(trx)
|
||||
.insert({
|
||||
name: companyName,
|
||||
service: 'Stripe',
|
||||
accountId: stripeUserId,
|
||||
paymentEnabled,
|
||||
payoutEnabled,
|
||||
});
|
||||
// Triggers `onStripeOAuthCodeGranted` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.stripeIntegration.onOAuthCodeGranted,
|
||||
{
|
||||
paymentIntegrationId: paymentIntegration.id,
|
||||
trx,
|
||||
} as StripeOAuthCodeGrantedEventPayload,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
@Injectable()
|
||||
export class GetStripeAuthorizationLinkService {
|
||||
constructor(private readonly config: ConfigService) {}
|
||||
|
||||
public getStripeAuthLink() {
|
||||
const clientId = this.config.get('stripePayment.clientId');
|
||||
const redirectUrl = this.config.get('stripePayment.redirectTo');
|
||||
|
||||
const authorizationUri = `https://connect.stripe.com/oauth/v2/authorize?response_type=code&client_id=${clientId}&scope=read_write&redirect_uri=${redirectUrl}`;
|
||||
|
||||
return authorizationUri;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Body, Controller, Get, Injectable, Post } from '@nestjs/common';
|
||||
import { StripePaymentApplication } from './StripePaymentApplication';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
|
||||
@Controller('/stripe')
|
||||
@ApiTags('stripe')
|
||||
export class StripeIntegrationController {
|
||||
constructor(private readonly stripePaymentApp: StripePaymentApplication) {}
|
||||
|
||||
/**
|
||||
* Retrieves Stripe OAuth2 connect link.
|
||||
* @returns {Promise<Response|void>}
|
||||
*/
|
||||
@Get('/link')
|
||||
public async getStripeConnectLink() {
|
||||
const authorizationUri = this.stripePaymentApp.getStripeConnectLink();
|
||||
|
||||
return { url: authorizationUri };
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchanges the given Stripe authorization code to Stripe user id and access token.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@Post('/callback')
|
||||
public async exchangeOAuth(@Body('code') code: string) {
|
||||
await this.stripePaymentApp.exchangeStripeOAuthToken(code);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Stripe account.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async createAccount() {
|
||||
const accountId = await this.stripePaymentApp.createStripeAccount();
|
||||
return {
|
||||
accountId,
|
||||
message: 'The Stripe account has been created successfully.',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Stripe account session.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
@Post('/account_link')
|
||||
public async createAccountLink(
|
||||
@Body('stripeAccountId') stripeAccountId: string,
|
||||
) {
|
||||
const clientSecret =
|
||||
await this.stripePaymentApp.createAccountLink(stripeAccountId);
|
||||
|
||||
return { clientSecret };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { CreateStripeAccountLinkService } from './CreateStripeAccountLink';
|
||||
import { CreateStripeAccountService } from './CreateStripeAccountService';
|
||||
import { StripePaymentApplication } from './StripePaymentApplication';
|
||||
import { ExchangeStripeOAuthTokenService } from './ExchangeStripeOauthToken';
|
||||
import { PaymentIntegration } from './models/PaymentIntegration.model';
|
||||
import { SeedStripeAccountsOnOAuthGrantedSubscriber } from './subscribers/SeedStripeAccounts';
|
||||
import { StripeWebhooksSubscriber } from './subscribers/StripeWebhooksSubscriber';
|
||||
import { StripeIntegrationController } from './StripePayment.controller';
|
||||
import { StripePaymentService } from './StripePaymentService';
|
||||
import { InjectSystemModel } from '../System/SystemModels/SystemModels.module';
|
||||
import { GetStripeAuthorizationLinkService } from './GetStripeAuthorizationLink';
|
||||
import { AccountsModule } from '../Accounts/Accounts.module';
|
||||
import { CreatePaymentReceiveStripePayment } from './CreatePaymentReceivedStripePayment';
|
||||
import { SaleInvoicesModule } from '../SaleInvoices/SaleInvoices.module';
|
||||
import { PaymentsReceivedModule } from '../PaymentReceived/PaymentsReceived.module';
|
||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||
|
||||
const models = [InjectSystemModel(PaymentIntegration)];
|
||||
|
||||
@Module({
|
||||
imports: [AccountsModule, SaleInvoicesModule, PaymentsReceivedModule],
|
||||
providers: [
|
||||
...models,
|
||||
StripePaymentService,
|
||||
GetStripeAuthorizationLinkService,
|
||||
CreateStripeAccountLinkService,
|
||||
CreateStripeAccountService,
|
||||
StripePaymentApplication,
|
||||
ExchangeStripeOAuthTokenService,
|
||||
CreatePaymentReceiveStripePayment,
|
||||
SeedStripeAccountsOnOAuthGrantedSubscriber,
|
||||
StripeWebhooksSubscriber,
|
||||
TenancyContext,
|
||||
],
|
||||
exports: [StripePaymentService, GetStripeAuthorizationLinkService],
|
||||
controllers: [StripeIntegrationController],
|
||||
})
|
||||
export class StripePaymentModule {}
|
||||
@@ -0,0 +1,19 @@
|
||||
export interface StripePaymentLinkCreatedEventPayload {
|
||||
paymentLinkId: string;
|
||||
saleInvoiceId: number;
|
||||
stripeIntegrationId: number;
|
||||
}
|
||||
|
||||
export interface StripeCheckoutSessionCompletedEventPayload {
|
||||
event: any;
|
||||
}
|
||||
|
||||
export interface StripeInvoiceCheckoutSessionPOJO {
|
||||
sessionId: string;
|
||||
publishableKey: string;
|
||||
redirectTo: string;
|
||||
}
|
||||
|
||||
export interface StripeWebhookEventPayload {
|
||||
event: any;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { CreateStripeAccountService } from './CreateStripeAccountService';
|
||||
import { CreateStripeAccountLinkService } from './CreateStripeAccountLink';
|
||||
import { CreateStripeAccountDTO } from './types';
|
||||
import { ExchangeStripeOAuthTokenService } from './ExchangeStripeOauthToken';
|
||||
import { GetStripeAuthorizationLinkService } from './GetStripeAuthorizationLink';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class StripePaymentApplication {
|
||||
constructor(
|
||||
private readonly createStripeAccountService: CreateStripeAccountService,
|
||||
private readonly createStripeAccountLinkService: CreateStripeAccountLinkService,
|
||||
private readonly exchangeStripeOAuthTokenService: ExchangeStripeOAuthTokenService,
|
||||
private readonly getStripeConnectLinkService: GetStripeAuthorizationLinkService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Creates a new Stripe account.
|
||||
* @param {number} createStripeAccountDTO
|
||||
*/
|
||||
public createStripeAccount(
|
||||
createStripeAccountDTO: CreateStripeAccountDTO = {},
|
||||
) {
|
||||
return this.createStripeAccountService.createStripeAccount(
|
||||
createStripeAccountDTO,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Stripe account link of the given Stripe account.
|
||||
* @param {string} stripeAccountId
|
||||
* @returns {}
|
||||
*/
|
||||
public createAccountLink(stripeAccountId: string) {
|
||||
return this.createStripeAccountLinkService.createAccountLink(
|
||||
stripeAccountId,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves Stripe OAuth2 connect link.
|
||||
* @returns {string}
|
||||
*/
|
||||
public getStripeConnectLink() {
|
||||
return this.getStripeConnectLinkService.getStripeAuthLink();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchanges the given Stripe authorization code to Stripe user id and access token.
|
||||
* @param {string} authorizationCode
|
||||
* @returns
|
||||
*/
|
||||
public exchangeStripeOAuthToken(authorizationCode: string) {
|
||||
return this.exchangeStripeOAuthTokenService.excahngeStripeOAuthToken(
|
||||
authorizationCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import stripe from 'stripe';
|
||||
|
||||
const origin = 'https://cfdf-102-164-97-88.ngrok-free.app';
|
||||
|
||||
@Injectable({ scope: Scope.DEFAULT })
|
||||
export class StripePaymentService {
|
||||
public stripe: stripe;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {ConfigService} config - ConfigService instance
|
||||
*/
|
||||
constructor(private readonly config: ConfigService) {
|
||||
const secretKey = this.config.get('stripePayment.secretKey');
|
||||
this.stripe = new stripe(secretKey, {
|
||||
apiVersion: '2024-06-20',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Stripe account session.
|
||||
* @param {number} accountId
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
public async createAccountSession(accountId: string): Promise<string> {
|
||||
try {
|
||||
const accountSession = await this.stripe.accountSessions.create({
|
||||
account: accountId,
|
||||
components: {
|
||||
account_onboarding: { enabled: true },
|
||||
},
|
||||
});
|
||||
return accountSession.client_secret;
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
'An error occurred when calling the Stripe API to create an account session',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Stripe account link.
|
||||
* @param {number} accountId - Account id.
|
||||
* @returns {Promise<stripe.Response<stripe.AccountLink>}
|
||||
*/
|
||||
public async createAccountLink(accountId: string) {
|
||||
try {
|
||||
const accountLink = await this.stripe.accountLinks.create({
|
||||
account: accountId,
|
||||
return_url: `${origin}/return/${accountId}`,
|
||||
refresh_url: `${origin}/refresh/${accountId}`,
|
||||
type: 'account_onboarding',
|
||||
});
|
||||
return accountLink;
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
'An error occurred when calling the Stripe API to create an account link:',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* C
|
||||
* @returns {Promise<stripe.Response<stripe.Account>>}
|
||||
*/
|
||||
public async createAccount() {
|
||||
try {
|
||||
const account = await this.stripe.accounts.create({
|
||||
type: 'standard',
|
||||
});
|
||||
return account;
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
'An error occurred when calling the Stripe API to create an account',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { BaseModel } from '@/models/Model';
|
||||
import { Model } from 'objection';
|
||||
|
||||
export class PaymentIntegration extends BaseModel {
|
||||
paymentEnabled!: boolean;
|
||||
payoutEnabled!: boolean;
|
||||
service?: string;
|
||||
name?: string;
|
||||
slug?: string;
|
||||
accountId?: string;
|
||||
options?: Record<string, any>;
|
||||
active?: boolean;
|
||||
|
||||
static get tableName() {
|
||||
return 'payment_integrations';
|
||||
}
|
||||
|
||||
static get idColumn() {
|
||||
return 'id';
|
||||
}
|
||||
|
||||
static get virtualAttributes() {
|
||||
return ['fullEnabled'];
|
||||
}
|
||||
|
||||
static get jsonAttributes() {
|
||||
return ['options'];
|
||||
}
|
||||
|
||||
get fullEnabled() {
|
||||
return this.paymentEnabled && this.payoutEnabled;
|
||||
}
|
||||
|
||||
static get modifiers() {
|
||||
return {
|
||||
/**
|
||||
* Query to filter enabled payment and payout.
|
||||
*/
|
||||
fullEnabled(query) {
|
||||
query.where('paymentEnabled', true).andWhere('payoutEnabled', true);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
static get jsonSchema() {
|
||||
return {
|
||||
type: 'object',
|
||||
required: ['name', 'service'],
|
||||
properties: {
|
||||
id: { type: 'integer' },
|
||||
service: { type: 'string' },
|
||||
paymentEnabled: { type: 'boolean' },
|
||||
payoutEnabled: { type: 'boolean' },
|
||||
accountId: { type: 'string' },
|
||||
options: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
bankAccountId: { type: 'number' },
|
||||
clearingAccountId: { type: 'number' },
|
||||
},
|
||||
},
|
||||
createdAt: { type: 'string', format: 'date-time' },
|
||||
updatedAt: { type: 'string', format: 'date-time' },
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { events } from '@/common/events/events';
|
||||
import { AccountRepository } from '@/modules/Accounts/repositories/Account.repository';
|
||||
import { PaymentIntegration } from '../models/PaymentIntegration.model';
|
||||
import { StripeOAuthCodeGrantedEventPayload } from '../types';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class SeedStripeAccountsOnOAuthGrantedSubscriber {
|
||||
/**
|
||||
* @param {AccountRepository} accountRepository
|
||||
* @param {TenantModelProxy<typeof PaymentIntegration>} paymentIntegrationModel
|
||||
*/
|
||||
constructor(
|
||||
private readonly accountRepository: AccountRepository,
|
||||
|
||||
@Inject(PaymentIntegration.name)
|
||||
private readonly paymentIntegrationModel: TenantModelProxy<
|
||||
typeof PaymentIntegration
|
||||
>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Seeds the default integration settings once oauth authorization code granted.
|
||||
* @param {StripeCheckoutSessionCompletedEventPayload} payload -
|
||||
*/
|
||||
@OnEvent(events.stripeIntegration.onOAuthCodeGranted)
|
||||
async handleSeedStripeAccount({
|
||||
paymentIntegrationId,
|
||||
trx,
|
||||
}: StripeOAuthCodeGrantedEventPayload) {
|
||||
const clearingAccount =
|
||||
await this.accountRepository.findOrCreateStripeClearing({}, trx);
|
||||
|
||||
const bankAccount = await this.accountRepository.findBySlug('bank-account');
|
||||
|
||||
// Patch the Stripe integration default settings.
|
||||
await this.paymentIntegrationModel()
|
||||
.query(trx)
|
||||
.findById(paymentIntegrationId)
|
||||
.patch({
|
||||
options: {
|
||||
// @ts-ignore
|
||||
bankAccountId: bankAccount.id,
|
||||
clearingAccountId: clearingAccount.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { CreatePaymentReceiveStripePayment } from '../CreatePaymentReceivedStripePayment';
|
||||
import {
|
||||
StripeCheckoutSessionCompletedEventPayload,
|
||||
StripeWebhookEventPayload,
|
||||
} 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 { PaymentIntegration } from '../models/PaymentIntegration.model';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { TenantModel } from '@/modules/System/models/TenantModel';
|
||||
|
||||
@Injectable()
|
||||
export class StripeWebhooksSubscriber {
|
||||
constructor(
|
||||
private readonly createPaymentReceiveStripePayment: CreatePaymentReceiveStripePayment,
|
||||
|
||||
@Inject(PaymentIntegration.name)
|
||||
private readonly paymentIntegrationModel: TenantModelProxy<
|
||||
typeof PaymentIntegration
|
||||
>,
|
||||
|
||||
@Inject(TenantModel.name)
|
||||
private readonly tenantModel: typeof TenantModel
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handles the checkout session completed webhook event.
|
||||
* @param {StripeCheckoutSessionCompletedEventPayload} payload -
|
||||
*/
|
||||
@OnEvent(events.stripeWebhooks.onCheckoutSessionCompleted)
|
||||
async handleCheckoutSessionCompleted({
|
||||
event,
|
||||
}: StripeCheckoutSessionCompletedEventPayload) {
|
||||
const { metadata } = event.data.object;
|
||||
const tenantId = parseInt(metadata.tenantId, 10);
|
||||
const saleInvoiceId = parseInt(metadata.saleInvoiceId, 10);
|
||||
|
||||
|
||||
|
||||
// await initalizeTenantServices(tenantId);
|
||||
// await initializeTenantSettings(tenantId);
|
||||
|
||||
// Get the amount from the event
|
||||
const amount = event.data.object.amount_total;
|
||||
|
||||
// Convert from Stripe amount (cents) to normal amount (dollars)
|
||||
const amountInDollars = amount / 100;
|
||||
|
||||
// Creates a new payment received transaction.
|
||||
await this.createPaymentReceiveStripePayment.createPaymentReceived(
|
||||
saleInvoiceId,
|
||||
amountInDollars,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the account updated.
|
||||
* @param {StripeWebhookEventPayload}
|
||||
*/
|
||||
@OnEvent(events.stripeWebhooks.onAccountUpdated)
|
||||
async handleAccountUpdated({ event }: StripeWebhookEventPayload) {
|
||||
const { metadata } = event.data.object;
|
||||
const account = event.data.object;
|
||||
const tenantId = parseInt(metadata.tenantId, 10);
|
||||
|
||||
if (!metadata?.paymentIntegrationId || !metadata.tenantId) return;
|
||||
|
||||
// Find the tenant or throw not found error.
|
||||
await this.tenantModel.query().findById(tenantId).throwIfNotFound();
|
||||
|
||||
// Check if the account capabilities are active
|
||||
if (account.capabilities.card_payments === 'active') {
|
||||
// Marks the payment method integration as active.
|
||||
await this.paymentIntegrationModel()
|
||||
.query()
|
||||
.findById(metadata?.paymentIntegrationId)
|
||||
.patch({
|
||||
active: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
9
packages/server/src/modules/StripePayment/types.ts
Normal file
9
packages/server/src/modules/StripePayment/types.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export interface CreateStripeAccountDTO {
|
||||
name?: string;
|
||||
}
|
||||
export interface StripeOAuthCodeGrantedEventPayload {
|
||||
paymentIntegrationId: number;
|
||||
trx?: Knex.Transaction;
|
||||
}
|
||||
Reference in New Issue
Block a user