feat: Stripe payment integration

This commit is contained in:
Ahmed Bouhuolia
2024-09-21 16:50:22 +02:00
parent 8de8695b25
commit 7756b5b304
24 changed files with 691 additions and 102 deletions

View File

@@ -0,0 +1,54 @@
import { Inject, Service } from 'typedi';
import { Knex } from 'knex';
import HasTenancyService from '../Tenancy/TenancyService';
import UnitOfWork from '../UnitOfWork';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import events from '@/subscribers/events';
@Service()
export class DeletePaymentMethodService {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private uow: UnitOfWork;
@Inject()
private eventPublisher: EventPublisher;
/**
* Deletes the given payment integration.
* @param {number} tenantId
* @param {number} paymentIntegrationId
* @returns {Promise<void>}
*/
public async deletePaymentMethod(
tenantId: number,
paymentIntegrationId: number
): Promise<void> {
const { PaymentIntegration, TransactionPaymentServiceEntry } =
this.tenancy.models(tenantId);
const paymentIntegration = await PaymentIntegration.query()
.findById(paymentIntegrationId)
.throwIfNotFound();
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Delete payment methods links.
await TransactionPaymentServiceEntry.query(trx)
.where('paymentIntegrationId', paymentIntegrationId)
.delete();
// Delete the payment integration.
await PaymentIntegration.query(trx)
.findById(paymentIntegrationId)
.delete();
// Triggers `onPaymentMethodDeleted` event.
await this.eventPublisher.emitAsync(events.paymentMethod.onDeleted, {
tenantId,
paymentIntegrationId,
});
});
}
}

View File

@@ -0,0 +1,60 @@
import { Knex } from 'knex';
import { Inject, Service } from 'typedi';
import HasTenancyService from '../Tenancy/TenancyService';
import UnitOfWork from '../UnitOfWork';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import { EditPaymentMethodDTO } from './types';
import events from '@/subscribers/events';
@Service()
export class EditPaymentMethodService {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private uow: UnitOfWork;
@Inject()
private eventPublisher: EventPublisher;
/**
* Edits the given payment method.
* @param {number} tenantId
* @param {number} paymentIntegrationId
* @param {EditPaymentMethodDTO} editPaymentMethodDTO
* @returns {Promise<void>}
*/
async editPaymentMethod(
tenantId: number,
paymentIntegrationId: number,
editPaymentMethodDTO: EditPaymentMethodDTO
): Promise<void> {
const { PaymentIntegration } = this.tenancy.models(tenantId);
const paymentMethod = await PaymentIntegration.query()
.findById(paymentIntegrationId)
.throwIfNotFound();
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Triggers `onPaymentMethodEditing` event.
await this.eventPublisher.emitAsync(events.paymentMethod.onEditing, {
tenantId,
paymentIntegrationId,
editPaymentMethodDTO,
trx,
});
await PaymentIntegration.query(trx)
.findById(paymentIntegrationId)
.patch({
...editPaymentMethodDTO,
});
// Triggers `onPaymentMethodEdited` event.
await this.eventPublisher.emitAsync(events.paymentMethod.onEdited, {
tenantId,
paymentIntegrationId,
editPaymentMethodDTO,
trx,
});
});
}
}

View File

@@ -0,0 +1,46 @@
import { Inject, Service } from 'typedi';
import HasTenancyService from '../Tenancy/TenancyService';
import { GetPaymentMethodsPOJO } from './types';
import config from '@/config';
@Service()
export class GetPaymentMethodsStateService {
@Inject()
private tenancy: HasTenancyService;
/**
* Retrieves the payment state provising state.
* @param {number} tenantId
* @returns {Promise<GetPaymentMethodsPOJO>}
*/
public async getPaymentMethodsState(
tenantId: number
): Promise<GetPaymentMethodsPOJO> {
const { PaymentIntegration } = this.tenancy.models(tenantId);
const stripePayment = await PaymentIntegration.query()
.orderBy('createdAt', 'ASC')
.findOne({
service: 'Stripe',
});
const isStripeAccountCreated = !!stripePayment;
const isStripePaymentActive = !!(stripePayment?.active || null);
const stripeAccountId = stripePayment?.accountId || null;
const stripePublishableKey = config.stripePayment.publishableKey;
const stripeCurrencies = ['USD', 'EUR'];
const stripeRedirectUrl = 'https://your-stripe-redirect-url.com';
const paymentMethodPOJO: GetPaymentMethodsPOJO = {
stripe: {
isStripeAccountCreated,
isStripePaymentActive,
stripeAccountId,
stripePublishableKey,
stripeCurrencies,
stripeRedirectUrl,
},
};
return paymentMethodPOJO;
}
}

