refactor: payment services to nestjs

This commit is contained in:
Ahmed Bouhuolia
2025-03-28 23:54:40 +02:00
parent f20f07a42f
commit 173610d0fa
13 changed files with 137 additions and 82 deletions

View File

@@ -15,9 +15,12 @@ import {
import { Request, Response, NextFunction } from 'express';
import { ApiTags } from '@nestjs/swagger';
import { PaymentServicesApplication } from './PaymentServicesApplication';
import { PublicRoute } from '../Auth/Jwt.guard';
import { EditPaymentMethodDTO } from './types';
@ApiTags('PaymentServices')
@Controller('payment-services')
@PublicRoute()
export class PaymentServicesController {
constructor(
private readonly paymentServicesApp: PaymentServicesApplication,
@@ -53,13 +56,10 @@ export class PaymentServicesController {
}
@Post('/:paymentMethodId')
@UsePipes(new ValidationPipe({ whitelist: true }))
async updatePaymentMethod(
@Param('paymentMethodId') paymentMethodId: number,
@Body() updatePaymentMethodDTO: any,
@Req() req: Request,
@Body() updatePaymentMethodDTO: EditPaymentMethodDTO,
@Res() res: Response,
@Next() next: NextFunction,
) {
await this.paymentServicesApp.editPaymentMethod(
paymentMethodId,

View File

@@ -6,8 +6,19 @@ import { GetPaymentServicesSpecificInvoice } from './queries/GetPaymentServicesS
import { GetPaymentMethodsStateService } from './queries/GetPaymentMethodsState';
import { PaymentServicesApplication } from './PaymentServicesApplication';
import { PaymentServicesController } from './PaymentServices.controller';
import { RegisterTenancyModel } from '../Tenancy/TenancyModels/Tenancy.module';
import { PaymentIntegration } from './models/PaymentIntegration.model';
import { TransactionPaymentServiceEntry } from './models/TransactionPaymentServiceEntry.model';
import { StripePaymentModule } from '../StripePayment/StripePayment.module';
const models = [
RegisterTenancyModel(PaymentIntegration),
RegisterTenancyModel(TransactionPaymentServiceEntry),
];
@Module({
imports: [...models, StripePaymentModule],
exports: [...models],
providers: [
DeletePaymentMethodService,
EditPaymentMethodService,

View File

@@ -1,12 +1,12 @@
import { Service, Inject } from 'typedi';
import { GetPaymentServicesSpecificInvoice } from './queries/GetPaymentServicesSpecificInvoice';
import { DeletePaymentMethodService } from './commands/DeletePaymentMethodService';
import { EditPaymentMethodService } from './commands/EditPaymentMethodService';
import { EditPaymentMethodDTO, GetPaymentMethodsPOJO } from './types';
import { GetPaymentMethodsStateService } from './queries/GetPaymentMethodsState';
import { GetPaymentMethodService } from './queries/GetPaymentService';
import { Injectable } from '@nestjs/common';
@Service()
@Injectable()
export class PaymentServicesApplication {
constructor(
private readonly getPaymentServicesSpecificInvoice: GetPaymentServicesSpecificInvoice,
@@ -22,9 +22,7 @@ export class PaymentServicesApplication {
* @returns {Promise<any>} The payment services for the specified invoice.
*/
public async getPaymentServicesForInvoice(): Promise<any> {
return this.getPaymentServicesSpecificInvoice.getPaymentServicesInvoice(
tenantId,
);
return this.getPaymentServicesSpecificInvoice.getPaymentServicesInvoice();
}
/**
@@ -37,7 +35,7 @@ export class PaymentServicesApplication {
/**
* Deletes the given payment method.
* @param {number} paymentIntegrationId
* @param {number} paymentIntegrationId - Payment integration id.
* @returns {Promise<void>}
*/
public async deletePaymentMethod(
@@ -50,8 +48,8 @@ export class PaymentServicesApplication {
/**
* Edits the given payment method.
* @param {number} paymentIntegrationId
* @param {EditPaymentMethodDTO} editPaymentMethodDTO
* @param {number} paymentIntegrationId - Payment integration id.
* @param {EditPaymentMethodDTO} editPaymentMethodDTO - Edit payment method DTO.
* @returns {Promise<void>}
*/
public async editPaymentMethod(

View File

@@ -1,6 +1,5 @@
import { Knex } from 'knex';
import { EditPaymentMethodDTO } from '../types';
import { TransactionPaymentServiceEntry } from '../models/TransactionPaymentServiceEntry.model';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { PaymentIntegration } from '../models/PaymentIntegration.model';
import { EventEmitter2 } from '@nestjs/event-emitter';
@@ -18,11 +17,6 @@ export class EditPaymentMethodService {
private readonly paymentIntegrationModel: TenantModelProxy<
typeof PaymentIntegration
>,
@Inject(TransactionPaymentServiceEntry.name)
private readonly transactionPaymentServiceEntryModel: TenantModelProxy<
typeof TransactionPaymentServiceEntry
>,
) {}
/**

View File

@@ -1,10 +1,12 @@
import { BaseModel } from "@/models/Model";
import { BaseModel } from '@/models/Model';
export class PaymentIntegration extends BaseModel {
readonly name!: string;
readonly service!: string;
readonly paymentEnabled!: boolean;
readonly payoutEnabled!: boolean;
readonly accountId!: string;
readonly options!: Record<string, any>;
static get tableName() {
return 'payment_integrations';
@@ -27,7 +29,7 @@ export class PaymentIntegration extends BaseModel {
}
/**
*
*
*/
static get modifiers() {
return {

View File

@@ -1,7 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { GetPaymentMethodsPOJO } from '../types';
import { isStripePaymentConfigured } from '../utils';
import { GetStripeAuthorizationLinkService } from '../../StripePayment/GetStripeAuthorizationLink';
import { PaymentIntegration } from '../models/PaymentIntegration.model';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@@ -23,8 +22,7 @@ export class GetPaymentMethodsStateService {
* @param {number} tenantId
* @returns {Promise<GetPaymentMethodsPOJO>}
*/
public async getPaymentMethodsState(
): Promise<GetPaymentMethodsPOJO> {
public async getPaymentMethodsState(): Promise<GetPaymentMethodsPOJO> {
const stripePayment = await this.paymentIntegrationModel()
.query()
.orderBy('createdAt', 'ASC')
@@ -43,7 +41,7 @@ export class GetPaymentMethodsStateService {
);
const stripeCurrencies = ['USD', 'EUR'];
const stripeRedirectUrl = 'https://your-stripe-redirect-url.com';
const isStripeServerConfigured = isStripePaymentConfigured();
const isStripeServerConfigured = this.isStripePaymentConfigured();
const stripeAuthLink =
this.getStripeAuthorizationLinkService.getStripeAuthLink();
@@ -64,4 +62,16 @@ export class GetPaymentMethodsStateService {
};
return paymentMethodPOJO;
}
/**
* Determines if Stripe payment is configured.
* @returns {boolean}
*/
private isStripePaymentConfigured() {
return (
this.configService.get('stripePayment.secretKey') &&
this.configService.get('stripePayment.publishableKey') &&
this.configService.get('stripePayment.webhooksSecret')
);
}
}

View File

@@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
import { GetPaymentMethodsPOJO } from '../types';
import { PaymentIntegration } from '../models/PaymentIntegration.model';
import { ModelObject } from 'objection';
@Injectable()
export class GetPaymentMethodService {
@@ -18,7 +19,7 @@ export class GetPaymentMethodService {
*/
public async getPaymentMethod(
paymentServiceId: number,
): Promise<GetPaymentMethodsPOJO> {
): Promise<ModelObject<PaymentIntegration>> {
const stripePayment = await this.paymentIntegrationModel()
.query()
.findById(paymentServiceId)

View File

@@ -1,33 +1,34 @@
import { Inject, Service } from 'typedi';
import HasTenancyService from '../Tenancy/TenancyService';
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
import { Inject, Injectable } from '@nestjs/common';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { GetPaymentServicesSpecificInvoiceTransformer } from './GetPaymentServicesSpecificInvoiceTransformer';
import { PaymentIntegration } from '../models/PaymentIntegration.model';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Service()
@Injectable()
export class GetPaymentServicesSpecificInvoice {
@Inject()
private tenancy: HasTenancyService;
constructor(
private readonly transform: TransformerInjectable,
@Inject()
private transform: TransformerInjectable;
@Inject(PaymentIntegration.name)
private readonly paymentIntegrationModel: TenantModelProxy<
typeof PaymentIntegration
>,
) {}
/**
* Retrieves the payment services of the given invoice.
* @param {number} tenantId
* @param {number} invoiceId
* @returns
*/
async getPaymentServicesInvoice(tenantId: number) {
const { PaymentIntegration } = this.tenancy.models(tenantId);
const paymentGateways = await PaymentIntegration.query()
async getPaymentServicesInvoice() {
const paymentGateways = await this.paymentIntegrationModel()
.query()
.modify('fullEnabled')
.orderBy('name', 'ASC');
return this.transform.transform(
tenantId,
paymentGateways,
new GetPaymentServicesSpecificInvoiceTransformer()
new GetPaymentServicesSpecificInvoiceTransformer(),
);
}
}

View File

@@ -1,16 +1,64 @@
export interface EditPaymentMethodDTO {
name?: string;
options?: {
bankAccountId?: number; // bank account.
clearningAccountId?: number; // current liability.
import { ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import {
IsBoolean,
IsNumber,
IsOptional,
IsString,
ValidateNested,
} from 'class-validator';
showVisa?: boolean;
showMasterCard?: boolean;
showDiscover?: boolean;
showAmer?: boolean;
showJcb?: boolean;
showDiners?: boolean;
};
class EditPaymentMethodOptionsDto {
@IsOptional()
@IsNumber()
bankAccountId?: number;
@IsOptional()
@IsNumber()
clearningAccountId?: number;
@IsOptional()
@IsBoolean()
showVisa?: boolean;
@IsOptional()
@IsBoolean()
showMasterCard?: boolean;
@IsOptional()
@IsBoolean()
showDiscover?: boolean;
@IsOptional()
@IsBoolean()
showAmer?: boolean;
@IsOptional()
@IsBoolean()
showJcb?: boolean;
@IsOptional()
@IsBoolean()
showDiners?: boolean;
}
export class EditPaymentMethodDTO {
@IsOptional()
@ValidateNested()
@Type(() => EditPaymentMethodOptionsDto)
@ApiPropertyOptional({
type: () => EditPaymentMethodOptionsDto,
description: 'Edit payment method options',
})
options?: EditPaymentMethodOptionsDto;
@IsOptional()
@IsString()
@ApiPropertyOptional({
type: String,
description: 'Payment method name',
})
name?: string;
}
export interface GetPaymentMethodsPOJO {
@@ -22,7 +70,7 @@ export interface GetPaymentMethodsPOJO {
isStripeEnabled: boolean;
isStripeServerConfigured: boolean;
stripeAccountId: string | null;
stripePaymentMethodId: number | null;
stripePublishableKey: string | null;

View File

@@ -1,9 +0,0 @@
import config from '@/config';
export const isStripePaymentConfigured = () => {
return (
config.stripePayment.secretKey &&
config.stripePayment.publishableKey &&
config.stripePayment.webhooksSecret
);
};

View File

@@ -33,7 +33,7 @@ const models = [InjectSystemModel(PaymentIntegration)];
StripeWebhooksSubscriber,
TenancyContext,
],
exports: [StripePaymentService],
exports: [StripePaymentService, GetStripeAuthorizationLinkService],
controllers: [StripeIntegrationController],
})
export class StripePaymentModule {}