Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Clean up payment links endpoints #674

Merged
merged 1 commit into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Inject, Service } from 'typedi';
import { Router, Request, Response, NextFunction } from 'express';
import { body, param } from 'express-validator';
import { param } from 'express-validator';
import BaseController from '@/api/controllers/BaseController';
import { GetInvoicePaymentLinkMetadata } from '@/services/Sales/Invoices/GetInvoicePaymentLinkMetadata';
import { PaymentLinksApplication } from '@/services/PaymentLinks/PaymentLinksApplication';

@Service()
export class PublicSharableLinkController extends BaseController {
@Inject()
private getSharableLinkMetaService: GetInvoicePaymentLinkMetadata;
private paymentLinkApp: PaymentLinksApplication;

/**
* Router constructor.
Expand All @@ -16,12 +16,18 @@ export class PublicSharableLinkController extends BaseController {
const router = Router();

router.get(
'/sharable-links/meta/invoice/:linkId',
[param('linkId').exists()],
'/:paymentLinkId/invoice',
[param('paymentLinkId').exists()],
this.validationResult,
this.getPaymentLinkPublicMeta.bind(this),
this.validationResult
);
router.post(
'/:paymentLinkId/stripe_checkout_session',
[param('paymentLinkId').exists()],
this.validationResult,
this.createInvoicePaymentLinkCheckoutSession.bind(this)
);
return router;
}

Expand All @@ -33,19 +39,45 @@ export class PublicSharableLinkController extends BaseController {
* @returns
*/
public async getPaymentLinkPublicMeta(
req: Request,
req: Request<{ paymentLinkId: string }>,
res: Response,
next: NextFunction
) {
const { linkId } = req.params;
const { paymentLinkId } = req.params;

try {
const data =
await this.getSharableLinkMetaService.getInvoicePaymentLinkMeta(linkId);
const data = await this.paymentLinkApp.getInvoicePaymentLink(
paymentLinkId
);

return res.status(200).send({ data });
} catch (error) {
next(error);
}
}

/**
* Creates a Stripe checkout session for the given payment link id.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<Response|void>}
*/
public async createInvoicePaymentLinkCheckoutSession(
req: Request<{ paymentLinkId: string }>,
res: Response,
next: NextFunction
) {
const { paymentLinkId } = req.params;

try {
const session =
await this.paymentLinkApp.createInvoicePaymentCheckoutSession(
paymentLinkId
);
return res.status(200).send(session);
} catch (error) {
next(error);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ export class StripeIntegrationController extends BaseController {
this.validationResult,
asyncMiddleware(this.createAccountLink.bind(this))
);
router.post(
'/:linkId/create_checkout_session',
this.createCheckoutSession.bind(this)
);
return router;
}

Expand Down Expand Up @@ -75,33 +71,6 @@ export class StripeIntegrationController extends BaseController {
}
}

/**
* Creates a Stripe checkout session for the given payment link id.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<Response|void>}
*/
public async createCheckoutSession(
req: Request<{ linkId: number }>,
res: Response,
next: NextFunction
) {
const { linkId } = req.params;
const { tenantId } = req;

try {
const session =
await this.stripePaymentApp.createSaleInvoiceCheckoutSession(
tenantId,
linkId
);
return res.status(200).send(session);
} catch (error) {
next(error);
}
}

/**
* Creates a new Stripe account.
* @param {Request} req - The Express request object.
Expand Down
5 changes: 4 additions & 1 deletion packages/server/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ export default () => {
app.use('/account', Container.get(Account).router());
app.use('/webhooks', Container.get(Webhooks).router());
app.use('/demo', Container.get(OneClickDemoController).router());
app.use(Container.get(PublicSharableLinkController).router());
app.use(
'/payment-links',
Container.get(PublicSharableLinkController).router()
);

// - Dashboard routes.
// ---------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ export const injectI18nUtils = (req) => {
export const initalizeTenantServices = async (tenantId: number) => {
const tenant = await Tenant.query()
.findById(tenantId)
.withGraphFetched('metadata');
.withGraphFetched('metadata')
.throwIfNotFound();

const tenantServices = Container.get(TenancyService);
const tenantsManager = Container.get(TenantsManagerService);
Expand Down
4 changes: 2 additions & 2 deletions packages/server/src/database/seeds/data/accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ export const PrepardExpenses = {
export const StripeClearingAccount = {
name: 'Stripe Clearing',
slug: 'stripe-clearing',
account_type: 'other-current-liability',
account_type: 'other-current-asset',
parent_account_id: null,
code: '50006',
code: '100020',
active: true,
index: 1,
predefined: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import config from '@/config';
import { StripePaymentService } from './StripePaymentService';
import { Inject, Service } from 'typedi';
import { StripePaymentService } from '../StripePayment/StripePaymentService';
import HasTenancyService from '../Tenancy/TenancyService';
import { ISaleInvoice } from '@/interfaces';
import { Inject, Service } from 'typedi';
import { StripeInvoiceCheckoutSessionPOJO } from '@/interfaces/StripePayment';
import { PaymentLink } from '@/system/models';
import { initializeTenantSettings } from '@/api/middleware/SettingsMiddleware';
import config from '@/config';

const origin = 'http://localhost';

@Service()
export class CreateInvoiceCheckoutSession {
@Inject()
private stripePaymentService: StripePaymentService;
private tenancy: HasTenancyService;

@Inject()
private tenancy: HasTenancyService;
private stripePaymentService: StripePaymentService;

/**
* Creates a new Stripe checkout session from the given sale invoice.
Expand All @@ -23,17 +24,18 @@ export class CreateInvoiceCheckoutSession {
* @returns {Promise<StripeInvoiceCheckoutSessionPOJO>}
*/
async createInvoiceCheckoutSession(
tenantId: number,
publicPaymentLinkId: number
publicPaymentLinkId: string
): Promise<StripeInvoiceCheckoutSessionPOJO> {
const { SaleInvoice } = this.tenancy.models(tenantId);

// Retrieves the payment link from the given id.
const paymentLink = await PaymentLink.query()
.findOne('linkId', publicPaymentLinkId)
.where('resourceType', 'SaleInvoice')
.throwIfNotFound();

const tenantId = paymentLink.tenantId;
await initializeTenantSettings(tenantId);

const { SaleInvoice } = this.tenancy.models(tenantId);
// Retrieves the invoice from associated payment link.
const invoice = await SaleInvoice.query()
.findById(paymentLink.resourceId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ServiceError } from '@/exceptions';
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { PaymentLink } from '@/system/models';
import { GetInvoicePaymentLinkMetaTransformer } from './GetInvoicePaymentLinkTransformer';
import { GetInvoicePaymentLinkMetaTransformer } from '../Sales/Invoices/GetInvoicePaymentLinkTransformer';
import { initalizeTenantServices } from '@/api/middleware/TenantDependencyInjection';

@Service()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Inject, Service } from 'typedi';
import { GetInvoicePaymentLinkMetadata } from './GetInvoicePaymentLinkMetadata';
import { CreateInvoiceCheckoutSession } from './CreateInvoiceCheckoutSession';
import { StripeInvoiceCheckoutSessionPOJO } from '@/interfaces/StripePayment';

@Service()
export class PaymentLinksApplication {
@Inject()
private getInvoicePaymentLinkMetadataService: GetInvoicePaymentLinkMetadata;

@Inject()
private createInvoiceCheckoutSessionService: CreateInvoiceCheckoutSession;

/**
* Retrieves the invoice payment link.
* @param {string} paymentLinkId
* @returns {}
*/
public getInvoicePaymentLink(paymentLinkId: string) {
return this.getInvoicePaymentLinkMetadataService.getInvoicePaymentLinkMeta(
paymentLinkId
);
}

/**
* Create the invoice payment checkout session from the given payment link id.
* @param {string} paymentLinkId - Payment link id.
* @returns {Promise<StripeInvoiceCheckoutSessionPOJO>}
*/
public createInvoicePaymentCheckoutSession(
paymentLinkId: string
): Promise<StripeInvoiceCheckoutSessionPOJO> {
return this.createInvoiceCheckoutSessionService.createInvoiceCheckoutSession(
paymentLinkId
);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Inject, Service } from 'typedi';
import { Knex } from 'knex';
import { GetSaleInvoice } from '../Sales/Invoices/GetSaleInvoice';
import { CreatePaymentReceived } from '../Sales/PaymentReceived/CreatePaymentReceived';
import HasTenancyService from '../Tenancy/TenancyService';
import UnitOfWork from '../UnitOfWork';

@Service()
export class CreatePaymentReceiveStripePayment {
Expand All @@ -10,28 +13,52 @@ export class CreatePaymentReceiveStripePayment {
@Inject()
private createPaymentReceivedService: CreatePaymentReceived;

@Inject()
private tenancy: HasTenancyService;

@Inject()
private uow: UnitOfWork;

/**
*
* @param {number} tenantId
* @param {number} saleInvoiceId
* @param {number} paidAmount
* @param {number} tenantId
* @param {number} saleInvoiceId
* @param {number} paidAmount
*/
async createPaymentReceived(
tenantId: number,
saleInvoiceId: number,
paidAmount: number
) {
const invoice = await this.getSaleInvoiceService.getSaleInvoice(
tenantId,
saleInvoiceId
);
await this.createPaymentReceivedService.createPaymentReceived(tenantId, {
customerId: invoice.customerId,
paymentDate: new Date(),
amount: paidAmount,
depositAccountId: 1002,
statement: '',
entries: [{ invoiceId: saleInvoiceId, paymentAmount: paidAmount }],
const { accountRepository } = this.tenancy.repositories(tenantId);

// Create a payment received transaction under UOW envirement.
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Finds or creates a new stripe payment clearing account (current asset).
const stripeClearingAccount =
accountRepository.findOrCreateStripeClearing({}, trx);

// Retrieves the given invoice to create payment transaction associated to it.
const invoice = await this.getSaleInvoiceService.getSaleInvoice(
tenantId,
saleInvoiceId
);
// Create a payment received transaction associated to the given invoice.
await this.createPaymentReceivedService.createPaymentReceived(
tenantId,
{
customerId: invoice.customerId,
paymentDate: new Date(),
amount: paidAmount,
exchangeRate: 1,
referenceNo: '',
statement: '',
depositAccountId: stripeClearingAccount.id,
entries: [{ invoiceId: saleInvoiceId, paymentAmount: paidAmount }],
},
{},
trx
);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { Inject } from 'typedi';
import { CreateInvoiceCheckoutSession } from './CreateInvoiceCheckoutSession';
import { StripeInvoiceCheckoutSessionPOJO } from '@/interfaces/StripePayment';
import { CreateStripeAccountService } from './CreateStripeAccountService';
import { CreateStripeAccountLinkService } from './CreateStripeAccountLink';
import { CreateStripeAccountDTO } from './types';
Expand All @@ -14,9 +12,6 @@ export class StripePaymentApplication {
@Inject()
private createStripeAccountLinkService: CreateStripeAccountLinkService;

@Inject()
private createInvoiceCheckoutSessionService: CreateInvoiceCheckoutSession;

@Inject()
private exchangeStripeOAuthTokenService: ExchangeStripeOAuthTokenService;

Expand Down Expand Up @@ -51,22 +46,6 @@ export class StripePaymentApplication {
);
}

/**
* Creates the Stripe checkout session from the given sale invoice.
* @param {number} tenantId
* @param {string} paymentLinkId
* @returns {Promise<StripeInvoiceCheckoutSessionPOJO>}
*/
public createSaleInvoiceCheckoutSession(
tenantId: number,
paymentLinkId: number
): Promise<StripeInvoiceCheckoutSessionPOJO> {
return this.createInvoiceCheckoutSessionService.createInvoiceCheckoutSession(
tenantId,
paymentLinkId
);
}

/**
* Retrieves Stripe OAuth2 connect link.
* @returns {string}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '@/interfaces/StripePayment';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { Tenant } from '@/system/models';
import { initalizeTenantServices } from '@/api/middleware/TenantDependencyInjection';

@Service()
export class StripeWebhooksSubscriber {
Expand Down Expand Up @@ -41,6 +42,8 @@ export class StripeWebhooksSubscriber {
const tenantId = parseInt(metadata.tenantId, 10);
const saleInvoiceId = parseInt(metadata.saleInvoiceId, 10);

await initalizeTenantServices(tenantId);

// Get the amount from the event
const amount = event.data.object.amount_total;

Expand Down
Loading
Loading