mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
feat: Link transations with payment methods
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { body, param } from 'express-validator';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import { GetInvoicePaymentLinkMetadata } from '@/services/Sales/Invoices/GetInvoicePaymentLinkMetadata';
|
||||
|
||||
@Service()
|
||||
export class PublicSharableLinkController extends BaseController {
|
||||
@Inject()
|
||||
private getSharableLinkMetaService: GetInvoicePaymentLinkMetadata;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/sharable-links/meta/invoice/:linkId',
|
||||
[param('linkId').exists()],
|
||||
this.validationResult,
|
||||
this.getPaymentLinkPublicMeta.bind(this),
|
||||
this.validationResult
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the payment link public meta.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
* @returns
|
||||
*/
|
||||
public async getPaymentLinkPublicMeta(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { linkId } = req.params;
|
||||
|
||||
try {
|
||||
const data =
|
||||
await this.getSharableLinkMetaService.getInvoicePaymentLinkMeta(linkId);
|
||||
|
||||
return res.status(200).send({ data });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Router, Request, Response, NextFunction } from 'express';
|
||||
import { body } from 'express-validator';
|
||||
import { AbilitySubject, PaymentReceiveAction } from '@/interfaces';
|
||||
import BaseController from '@/api/controllers/BaseController';
|
||||
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
|
||||
import CheckPolicies from '@/api/middleware/CheckPolicies';
|
||||
import { GenerateShareLink } from '@/services/Sales/Invoices/GenerateeInvoicePaymentLink';
|
||||
|
||||
@Service()
|
||||
export class ShareLinkController extends BaseController {
|
||||
@Inject()
|
||||
private generateShareLinkService: GenerateShareLink;
|
||||
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
'/payment-links/generate',
|
||||
CheckPolicies(PaymentReceiveAction.Edit, AbilitySubject.PaymentReceive),
|
||||
[
|
||||
body('transaction_type').exists(),
|
||||
body('transaction_id').exists().isNumeric().toInt(),
|
||||
body('publicity').optional(),
|
||||
body('expiry_date').optional({ nullable: true }),
|
||||
],
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.generateShareLink.bind(this))
|
||||
);
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates sharable link for the given transaction.
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
public async generateShareLink(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { tenantId } = req;
|
||||
const { transactionType, transactionId, publicity, expiryDate } =
|
||||
this.matchedBodyData(req);
|
||||
|
||||
try {
|
||||
const link = await this.generateShareLinkService.generatePaymentLink(
|
||||
tenantId,
|
||||
transactionId,
|
||||
transactionType,
|
||||
publicity,
|
||||
expiryDate
|
||||
);
|
||||
res.status(200).json({ link });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,6 @@ export class StripeWebhooksController {
|
||||
config.stripePayment.webhooksSecret
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return res.status(400).send(`Webhook Error: ${err.message}`);
|
||||
}
|
||||
// Handle the event based on its type
|
||||
|
||||
@@ -65,6 +65,8 @@ import { ExportController } from './controllers/Export/ExportController';
|
||||
import { AttachmentsController } from './controllers/Attachments/AttachmentsController';
|
||||
import { OneClickDemoController } from './controllers/OneClickDemo/OneClickDemoController';
|
||||
import { StripeIntegrationController } from './controllers/StripeIntegration/StripeIntegrationController';
|
||||
import { ShareLinkController } from './controllers/ShareLink/ShareLinkController';
|
||||
import { PublicSharableLinkController } from './controllers/ShareLink/PublicSharableLinkController';
|
||||
|
||||
export default () => {
|
||||
const app = Router();
|
||||
@@ -82,7 +84,8 @@ export default () => {
|
||||
app.use('/jobs', Container.get(Jobs).router());
|
||||
app.use('/account', Container.get(Account).router());
|
||||
app.use('/webhooks', Container.get(Webhooks).router());
|
||||
app.use('/demo', Container.get(OneClickDemoController).router())
|
||||
app.use('/demo', Container.get(OneClickDemoController).router());
|
||||
app.use(Container.get(PublicSharableLinkController).router());
|
||||
|
||||
// - Dashboard routes.
|
||||
// ---------------------------
|
||||
@@ -148,11 +151,14 @@ export default () => {
|
||||
dashboard.use('/import', Container.get(ImportController).router());
|
||||
dashboard.use('/export', Container.get(ExportController).router());
|
||||
dashboard.use('/attachments', Container.get(AttachmentsController).router());
|
||||
dashboard.use('/stripe_integration', Container.get(StripeIntegrationController).router());
|
||||
|
||||
dashboard.use(
|
||||
'/stripe_integration',
|
||||
Container.get(StripeIntegrationController).router()
|
||||
);
|
||||
dashboard.use('/', Container.get(ProjectTasksController).router());
|
||||
dashboard.use('/', Container.get(ProjectTimesController).router());
|
||||
dashboard.use('/', Container.get(WarehousesItemController).router());
|
||||
dashboard.use('/', Container.get(ShareLinkController).router());
|
||||
|
||||
dashboard.use('/dashboard', Container.get(DashboardController).router());
|
||||
dashboard.use('/', Container.get(Miscellaneous).router());
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('transactions_payment_methods', (table) => {
|
||||
table.increments('id');
|
||||
table.integer('reference_id').unsigned();
|
||||
table.string('reference_type');
|
||||
table.integer('integration_id');
|
||||
table.json('options');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.down = function (knex) {
|
||||
return knex.schema.dropTableIfExists('transactions_payment_methods');
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('payment_integrations', (table) => {
|
||||
table.increments('id');
|
||||
table.string('service');
|
||||
table.string('name');
|
||||
table.string('slug');
|
||||
table.boolean('enable');
|
||||
table.string('account_id');
|
||||
table.json('options');
|
||||
table.timestamps();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.down = function (knex) {
|
||||
return knex.schema.dropTableIfExists('payment_integrations');
|
||||
};
|
||||
27
packages/server/src/models/PaymentIntegration.ts
Normal file
27
packages/server/src/models/PaymentIntegration.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Model } from 'objection';
|
||||
|
||||
export class PaymentIntegration extends Model {
|
||||
static get tableName() {
|
||||
return 'payment_integrations';
|
||||
}
|
||||
|
||||
static get idColumn() {
|
||||
return 'id';
|
||||
}
|
||||
|
||||
static get jsonSchema() {
|
||||
return {
|
||||
type: 'object',
|
||||
required: ['service', 'enable'],
|
||||
properties: {
|
||||
id: { type: 'integer' },
|
||||
service: { type: 'string' },
|
||||
enable: { type: 'boolean' },
|
||||
accountId: { type: 'string' },
|
||||
options: { type: 'object' },
|
||||
createdAt: { type: 'string', format: 'date-time' },
|
||||
updatedAt: { type: 'string', format: 'date-time' },
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -413,6 +413,9 @@ export default class SaleInvoice extends mixin(TenantModel, [
|
||||
const TaxRateTransaction = require('models/TaxRateTransaction');
|
||||
const Document = require('models/Document');
|
||||
const { MatchedBankTransaction } = require('models/MatchedBankTransaction');
|
||||
const {
|
||||
TransactionPaymentService,
|
||||
} = require('models/TransactionPaymentService');
|
||||
|
||||
return {
|
||||
/**
|
||||
@@ -509,7 +512,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
|
||||
join: {
|
||||
from: 'sales_invoices.warehouseId',
|
||||
to: 'warehouses.id',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -566,12 +569,27 @@ export default class SaleInvoice extends mixin(TenantModel, [
|
||||
modelClass: MatchedBankTransaction,
|
||||
join: {
|
||||
from: 'sales_invoices.id',
|
||||
to: "matched_bank_transactions.referenceId",
|
||||
to: 'matched_bank_transactions.referenceId',
|
||||
},
|
||||
filter(query) {
|
||||
query.where('reference_type', 'SaleInvoice');
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Sale invoice may belongs to payment methods.
|
||||
*/
|
||||
paymentMethods: {
|
||||
relation: Model.HasManyRelation,
|
||||
modelClass: TransactionPaymentService,
|
||||
join: {
|
||||
from: 'sales_invoices.id',
|
||||
to: 'transactions_payment_services.referenceId',
|
||||
},
|
||||
filter: (query) => {
|
||||
query.where('reference_type', 'SaleInvoice');
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
33
packages/server/src/models/TransactionPaymentService.ts
Normal file
33
packages/server/src/models/TransactionPaymentService.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Model, mixin } from 'objection';
|
||||
import TenantModel from 'models/TenantModel';
|
||||
|
||||
export class TransactionPaymentService extends TenantModel {
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
static get tableName() {
|
||||
return 'transactions_payment_services';
|
||||
}
|
||||
|
||||
static get jsonSchema() {
|
||||
return {
|
||||
type: 'object',
|
||||
required: ['service', 'enable'],
|
||||
properties: {
|
||||
id: { type: 'integer' },
|
||||
reference_id: { type: 'integer' },
|
||||
reference_type: { type: 'string' },
|
||||
service: { type: 'string' },
|
||||
enable: { type: 'boolean' },
|
||||
options: { type: 'object' },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Relationship mapping.
|
||||
*/
|
||||
static get relationMappings() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
|
||||
export class GeneratePaymentLinkTransformer extends Transformer {
|
||||
/**
|
||||
* Exclude these attributes from payment link object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['linkId'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Included attributes.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return ['link'];
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param link
|
||||
* @returns
|
||||
*/
|
||||
public link(link) {
|
||||
return `http://localhost:3000/payment/${link.linkId}`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import events from '@/subscribers/events';
|
||||
import { PaymentLink } from '@/system/models';
|
||||
import { GeneratePaymentLinkTransformer } from './GeneratePaymentLinkTransformer';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
|
||||
@Service()
|
||||
export class GenerateShareLink {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Generates private or public payment link for the given sale invoice.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} invoiceId - Sale invoice id.
|
||||
* @param {string} publicOrPrivate - Public or private.
|
||||
* @param {string} expiryTime - Expiry time.
|
||||
*/
|
||||
async generatePaymentLink(
|
||||
tenantId: number,
|
||||
transactionId: number,
|
||||
transactionType: string,
|
||||
publicity: string = 'private',
|
||||
expiryTime: string = ''
|
||||
) {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
|
||||
const foundInvoice = await SaleInvoice.query()
|
||||
.findById(transactionId)
|
||||
.throwIfNotFound();
|
||||
|
||||
// Generate unique uuid for sharable link.
|
||||
const linkId = uuidv4() as string;
|
||||
|
||||
const commonEventPayload = {
|
||||
tenantId,
|
||||
transactionId,
|
||||
transactionType,
|
||||
publicity,
|
||||
expiryTime,
|
||||
};
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onPublicSharableLinkGenerating` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.saleInvoice.onPublicLinkGenerating,
|
||||
{ ...commonEventPayload, trx }
|
||||
);
|
||||
const paymentLink = await PaymentLink.query().insert({
|
||||
linkId,
|
||||
tenantId,
|
||||
publicity,
|
||||
resourceId: foundInvoice.id,
|
||||
resourceType: 'SaleInvoice',
|
||||
});
|
||||
// Triggers `onPublicSharableLinkGenerated` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.saleInvoice.onPublicLinkGenerated,
|
||||
{
|
||||
...commonEventPayload,
|
||||
paymentLink,
|
||||
trx,
|
||||
}
|
||||
);
|
||||
return this.transformer.transform(
|
||||
tenantId,
|
||||
paymentLink,
|
||||
new GeneratePaymentLinkTransformer()
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import moment from 'moment';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { PaymentLink } from '@/system/models';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { GeneratePaymentLinkTransformer } from './GeneratePaymentLinkTransformer';
|
||||
import { GetInvoicePaymentLinkMetaTransformer } from './GetInvoicePaymentLinkTransformer';
|
||||
import { initalizeTenantServices } from '@/api/middleware/TenantDependencyInjection';
|
||||
|
||||
@Service()
|
||||
export class GetInvoicePaymentLinkMetadata {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private transformer: TransformerInjectable;
|
||||
|
||||
/**
|
||||
* Retrieves the invoice sharable link meta of the link id.
|
||||
* @param {number}
|
||||
* @param {string} linkId
|
||||
*/
|
||||
async getInvoicePaymentLinkMeta(linkId: string) {
|
||||
const paymentLink = await PaymentLink.query()
|
||||
.findOne('linkId', linkId)
|
||||
.throwIfNotFound();
|
||||
|
||||
//
|
||||
if (paymentLink.resourceType !== 'SaleInvoice') {
|
||||
throw new ServiceError('');
|
||||
}
|
||||
// Validate the expiry at date.
|
||||
if (paymentLink.expiryAt) {
|
||||
const currentDate = moment();
|
||||
const expiryDate = moment(paymentLink.expiryAt);
|
||||
|
||||
if (expiryDate.isBefore(currentDate)) {
|
||||
throw new ServiceError('PAYMENT_LINK_EXPIRED');
|
||||
}
|
||||
}
|
||||
const tenantId = paymentLink.tenantId;
|
||||
await initalizeTenantServices(tenantId);
|
||||
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
|
||||
const invoice = await SaleInvoice.query()
|
||||
.findById(paymentLink.resourceId)
|
||||
.withGraphFetched('entries')
|
||||
.withGraphFetched('customer')
|
||||
.throwIfNotFound();
|
||||
|
||||
return this.transformer.transform(
|
||||
tenantId,
|
||||
invoice,
|
||||
new GetInvoicePaymentLinkMetaTransformer()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { SaleInvoiceTransformer } from './SaleInvoiceTransformer';
|
||||
|
||||
export class GetInvoicePaymentLinkMetaTransformer extends SaleInvoiceTransformer {
|
||||
/**
|
||||
* Exclude these attributes from payment link object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['*'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Included attributes.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'companyName',
|
||||
'customerName',
|
||||
'dueAmount',
|
||||
'dueDateFormatted',
|
||||
'invoiceDateFormatted',
|
||||
'total',
|
||||
'totalFormatted',
|
||||
'totalLocalFormatted',
|
||||
'subtotal',
|
||||
'subtotalFormatted',
|
||||
'subtotalLocalFormatted',
|
||||
'dueAmount',
|
||||
'dueAmountFormatted',
|
||||
'paymentAmount',
|
||||
'paymentAmountFormatted',
|
||||
'dueDate',
|
||||
'dueDateFormatted',
|
||||
'invoiceNo',
|
||||
];
|
||||
};
|
||||
|
||||
public customerName(invoice) {
|
||||
return invoice.customer.displayName;
|
||||
}
|
||||
|
||||
public companyName() {
|
||||
return 'Bigcapital Technology, Inc.';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { StripePaymentService } from './StripePaymentService';
|
||||
import HasTenancyService from '../Tenancy/TenancyService';
|
||||
import { snakeCase } from 'lodash';
|
||||
|
||||
interface CreateStripeAccountDTO {
|
||||
name: string;
|
||||
}
|
||||
|
||||
@Service()
|
||||
export class CreateStripeAccountService {
|
||||
@Inject()
|
||||
private stripePaymentService: StripePaymentService;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Creates a new Stripe account for Bigcapital.
|
||||
* @param {number} tenantId
|
||||
* @param {number} createStripeAccountDTO
|
||||
*/
|
||||
async createAccount(
|
||||
tenantId: number,
|
||||
createStripeAccountDTO: CreateStripeAccountDTO
|
||||
) {
|
||||
const { PaymentIntegration } = this.tenancy.models(tenantId);
|
||||
|
||||
// Creates a new Stripe account.
|
||||
const account = await this.stripePaymentService.createAccount();
|
||||
|
||||
const slug = snakeCase(createStripeAccountDTO.name);
|
||||
|
||||
// Store the Stripe account on tenant store.
|
||||
await PaymentIntegration.query().insert({
|
||||
service: 'stripe',
|
||||
name: createStripeAccountDTO.name,
|
||||
slug,
|
||||
enable: true,
|
||||
accountId: account.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -33,11 +33,15 @@ export class StripePaymentService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
public async createAccount(): Promise<string> {
|
||||
try {
|
||||
const account = await this.stripe.accounts.create({});
|
||||
|
||||
return account.id;
|
||||
return account;
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
'An error occurred when calling the Stripe API to create an account'
|
||||
|
||||
@@ -163,6 +163,9 @@ export default {
|
||||
|
||||
onMailReminderSend: 'onSaleInvoiceMailReminderSend',
|
||||
onMailReminderSent: 'onSaleInvoiceMailReminderSent',
|
||||
|
||||
onPublicLinkGenerating: 'onPublicSharableLinkGenerating',
|
||||
onPublicLinkGenerated: 'onPublicSharableLinkGenerated',
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.createTable('payment_links', (table) => {
|
||||
table.increments('id');
|
||||
table.integer('tenant_id');
|
||||
table.integer('resource_id');
|
||||
table.text('resource_type');
|
||||
table.string('linkId');
|
||||
table.string('publicity');
|
||||
table.datetime('expiry_at');
|
||||
table.timestamps();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.down = function (knex) {
|
||||
return knex.schema.dropTableIfExists('payment_links');
|
||||
};
|
||||
26
packages/server/src/system/models/PaymentLink.ts
Normal file
26
packages/server/src/system/models/PaymentLink.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Model } from 'objection';
|
||||
|
||||
export class PaymentLink extends Model {
|
||||
static get tableName() {
|
||||
return 'payment_links';
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamps columns.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
static get timestamps() {
|
||||
return ['createdAt', 'updatedAt'];
|
||||
}
|
||||
|
||||
public tenantId!: number;
|
||||
public resourceId!: number;
|
||||
public resourceType!: string;
|
||||
public linkId!: string;
|
||||
public publicity!: string;
|
||||
public expiryAt!: Date;
|
||||
|
||||
// Timestamps
|
||||
public createdAt!: Date;
|
||||
public updatedAt!: Date;
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import Invite from './Invite';
|
||||
import SystemPlaidItem from './SystemPlaidItem';
|
||||
import { Import } from './Import';
|
||||
import { StripeAccount } from './StripeAccount';
|
||||
import { PaymentLink } from './PaymentLink';
|
||||
|
||||
export {
|
||||
Plan,
|
||||
@@ -19,5 +20,6 @@ export {
|
||||
Invite,
|
||||
SystemPlaidItem,
|
||||
Import,
|
||||
StripeAccount
|
||||
StripeAccount,
|
||||
PaymentLink,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user