View File

@@ -1,20 +1,79 @@
import { Service, Inject } from 'typedi';
import { GetPaymentServicesSpecificInvoice } from './GetPaymentServicesSpecificInvoice';
import { DeletePaymentMethodService } from './DeletePaymentMethodService';
import { EditPaymentMethodService } from './EditPaymentMethodService';
import { EditPaymentMethodDTO, GetPaymentMethodsPOJO } from './types';
import { GetPaymentMethodsStateService } from './GetPaymentMethodsState';
@Service()
export class PaymentServicesApplication {
@Inject()
private getPaymentServicesSpecificInvoice: GetPaymentServicesSpecificInvoice;
@Inject()
private deletePaymentMethodService: DeletePaymentMethodService;
@Inject()
private editPaymentMethodService: EditPaymentMethodService;
@Inject()
private getPaymentMethodsStateService: GetPaymentMethodsStateService;
/**
* Retrieves the payment services for a specific invoice.
* @param {number} tenantId - The ID of the tenant.
* @param {number} invoiceId - The ID of the invoice.
* @returns {Promise<any>} The payment services for the specified invoice.
*/
async getPaymentServicesForInvoice(tenantId: number): Promise<any> {
public async getPaymentServicesForInvoice(tenantId: number): Promise<any> {
return this.getPaymentServicesSpecificInvoice.getPaymentServicesInvoice(
tenantId
);
}
/**
* Deletes the given payment method.
* @param {number} tenantId
* @param {number} paymentIntegrationId
* @returns {Promise<void>}
*/
public async deletePaymentMethod(
tenantId: number,
paymentIntegrationId: number
): Promise<void> {
return this.deletePaymentMethodService.deletePaymentMethod(
tenantId,
paymentIntegrationId
);
}
/**
* Edits the given payment method.
* @param {number} tenantId
* @param {number} paymentIntegrationId
* @param {EditPaymentMethodDTO} editPaymentMethodDTO
* @returns {Promise<void>}
*/
public async editPaymentMethod(
tenantId: number,
paymentIntegrationId: number,
editPaymentMethodDTO: EditPaymentMethodDTO
): Promise<void> {
return this.editPaymentMethodService.editPaymentMethod(
tenantId,
paymentIntegrationId,
editPaymentMethodDTO
);
}
/**
* Retrieves the payment state providing state.
* @param {number} tenantId
* @returns {Promise<GetPaymentMethodsPOJO>}
*/
public async getPaymentMethodsState(
tenantId: number
): Promise<GetPaymentMethodsPOJO> {
return this.getPaymentMethodsStateService.getPaymentMethodsState(tenantId);
}
}

View File

@@ -0,0 +1,25 @@
export interface EditPaymentMethodDTO {
name?: string;
options?: {
bankAccountId?: number; // bank account.
clearningAccountId?: number; // current liability.
showVisa?: boolean;
showMasterCard?: boolean;
showDiscover?: boolean;
showAmer?: boolean;
showJcb?: boolean;
showDiners?: boolean;
};
}
export interface GetPaymentMethodsPOJO {
stripe: {
isStripeAccountCreated: boolean;
isStripePaymentActive: boolean;
stripeAccountId: string | null;
stripePublishableKey: string | null;
stripeCurrencies: Array<string>;
stripeRedirectUrl: string | null;
};
}