mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
feat: Stripe payment integration
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import { Request, Response, Router, NextFunction } from 'express';
|
import { Request, Response, Router, NextFunction } from 'express';
|
||||||
|
import { body, param } from 'express-validator';
|
||||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||||
import BaseController from '@/api/controllers/BaseController';
|
import BaseController from '@/api/controllers/BaseController';
|
||||||
import { PaymentServicesApplication } from '@/services/PaymentServices/PaymentServicesApplication';
|
import { PaymentServicesApplication } from '@/services/PaymentServices/PaymentServicesApplication';
|
||||||
@@ -19,6 +20,25 @@ export class PaymentServicesController extends BaseController {
|
|||||||
'/',
|
'/',
|
||||||
asyncMiddleware(this.getPaymentServicesSpecificInvoice.bind(this))
|
asyncMiddleware(this.getPaymentServicesSpecificInvoice.bind(this))
|
||||||
);
|
);
|
||||||
|
router.get('/state', this.getPaymentMethodsState.bind(this));
|
||||||
|
router.post(
|
||||||
|
'/:paymentMethodId',
|
||||||
|
[
|
||||||
|
param('paymentMethodId').exists(),
|
||||||
|
body('name').optional().isString(),
|
||||||
|
body('options.bankAccountId').optional().isNumeric(),
|
||||||
|
body('options.clearningAccountId').optional().isNumeric(),
|
||||||
|
body('options.showVisa').optional().isBoolean(),
|
||||||
|
body('options.showMasterCard').optional().isBoolean(),
|
||||||
|
body('options.showDiscover').optional().isBoolean(),
|
||||||
|
body('options.showAmer').optional().isBoolean(),
|
||||||
|
body('options.showJcb').optional().isBoolean(),
|
||||||
|
body('options.showDiners').optional().isBoolean(),
|
||||||
|
],
|
||||||
|
this.validationResult,
|
||||||
|
asyncMiddleware(this.updatePaymentMethod.bind(this))
|
||||||
|
);
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +46,7 @@ export class PaymentServicesController extends BaseController {
|
|||||||
* Retrieve accounts types list.
|
* Retrieve accounts types list.
|
||||||
* @param {Request} req - Request.
|
* @param {Request} req - Request.
|
||||||
* @param {Response} res - Response.
|
* @param {Response} res - Response.
|
||||||
* @return {Response}
|
* @return {Promise<Response | void>}
|
||||||
*/
|
*/
|
||||||
private async getPaymentServicesSpecificInvoice(
|
private async getPaymentServicesSpecificInvoice(
|
||||||
req: Request<{ invoiceId: number }>,
|
req: Request<{ invoiceId: number }>,
|
||||||
@@ -44,4 +64,58 @@ export class PaymentServicesController extends BaseController {
|
|||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edits the given payment method settings.
|
||||||
|
* @param {Request} req - Request.
|
||||||
|
* @param {Response} res - Response.
|
||||||
|
* @return {Promise<Response | void>}
|
||||||
|
*/
|
||||||
|
private async updatePaymentMethod(
|
||||||
|
req: Request<{ paymentMethodId: number }>,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
const { paymentMethodId } = req.params;
|
||||||
|
const updatePaymentMethodDTO = this.matchedBodyData(req);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.paymentServicesApp.editPaymentMethod(
|
||||||
|
tenantId,
|
||||||
|
paymentMethodId,
|
||||||
|
updatePaymentMethodDTO
|
||||||
|
);
|
||||||
|
return res.status(200).send({
|
||||||
|
id: paymentMethodId,
|
||||||
|
message: 'The given payment method has been updated.',
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the payment state providing state.
|
||||||
|
* @param {Request} req - Request.
|
||||||
|
* @param {Response} res - Response.
|
||||||
|
* @param {NextFunction} next - Next function.
|
||||||
|
* @return {Promise<Response | void>}
|
||||||
|
*/
|
||||||
|
private async getPaymentMethodsState(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
) {
|
||||||
|
const { tenantId } = req;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const paymentMethodsState =
|
||||||
|
await this.paymentServicesApp.getPaymentMethodsState(tenantId);
|
||||||
|
|
||||||
|
return res.status(200).send({ data: paymentMethodsState });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,29 @@
|
|||||||
import { NextFunction, Request, Response, Router } from 'express';
|
import { NextFunction, Request, Response, Router } from 'express';
|
||||||
|
import { body } from 'express-validator';
|
||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||||
import { StripePaymentApplication } from '@/services/StripePayment/StripePaymentApplication';
|
import { StripePaymentApplication } from '@/services/StripePayment/StripePaymentApplication';
|
||||||
|
import BaseController from '../BaseController';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class StripeIntegrationController {
|
export class StripeIntegrationController extends BaseController {
|
||||||
@Inject()
|
@Inject()
|
||||||
private stripePaymentApp: StripePaymentApplication;
|
private stripePaymentApp: StripePaymentApplication;
|
||||||
|
|
||||||
router() {
|
public router() {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.post('/account', asyncMiddleware(this.createAccount.bind(this)));
|
router.post('/account', asyncMiddleware(this.createAccount.bind(this)));
|
||||||
router.post(
|
router.post(
|
||||||
'/account_session',
|
'/account_link',
|
||||||
asyncMiddleware(this.createAccountSession.bind(this))
|
[body('stripe_account_id').exists()],
|
||||||
|
this.validationResult,
|
||||||
|
asyncMiddleware(this.createAccountLink.bind(this))
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/:linkId/create_checkout_session',
|
'/:linkId/create_checkout_session',
|
||||||
this.createCheckoutSession.bind(this)
|
this.createCheckoutSession.bind(this)
|
||||||
);
|
);
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,8 +68,7 @@ export class StripeIntegrationController {
|
|||||||
const accountId = await this.stripePaymentApp.createStripeAccount(
|
const accountId = await this.stripePaymentApp.createStripeAccount(
|
||||||
tenantId
|
tenantId
|
||||||
);
|
);
|
||||||
|
return res.status(201).json({
|
||||||
res.status(201).json({
|
|
||||||
accountId,
|
accountId,
|
||||||
message: 'The Stripe account has been created successfully.',
|
message: 'The Stripe account has been created successfully.',
|
||||||
});
|
});
|
||||||
@@ -82,20 +84,20 @@ export class StripeIntegrationController {
|
|||||||
* @param {NextFunction} next - The Express next middleware function.
|
* @param {NextFunction} next - The Express next middleware function.
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public async createAccountSession(
|
public async createAccountLink(
|
||||||
req: Request,
|
req: Request,
|
||||||
res: Response,
|
res: Response,
|
||||||
next: NextFunction
|
next: NextFunction
|
||||||
) {
|
) {
|
||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
const { account } = req.body;
|
const { stripeAccountId } = this.matchedBodyData(req);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const clientSecret = await this.stripePaymentApp.createStripeAccount(
|
const clientSecret = await this.stripePaymentApp.createAccountLink(
|
||||||
tenantId,
|
tenantId,
|
||||||
account
|
stripeAccountId
|
||||||
);
|
);
|
||||||
res.status(200).json({ clientSecret });
|
return res.status(200).json({ clientSecret });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export class StripeWebhooksController {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private eventPublisher: EventPublisher;
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
router() {
|
public router() {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
@@ -35,7 +35,7 @@ export class StripeWebhooksController {
|
|||||||
* @param {Response} res - The Express response object.
|
* @param {Response} res - The Express response object.
|
||||||
* @param {NextFunction} next - The Express next middleware function.
|
* @param {NextFunction} next - The Express next middleware function.
|
||||||
*/
|
*/
|
||||||
public async handleWebhook(req: Request, res: Response, next: NextFunction) {
|
private async handleWebhook(req: Request, res: Response, next: NextFunction) {
|
||||||
try {
|
try {
|
||||||
let event = req.body;
|
let event = req.body;
|
||||||
const sig = req.headers['stripe-signature'];
|
const sig = req.headers['stripe-signature'];
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ exports.up = function (knex) {
|
|||||||
table.string('service');
|
table.string('service');
|
||||||
table.string('name');
|
table.string('name');
|
||||||
table.string('slug');
|
table.string('slug');
|
||||||
table.boolean('enable').defaultTo(true);
|
table.boolean('active').defaultTo(false);
|
||||||
table.string('account_id');
|
table.string('account_id');
|
||||||
table.json('options');
|
table.json('options');
|
||||||
table.timestamps();
|
table.timestamps();
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ export interface StripeCheckoutSessionCompletedEventPayload {
|
|||||||
event: any;
|
event: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface StripeInvoiceCheckoutSessionPOJO {
|
export interface StripeInvoiceCheckoutSessionPOJO {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
publishableKey: string;
|
publishableKey: string;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,79 @@
|
|||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import { GetPaymentServicesSpecificInvoice } from './GetPaymentServicesSpecificInvoice';
|
import { GetPaymentServicesSpecificInvoice } from './GetPaymentServicesSpecificInvoice';
|
||||||
|
import { DeletePaymentMethodService } from './DeletePaymentMethodService';
|
||||||
|
import { EditPaymentMethodService } from './EditPaymentMethodService';
|
||||||
|
import { EditPaymentMethodDTO, GetPaymentMethodsPOJO } from './types';
|
||||||
|
import { GetPaymentMethodsStateService } from './GetPaymentMethodsState';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class PaymentServicesApplication {
|
export class PaymentServicesApplication {
|
||||||
@Inject()
|
@Inject()
|
||||||
private getPaymentServicesSpecificInvoice: GetPaymentServicesSpecificInvoice;
|
private getPaymentServicesSpecificInvoice: GetPaymentServicesSpecificInvoice;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private deletePaymentMethodService: DeletePaymentMethodService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private editPaymentMethodService: EditPaymentMethodService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private getPaymentMethodsStateService: GetPaymentMethodsStateService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the payment services for a specific invoice.
|
* Retrieves the payment services for a specific invoice.
|
||||||
* @param {number} tenantId - The ID of the tenant.
|
* @param {number} tenantId - The ID of the tenant.
|
||||||
* @param {number} invoiceId - The ID of the invoice.
|
* @param {number} invoiceId - The ID of the invoice.
|
||||||
* @returns {Promise<any>} The payment services for the specified 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(
|
return this.getPaymentServicesSpecificInvoice.getPaymentServicesInvoice(
|
||||||
tenantId
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
25
packages/server/src/services/PaymentServices/types.ts
Normal file
25
packages/server/src/services/PaymentServices/types.ts
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { Service, Inject } from 'typedi';
|
||||||
|
import { StripePaymentService } from './StripePaymentService';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class CreateStripeAccountLinkService {
|
||||||
|
@Inject()
|
||||||
|
private stripePaymentService: StripePaymentService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Stripe account id.
|
||||||
|
* @param {number} tenantId
|
||||||
|
*/
|
||||||
|
public createAccountLink(tenantId: number, stripeAccountId: string) {
|
||||||
|
return this.stripePaymentService.createAccountLink(stripeAccountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ export class CreateStripeAccountService {
|
|||||||
await PaymentIntegration.query().insert({
|
await PaymentIntegration.query().insert({
|
||||||
name: parsedStripeAccountDTO.name,
|
name: parsedStripeAccountDTO.name,
|
||||||
accountId: stripeAccountId,
|
accountId: stripeAccountId,
|
||||||
enable: false,
|
active: false, // Active will turn true after onboarding.
|
||||||
service: 'Stripe',
|
service: 'Stripe',
|
||||||
});
|
});
|
||||||
// Triggers `onStripeIntegrationAccountCreated` event.
|
// Triggers `onStripeIntegrationAccountCreated` event.
|
||||||
|
|||||||
@@ -2,12 +2,16 @@ import { Inject } from 'typedi';
|
|||||||
import { CreateInvoiceCheckoutSession } from './CreateInvoiceCheckoutSession';
|
import { CreateInvoiceCheckoutSession } from './CreateInvoiceCheckoutSession';
|
||||||
import { StripeInvoiceCheckoutSessionPOJO } from '@/interfaces/StripePayment';
|
import { StripeInvoiceCheckoutSessionPOJO } from '@/interfaces/StripePayment';
|
||||||
import { CreateStripeAccountService } from './CreateStripeAccountService';
|
import { CreateStripeAccountService } from './CreateStripeAccountService';
|
||||||
|
import { CreateStripeAccountLinkService } from './CreateStripeAccountLink';
|
||||||
import { CreateStripeAccountDTO } from './types';
|
import { CreateStripeAccountDTO } from './types';
|
||||||
|
|
||||||
export class StripePaymentApplication {
|
export class StripePaymentApplication {
|
||||||
@Inject()
|
@Inject()
|
||||||
private createStripeAccountService: CreateStripeAccountService;
|
private createStripeAccountService: CreateStripeAccountService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private createStripeAccountLinkService: CreateStripeAccountLinkService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private createInvoiceCheckoutSessionService: CreateInvoiceCheckoutSession;
|
private createInvoiceCheckoutSessionService: CreateInvoiceCheckoutSession;
|
||||||
|
|
||||||
@@ -26,6 +30,19 @@ export class StripePaymentApplication {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Stripe account link of the given Stripe accoun..
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string} stripeAccountId
|
||||||
|
* @returns {}
|
||||||
|
*/
|
||||||
|
public createAccountLink(tenantId: number, stripeAccountId: string) {
|
||||||
|
return this.createStripeAccountLinkService.createAccountLink(
|
||||||
|
tenantId,
|
||||||
|
stripeAccountId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the Stripe checkout session from the given sale invoice.
|
* Creates the Stripe checkout session from the given sale invoice.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { Service } from 'typedi';
|
|||||||
import stripe from 'stripe';
|
import stripe from 'stripe';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
|
|
||||||
|
const origin = 'http://localhost:4000';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class StripePaymentService {
|
export class StripePaymentService {
|
||||||
public stripe;
|
public stripe;
|
||||||
@@ -33,6 +35,27 @@ export class StripePaymentService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} accountId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
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:'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
|
|||||||
@@ -703,6 +703,14 @@ export default {
|
|||||||
onAssigningDefault: 'onPdfTemplateAssigningDefault',
|
onAssigningDefault: 'onPdfTemplateAssigningDefault',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Payment method.
|
||||||
|
paymentMethod: {
|
||||||
|
onEditing: 'onPaymentMethodEditing',
|
||||||
|
onEdited: 'onPaymentMethodEdited',
|
||||||
|
|
||||||
|
onDeleted: 'onPaymentMethodDeleted',
|
||||||
|
},
|
||||||
|
|
||||||
// Payment methods integrations
|
// Payment methods integrations
|
||||||
paymentIntegrationLink: {
|
paymentIntegrationLink: {
|
||||||
onPaymentIntegrationLink: 'onPaymentIntegrationLink',
|
onPaymentIntegrationLink: 'onPaymentIntegrationLink',
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export function PaymentPortal() {
|
|||||||
|
|
||||||
<Group position={'apart'} className={styles.totalItem}>
|
<Group position={'apart'} className={styles.totalItem}>
|
||||||
<Text>Total</Text>
|
<Text>Total</Text>
|
||||||
<Text style={{ fontWeight: 600 }}>
|
<Text style={{ fontWeight: 500 }}>
|
||||||
{sharableLinkMeta?.totalFormatted}
|
{sharableLinkMeta?.totalFormatted}
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -96,7 +96,7 @@ export function PaymentPortal() {
|
|||||||
className={clsx(styles.totalItem, styles.borderBottomDark)}
|
className={clsx(styles.totalItem, styles.borderBottomDark)}
|
||||||
>
|
>
|
||||||
<Text>Due Amount</Text>
|
<Text>Due Amount</Text>
|
||||||
<Text style={{ fontWeight: 600 }}>
|
<Text style={{ fontWeight: 500 }}>
|
||||||
{sharableLinkMeta?.dueAmountFormatted}
|
{sharableLinkMeta?.dueAmountFormatted}
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { StripeIntegration } from '@/containers/StripePayment/StripeIntegration';
|
import { StripeIntegration2 } from '@/containers/StripePayment/StripeIntegration';
|
||||||
|
|
||||||
export default function IntegrationsPage() {
|
export default function IntegrationsPage() {
|
||||||
return <StripeIntegration />
|
return <StripeIntegration2 />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import React, { createContext, ReactNode, useContext } from 'react';
|
||||||
|
import { useGetPaymentServicesState } from '@/hooks/query/payment-services';
|
||||||
|
|
||||||
|
type PaymentMethodsContextType = {
|
||||||
|
isPaymentMethodsStateLoading: boolean;
|
||||||
|
paymentMethodsState: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PaymentMethodsContext = createContext<PaymentMethodsContextType>(
|
||||||
|
{} as PaymentMethodsContextType,
|
||||||
|
);
|
||||||
|
|
||||||
|
type PaymentMethodsProviderProps = {
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PaymentMethodsBoot = ({ children }: PaymentMethodsProviderProps) => {
|
||||||
|
const { data: paymentMethodsState, isLoading: isPaymentMethodsStateLoading } =
|
||||||
|
useGetPaymentServicesState();
|
||||||
|
|
||||||
|
const value = { isPaymentMethodsStateLoading, paymentMethodsState };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PaymentMethodsContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</PaymentMethodsContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const usePaymentMethodsBoot = () => {
|
||||||
|
const context = useContext<PaymentMethodsContextType>(PaymentMethodsContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error(
|
||||||
|
'usePaymentMethods must be used within a PaymentMethodsProvider',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { PaymentMethodsBoot, usePaymentMethodsBoot };
|
||||||
@@ -1,20 +1,23 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import { Button, Classes, Intent, Text } from '@blueprintjs/core';
|
||||||
import { Box, Card, Group, Stack } from '@/components';
|
import { Box, Card, Group, Stack } from '@/components';
|
||||||
import { StripeLogo } from '@/icons/StripeLogo';
|
import { StripeLogo } from '@/icons/StripeLogo';
|
||||||
import { Button, Classes, Intent, Text } from '@blueprintjs/core';
|
import { PaymentMethodsBoot } from './PreferencesPaymentMethodsBoot';
|
||||||
|
|
||||||
export default function PreferencesPaymentMethodsPage() {
|
export default function PreferencesPaymentMethodsPage() {
|
||||||
return (
|
return (
|
||||||
<PaymentMethodsRoot>
|
<PaymentMethodsRoot>
|
||||||
<Text className={Classes.TEXT_MUTED} style={{ marginBottom: 20 }}>
|
<PaymentMethodsBoot>
|
||||||
Accept payments from all the major debit and credit card networks
|
<Text className={Classes.TEXT_MUTED} style={{ marginBottom: 20 }}>
|
||||||
through the supported payment gateways.
|
Accept payments from all the major debit and credit card networks
|
||||||
</Text>
|
through the supported payment gateways.
|
||||||
|
</Text>
|
||||||
|
|
||||||
<Stack>
|
<Stack>
|
||||||
<StripePaymentMethod />
|
<StripePaymentMethod />
|
||||||
</Stack>
|
</Stack>
|
||||||
|
</PaymentMethodsBoot>
|
||||||
</PaymentMethodsRoot>
|
</PaymentMethodsRoot>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,17 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
ConnectAccountOnboarding,
|
useCreateStripeAccount,
|
||||||
ConnectComponentsProvider,
|
useCreateStripeAccountLink,
|
||||||
} from '@stripe/react-connect-js';
|
} from '@/hooks/query/stripe-integration';
|
||||||
import { useStripeConnect } from './use-stripe-connect';
|
|
||||||
import { useCreateStripeAccount } from '@/hooks/query/stripe-integration';
|
|
||||||
|
|
||||||
export function StripeIntegration() {
|
export const StripeIntegration2 = () => {
|
||||||
const [accountCreatePending, setAccountCreatePending] =
|
const [accountCreatePending, setAccountCreatePending] = useState(false);
|
||||||
useState<boolean>(false);
|
const [accountLinkCreatePending, setAccountLinkCreatePending] =
|
||||||
const [onboardingExited, setOnboardingExited] = useState<boolean>(false);
|
useState(false);
|
||||||
const [error, setError] = useState<boolean>(false);
|
const [error, setError] = useState(false);
|
||||||
const [connectedAccountId, setConnectedAccountId] = useState<string | null>(
|
const [connectedAccountId, setConnectedAccountId] = useState<string>();
|
||||||
null,
|
const { mutateAsync: createStripeAccount } = useCreateStripeAccount();
|
||||||
);
|
const { mutateAsync: createStripeAccountLink } = useCreateStripeAccountLink();
|
||||||
const stripeConnectInstance = useStripeConnect(connectedAccountId || '');
|
|
||||||
const { mutateAsync: createAccount } = useCreateStripeAccount();
|
|
||||||
|
|
||||||
const handleSignupBtnClick = () => {
|
|
||||||
setAccountCreatePending(true);
|
|
||||||
setError(false);
|
|
||||||
|
|
||||||
createAccount({})
|
|
||||||
.then((account) => {
|
|
||||||
setConnectedAccountId(account.account_id);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
setError(true);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setAccountCreatePending(false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
@@ -40,29 +20,70 @@ export function StripeIntegration() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="content">
|
<div className="content">
|
||||||
{!connectedAccountId && <h2>Get ready for take off</h2>}
|
{!connectedAccountId && <h2>Get ready for take off</h2>}
|
||||||
{connectedAccountId && !stripeConnectInstance && (
|
|
||||||
<h2>Add information to start accepting money</h2>
|
|
||||||
)}
|
|
||||||
{!connectedAccountId && (
|
{!connectedAccountId && (
|
||||||
<p>
|
<p>
|
||||||
Bigcapital Technology, Inc. is the world's leading air travel
|
Bigcapital Technology, Inc. is the world's leading air travel
|
||||||
platform: join our team of pilots to help people travel faster.
|
platform: join our team of pilots to help people travel faster.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{!accountCreatePending && !connectedAccountId && (
|
{connectedAccountId && (
|
||||||
<div>
|
<h2>Add information to start accepting money</h2>
|
||||||
<button onClick={handleSignupBtnClick}>Sign up</button>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
{stripeConnectInstance && (
|
{connectedAccountId && (
|
||||||
<ConnectComponentsProvider connectInstance={stripeConnectInstance}>
|
<p>
|
||||||
<ConnectAccountOnboarding
|
Matt's Mats partners with Stripe to help you receive payments and
|
||||||
onExit={() => setOnboardingExited(true)}
|
keep your personal bank and details secure.
|
||||||
/>
|
</p>
|
||||||
</ConnectComponentsProvider>
|
)}
|
||||||
|
{!accountCreatePending && !connectedAccountId && (
|
||||||
|
<button
|
||||||
|
onClick={async () => {
|
||||||
|
setAccountCreatePending(true);
|
||||||
|
setError(false);
|
||||||
|
createStripeAccount({}).then((response) => {
|
||||||
|
const { account_id: accountId } = response;
|
||||||
|
setAccountCreatePending(false);
|
||||||
|
|
||||||
|
if (accountId) {
|
||||||
|
setConnectedAccountId(accountId);
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
setError(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Create an account!
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{connectedAccountId && !accountLinkCreatePending && (
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setAccountLinkCreatePending(true);
|
||||||
|
setError(false);
|
||||||
|
createStripeAccountLink({
|
||||||
|
stripeAccountId: connectedAccountId,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
const { clientSecret } = res;
|
||||||
|
setAccountLinkCreatePending(false);
|
||||||
|
|
||||||
|
if (clientSecret.url) {
|
||||||
|
window.location.href = clientSecret.url;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setError(true);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add information
|
||||||
|
</button>
|
||||||
)}
|
)}
|
||||||
{error && <p className="error">Something went wrong!</p>}
|
{error && <p className="error">Something went wrong!</p>}
|
||||||
{(connectedAccountId || accountCreatePending || onboardingExited) && (
|
{(connectedAccountId ||
|
||||||
|
accountCreatePending ||
|
||||||
|
accountLinkCreatePending) && (
|
||||||
<div className="dev-callout">
|
<div className="dev-callout">
|
||||||
{connectedAccountId && (
|
{connectedAccountId && (
|
||||||
<p>
|
<p>
|
||||||
@@ -71,17 +92,14 @@ export function StripeIntegration() {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{accountCreatePending && <p>Creating a connected account...</p>}
|
{accountCreatePending && <p>Creating a connected account...</p>}
|
||||||
{onboardingExited && (
|
{accountLinkCreatePending && <p>Creating a new Account Link...</p>}
|
||||||
<p>The Account Onboarding component has exited</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="info-callout">
|
<div className="info-callout">
|
||||||
<p>
|
<p>
|
||||||
This is a sample app for Connect onboarding using the Account
|
This is a sample app for Stripe-hosted Connect onboarding.{' '}
|
||||||
Onboarding embedded component.{' '}
|
|
||||||
<a
|
<a
|
||||||
href="https://docs.stripe.com/connect/onboarding/quickstart?connect-onboarding-surface=embedded"
|
href="https://docs.stripe.com/connect/onboarding/quickstart?connect-onboarding-surface=hosted"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
@@ -92,4 +110,4 @@ export function StripeIntegration() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
69
packages/webapp/src/hooks/query/payment-methods.ts
Normal file
69
packages/webapp/src/hooks/query/payment-methods.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import {
|
||||||
|
useMutation,
|
||||||
|
UseMutationOptions,
|
||||||
|
UseMutationResult,
|
||||||
|
} from 'react-query';
|
||||||
|
import useApiRequest from '../useRequest';
|
||||||
|
|
||||||
|
|
||||||
|
// # Delete payment method
|
||||||
|
// -----------------------------------------
|
||||||
|
interface DeletePaymentMethodValues {
|
||||||
|
paymentMethodId: number;
|
||||||
|
}
|
||||||
|
export const useDeletePaymentMethod = (
|
||||||
|
options?: UseMutationOptions<void, Error, DeletePaymentMethodValues>,
|
||||||
|
): UseMutationResult<void, Error, DeletePaymentMethodValues> => {
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useMutation<void, Error, DeletePaymentMethodValues>(
|
||||||
|
({ paymentMethodId }) => {
|
||||||
|
return apiRequest
|
||||||
|
.delete(`/payment-methods/${paymentMethodId}`)
|
||||||
|
.then((res) => res.data);
|
||||||
|
},
|
||||||
|
{ ...options },
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// # Edit payment method
|
||||||
|
// -----------------------------------------
|
||||||
|
interface EditPaymentMethodValues {
|
||||||
|
paymentMethodId: number;
|
||||||
|
name?: string;
|
||||||
|
bankAccountId?: number;
|
||||||
|
clearningAccountId?: number;
|
||||||
|
showVisa?: boolean;
|
||||||
|
showMasterCard?: boolean;
|
||||||
|
showDiscover?: boolean;
|
||||||
|
showAmer?: boolean;
|
||||||
|
showJcb?: boolean;
|
||||||
|
showDiners?: boolean;
|
||||||
|
}
|
||||||
|
interface EditPaymentMethodResponse {
|
||||||
|
id: number;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
export const useEditPaymentMethod = (
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
EditPaymentMethodResponse,
|
||||||
|
Error,
|
||||||
|
EditPaymentMethodValues
|
||||||
|
>,
|
||||||
|
): UseMutationResult<
|
||||||
|
EditPaymentMethodResponse,
|
||||||
|
Error,
|
||||||
|
EditPaymentMethodValues
|
||||||
|
> => {
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useMutation<EditPaymentMethodResponse, Error, EditPaymentMethodValues>(
|
||||||
|
({ paymentMethodId, ...editData }) => {
|
||||||
|
return apiRequest
|
||||||
|
.put(`/payment-methods/${paymentMethodId}`, editData)
|
||||||
|
.then((res) => res.data);
|
||||||
|
},
|
||||||
|
{ ...options },
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -6,7 +6,6 @@ import { transformToCamelCase } from '@/utils';
|
|||||||
const PaymentServicesQueryKey = 'PaymentServices';
|
const PaymentServicesQueryKey = 'PaymentServices';
|
||||||
|
|
||||||
export interface GetPaymentServicesResponse {}
|
export interface GetPaymentServicesResponse {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the integrated payment services.
|
* Retrieves the integrated payment services.
|
||||||
* @param {UseQueryOptions<GetPaymentServicesResponse, Error>} options
|
* @param {UseQueryOptions<GetPaymentServicesResponse, Error>} options
|
||||||
@@ -33,3 +32,31 @@ export const useGetPaymentServices = (
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface GetPaymentServicesStateResponse {}
|
||||||
|
/**
|
||||||
|
* Retrieves the state of payment services.
|
||||||
|
* @param {UseQueryOptions<GetPaymentServicesStateResponse, Error>} options
|
||||||
|
* @returns {UseQueryResult<GetPaymentServicesStateResponse, Error>}
|
||||||
|
*/
|
||||||
|
export const useGetPaymentServicesState = (
|
||||||
|
options?: UseQueryOptions<GetPaymentServicesStateResponse, Error>,
|
||||||
|
): UseQueryResult<GetPaymentServicesStateResponse, Error> => {
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useQuery<GetPaymentServicesStateResponse, Error>(
|
||||||
|
['PaymentServicesState'],
|
||||||
|
() =>
|
||||||
|
apiRequest
|
||||||
|
.get('/payment-services/state')
|
||||||
|
.then(
|
||||||
|
(response) =>
|
||||||
|
transformToCamelCase(
|
||||||
|
response.data?.paymentServicesState,
|
||||||
|
) as GetPaymentServicesStateResponse,
|
||||||
|
),
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -7,6 +7,49 @@ import {
|
|||||||
import useApiRequest from '../useRequest';
|
import useApiRequest from '../useRequest';
|
||||||
import { transformToCamelCase } from '@/utils';
|
import { transformToCamelCase } from '@/utils';
|
||||||
|
|
||||||
|
|
||||||
|
// Create Stripe Account Link.
|
||||||
|
// ------------------------------------
|
||||||
|
interface StripeAccountLinkResponse {
|
||||||
|
clientSecret: {
|
||||||
|
created: number;
|
||||||
|
expiresAt: number;
|
||||||
|
object: string;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
interface StripeAccountLinkValues {
|
||||||
|
stripeAccountId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useCreateStripeAccountLink = (
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
StripeAccountLinkResponse,
|
||||||
|
Error,
|
||||||
|
StripeAccountLinkValues
|
||||||
|
>,
|
||||||
|
): UseMutationResult<
|
||||||
|
StripeAccountLinkResponse,
|
||||||
|
Error,
|
||||||
|
StripeAccountLinkValues
|
||||||
|
> => {
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(values: StripeAccountLinkValues) => {
|
||||||
|
return apiRequest
|
||||||
|
.post('/stripe_integration/account_link', {
|
||||||
|
stripe_account_id: values?.stripeAccountId,
|
||||||
|
})
|
||||||
|
.then((res) => transformToCamelCase(res.data));
|
||||||
|
},
|
||||||
|
{ ...options },
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Create Stripe Account Session.
|
||||||
|
// ------------------------------------
|
||||||
interface AccountSessionValues {
|
interface AccountSessionValues {
|
||||||
connectedAccountId?: string;
|
connectedAccountId?: string;
|
||||||
}
|
}
|
||||||
@@ -40,6 +83,8 @@ export const useCreateStripeAccountSession = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Create Stripe Account.
|
||||||
|
// ------------------------------------
|
||||||
interface CreateStripeAccountValues {}
|
interface CreateStripeAccountValues {}
|
||||||
interface CreateStripeAccountResponse {
|
interface CreateStripeAccountResponse {
|
||||||
account_id: string;
|
account_id: string;
|
||||||
@@ -64,6 +109,8 @@ export const useCreateStripeAccount = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Create Stripe Checkout Session.
|
||||||
|
// ------------------------------------
|
||||||
interface CreateCheckoutSessionValues {
|
interface CreateCheckoutSessionValues {
|
||||||
linkId: string;
|
linkId: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,20 +9,22 @@ export const ArrowBottomLeft: React.FC<ArrowBottomLeftProps> = ({
|
|||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<svg
|
<span className={'bp4-icon bp4-icon-arrow-bottom-left'}>
|
||||||
width={size}
|
<svg
|
||||||
height={size}
|
width={size}
|
||||||
viewBox="0 0 16 16"
|
height={size}
|
||||||
fill="none"
|
viewBox="0 0 16 16"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
fill="none"
|
||||||
{...props}
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
{...props}
|
||||||
<path
|
>
|
||||||
fillRule="evenodd"
|
<path
|
||||||
clipRule="evenodd"
|
fillRule="evenodd"
|
||||||
d="M14 3C14 2.45 13.55 2 13 2C12.72 2 12.47 2.11 12.29 2.29L4 10.59V6C4 5.45 3.55 5 3 5S2 5.45 2 6V13C2 13.55 2.45 14 3 14H10C10.55 14 11 13.55 11 13C11 12.45 10.55 12 10 12H5.41L13.7 3.71C13.89 3.53 14 3.28 14 3Z"
|
clipRule="evenodd"
|
||||||
fill="currentColor"
|
d="M14 3C14 2.45 13.55 2 13 2C12.72 2 12.47 2.11 12.29 2.29L4 10.59V6C4 5.45 3.55 5 3 5S2 5.45 2 6V13C2 13.55 2.45 14 3 14H10C10.55 14 11 13.55 11 13C11 12.45 10.55 12 10 12H5.41L13.7 3.71C13.89 3.53 14 3.28 14 3Z"
|
||||||
/>
|
fill="currentColor"
|
||||||
</svg>
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user