mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 22:30:31 +00:00
feat: rename voucher to license.
This commit is contained in:
@@ -1,17 +1,17 @@
|
|||||||
import { Router, Request, Response } from 'express'
|
import { Router, Request, Response } from 'express'
|
||||||
import { check, oneOf, ValidationChain } from 'express-validator';
|
import { check, oneOf, ValidationChain } from 'express-validator';
|
||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import { Voucher, Plan } from '@/system/models';
|
import { License, Plan } from '@/system/models';
|
||||||
import BaseController from '@/http/controllers/BaseController';
|
import BaseController from '@/http/controllers/BaseController';
|
||||||
import VoucherService from '@/services/Payment/Voucher';
|
import LicenseService from '@/services/Payment/License';
|
||||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
import { IVouchersFilter } from '@/interfaces';
|
import { ILicensesFilter } from '@/interfaces';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class VouchersController extends BaseController {
|
export default class LicensesController extends BaseController {
|
||||||
@Inject()
|
@Inject()
|
||||||
voucherService: VoucherService;
|
licenseService: LicenseService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Router constructor.
|
* Router constructor.
|
||||||
@@ -21,40 +21,40 @@ export default class VouchersController extends BaseController {
|
|||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/generate',
|
'/generate',
|
||||||
this.generateVoucherSchema,
|
this.generateLicenseSchema,
|
||||||
validateMiddleware,
|
validateMiddleware,
|
||||||
asyncMiddleware(this.validatePlanExistance.bind(this)),
|
asyncMiddleware(this.validatePlanExistance.bind(this)),
|
||||||
asyncMiddleware(this.generateVoucher.bind(this)),
|
asyncMiddleware(this.generateLicense.bind(this)),
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/disable/:voucherId',
|
'/disable/:licenseId',
|
||||||
validateMiddleware,
|
validateMiddleware,
|
||||||
asyncMiddleware(this.validateVoucherExistance.bind(this)),
|
asyncMiddleware(this.validateLicenseExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateNotDisabledVoucher.bind(this)),
|
asyncMiddleware(this.validateNotDisabledLicense.bind(this)),
|
||||||
asyncMiddleware(this.disableVoucher.bind(this)),
|
asyncMiddleware(this.disableLicense.bind(this)),
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/send',
|
'/send',
|
||||||
this.sendVoucherSchemaValidation,
|
this.sendLicenseSchemaValidation,
|
||||||
validateMiddleware,
|
validateMiddleware,
|
||||||
asyncMiddleware(this.sendVoucher.bind(this)),
|
asyncMiddleware(this.sendLicense.bind(this)),
|
||||||
);
|
);
|
||||||
router.delete(
|
router.delete(
|
||||||
'/:voucherId',
|
'/:licenseId',
|
||||||
asyncMiddleware(this.validateVoucherExistance.bind(this)),
|
asyncMiddleware(this.validateLicenseExistance.bind(this)),
|
||||||
asyncMiddleware(this.deleteVoucher.bind(this)),
|
asyncMiddleware(this.deleteLicense.bind(this)),
|
||||||
);
|
);
|
||||||
router.get(
|
router.get(
|
||||||
'/',
|
'/',
|
||||||
asyncMiddleware(this.listVouchers.bind(this)),
|
asyncMiddleware(this.listLicenses.bind(this)),
|
||||||
);
|
);
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate voucher validation schema.
|
* Generate license validation schema.
|
||||||
*/
|
*/
|
||||||
get generateVoucherSchema(): ValidationChain[] {
|
get generateLicenseSchema(): ValidationChain[] {
|
||||||
return [
|
return [
|
||||||
check('loop').exists().isNumeric().toInt(),
|
check('loop').exists().isNumeric().toInt(),
|
||||||
check('period').exists().isNumeric().toInt(),
|
check('period').exists().isNumeric().toInt(),
|
||||||
@@ -66,22 +66,22 @@ export default class VouchersController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specific voucher validation schema.
|
* Specific license validation schema.
|
||||||
*/
|
*/
|
||||||
get specificVoucherSchema(): ValidationChain[] {
|
get specificLicenseSchema(): ValidationChain[] {
|
||||||
return [
|
return [
|
||||||
oneOf([
|
oneOf([
|
||||||
check('voucher_id').exists().isNumeric().toInt(),
|
check('license_id').exists().isNumeric().toInt(),
|
||||||
], [
|
], [
|
||||||
check('voucher_code').exists().isNumeric().toInt(),
|
check('license_code').exists().isNumeric().toInt(),
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send voucher validation schema.
|
* Send license validation schema.
|
||||||
*/
|
*/
|
||||||
get sendVoucherSchemaValidation(): ValidationChain[] {
|
get sendLicenseSchemaValidation(): ValidationChain[] {
|
||||||
return [
|
return [
|
||||||
check('period').exists().isNumeric(),
|
check('period').exists().isNumeric(),
|
||||||
check('period_interval').exists().trim().escape(),
|
check('period_interval').exists().trim().escape(),
|
||||||
@@ -113,60 +113,60 @@ export default class VouchersController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Valdiate the voucher existance on the storage.
|
* Valdiate the license existance on the storage.
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @param {Function}
|
* @param {Function}
|
||||||
*/
|
*/
|
||||||
async validateVoucherExistance(req: Request, res: Response, next: Function) {
|
async validateLicenseExistance(req: Request, res: Response, next: Function) {
|
||||||
const body = this.matchedBodyData(req);
|
const body = this.matchedBodyData(req);
|
||||||
|
|
||||||
const voucherId = body.voucherId || req.params.voucherId;
|
const licenseId = body.licenseId || req.params.licenseId;
|
||||||
const foundVoucher = await Voucher.query().findById(voucherId);
|
const foundLicense = await License.query().findById(licenseId);
|
||||||
|
|
||||||
if (!foundVoucher) {
|
if (!foundLicense) {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
errors: [{ type: 'VOUCHER.NOT.FOUND', code: 200 }],
|
errors: [{ type: 'LICENSE.NOT.FOUND', code: 200 }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates whether the voucher id is disabled.
|
* Validates whether the license id is disabled.
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @param {Function} next
|
* @param {Function} next
|
||||||
*/
|
*/
|
||||||
async validateNotDisabledVoucher(req: Request, res: Response, next: Function) {
|
async validateNotDisabledLicense(req: Request, res: Response, next: Function) {
|
||||||
const voucherId = req.params.voucherId || req.query.voucherId;
|
const licenseId = req.params.licenseId || req.query.licenseId;
|
||||||
const foundVoucher = await Voucher.query().findById(voucherId);
|
const foundLicense = await License.query().findById(licenseId);
|
||||||
|
|
||||||
if (foundVoucher.disabled) {
|
if (foundLicense.disabled) {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
errors: [{ type: 'VOUCHER.ALREADY.DISABLED', code: 200 }],
|
errors: [{ type: 'LICENSE.ALREADY.DISABLED', code: 200 }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate vouchers codes with given period in bulk.
|
* Generate licenses codes with given period in bulk.
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @return {Response}
|
* @return {Response}
|
||||||
*/
|
*/
|
||||||
async generateVoucher(req: Request, res: Response, next: Function) {
|
async generateLicense(req: Request, res: Response, next: Function) {
|
||||||
const { loop = 10, period, periodInterval, planId } = this.matchedBodyData(req);
|
const { loop = 10, period, periodInterval, planId } = this.matchedBodyData(req);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.voucherService.generateVouchers(
|
await this.licenseService.generateLicenses(
|
||||||
loop, period, periodInterval, planId,
|
loop, period, periodInterval, planId,
|
||||||
);
|
);
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
code: 100,
|
code: 100,
|
||||||
type: 'VOUCHERES.GENERATED.SUCCESSFULLY',
|
type: 'LICENSEES.GENERATED.SUCCESSFULLY',
|
||||||
message: 'The vouchers have been generated successfully.'
|
message: 'The licenses have been generated successfully.'
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
@@ -175,84 +175,84 @@ export default class VouchersController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable the given voucher on the storage.
|
* Disable the given license on the storage.
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @return {Response}
|
* @return {Response}
|
||||||
*/
|
*/
|
||||||
async disableVoucher(req: Request, res: Response) {
|
async disableLicense(req: Request, res: Response) {
|
||||||
const { voucherId } = req.params;
|
const { licenseId } = req.params;
|
||||||
|
|
||||||
await this.voucherService.disableVoucher(voucherId);
|
await this.licenseService.disableLicense(licenseId);
|
||||||
|
|
||||||
return res.status(200).send({ voucher_id: voucherId });
|
return res.status(200).send({ license_id: licenseId });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the given voucher code on the storage.
|
* Deletes the given license code on the storage.
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @return {Response}
|
* @return {Response}
|
||||||
*/
|
*/
|
||||||
async deleteVoucher(req: Request, res: Response) {
|
async deleteLicense(req: Request, res: Response) {
|
||||||
const { voucherId } = req.params;
|
const { licenseId } = req.params;
|
||||||
|
|
||||||
await this.voucherService.deleteVoucher(voucherId);
|
await this.licenseService.deleteLicense(licenseId);
|
||||||
|
|
||||||
return res.status(200).send({ voucher_id: voucherId });
|
return res.status(200).send({ license_id: licenseId });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send voucher code in the given period to the customer via email or phone number
|
* Send license code in the given period to the customer via email or phone number
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @return {Response}
|
* @return {Response}
|
||||||
*/
|
*/
|
||||||
async sendVoucher(req: Request, res: Response) {
|
async sendLicense(req: Request, res: Response) {
|
||||||
const { phoneNumber, email, period, periodInterval, planId } = this.matchedBodyData(req);
|
const { phoneNumber, email, period, periodInterval, planId } = this.matchedBodyData(req);
|
||||||
|
|
||||||
const voucher = await Voucher.query()
|
const license = await License.query()
|
||||||
.modify('filterActiveVoucher')
|
.modify('filterActiveLicense')
|
||||||
.where('voucher_period', period)
|
.where('license_period', period)
|
||||||
.where('period_interval', periodInterval)
|
.where('period_interval', periodInterval)
|
||||||
.where('plan_id', planId)
|
.where('plan_id', planId)
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (!voucher) {
|
if (!license) {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
status: 110,
|
status: 110,
|
||||||
message: 'There is no vouchers availiable right now with the given period and plan.',
|
message: 'There is no licenses availiable right now with the given period and plan.',
|
||||||
code: 'NO.AVALIABLE.VOUCHER.CODE',
|
code: 'NO.AVALIABLE.LICENSE.CODE',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await this.voucherService.sendVoucherToCustomer(
|
await this.licenseService.sendLicenseToCustomer(
|
||||||
voucher.voucherCode, phoneNumber, email,
|
license.licenseCode, phoneNumber, email,
|
||||||
);
|
);
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
status: 100,
|
status: 100,
|
||||||
code: 'VOUCHER.CODE.SENT',
|
code: 'LICENSE.CODE.SENT',
|
||||||
message: 'The voucher has been sent to the given customer.',
|
message: 'The license has been sent to the given customer.',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listing vouchers.
|
* Listing licenses.
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
*/
|
*/
|
||||||
async listVouchers(req: Request, res: Response) {
|
async listLicenses(req: Request, res: Response) {
|
||||||
const filter: IVouchersFilter = {
|
const filter: ILicensesFilter = {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
used: false,
|
used: false,
|
||||||
sent: false,
|
sent: false,
|
||||||
active: false,
|
active: false,
|
||||||
...req.query,
|
...req.query,
|
||||||
};
|
};
|
||||||
const vouchers = await Voucher.query()
|
const licenses = await License.query()
|
||||||
.onBuild((builder) => {
|
.onBuild((builder) => {
|
||||||
builder.modify('filter', filter);
|
builder.modify('filter', filter);
|
||||||
builder.orderBy('createdAt', 'ASC');
|
builder.orderBy('createdAt', 'ASC');
|
||||||
});
|
});
|
||||||
return res.status(200).send({ vouchers });
|
return res.status(200).send({ licenses });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { Router, Request, Response } from 'express';
|
import { Router, Request, Response } from 'express';
|
||||||
import { check, param, query, ValidationSchema } from 'express-validator';
|
import { check, param, query, ValidationSchema } from 'express-validator';
|
||||||
import { Voucher, Plan } from '@/system/models';
|
import { License, Plan } from '@/system/models';
|
||||||
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
import validateMiddleware from '@/http/middleware/validateMiddleware';
|
||||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||||
import PaymentMethodController from '@/http/controllers/Subscription/PaymentMethod';
|
import PaymentMethodController from '@/http/controllers/Subscription/PaymentMethod';
|
||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
} from '@/exceptions';
|
} from '@/exceptions';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class PaymentViaVoucherController extends PaymentMethodController {
|
export default class PaymentViaLicenseController extends PaymentMethodController {
|
||||||
@Inject('logger')
|
@Inject('logger')
|
||||||
logger: any;
|
logger: any;
|
||||||
|
|
||||||
@@ -22,88 +22,88 @@ export default class PaymentViaVoucherController extends PaymentMethodController
|
|||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/payment',
|
'/payment',
|
||||||
this.paymentViaVoucherSchema,
|
this.paymentViaLicenseSchema,
|
||||||
validateMiddleware,
|
validateMiddleware,
|
||||||
asyncMiddleware(this.validateVoucherCodeExistance.bind(this)),
|
asyncMiddleware(this.validateLicenseCodeExistance.bind(this)),
|
||||||
asyncMiddleware(this.validatePlanSlugExistance.bind(this)),
|
asyncMiddleware(this.validatePlanSlugExistance.bind(this)),
|
||||||
asyncMiddleware(this.validateVoucherAndPlan.bind(this)),
|
asyncMiddleware(this.validateLicenseAndPlan.bind(this)),
|
||||||
asyncMiddleware(this.paymentViaVoucher.bind(this)),
|
asyncMiddleware(this.paymentViaLicense.bind(this)),
|
||||||
);
|
);
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment via voucher validation schema.
|
* Payment via license validation schema.
|
||||||
*/
|
*/
|
||||||
get paymentViaVoucherSchema() {
|
get paymentViaLicenseSchema() {
|
||||||
return [
|
return [
|
||||||
check('plan_slug').exists().trim().escape(),
|
check('plan_slug').exists().trim().escape(),
|
||||||
check('voucher_code').exists().trim().escape(),
|
check('license_code').exists().trim().escape(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate the given voucher code exists on the storage.
|
* Validate the given license code exists on the storage.
|
||||||
* @async
|
* @async
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
*/
|
*/
|
||||||
async validateVoucherCodeExistance(req: Request, res: Response, next: Function) {
|
async validateLicenseCodeExistance(req: Request, res: Response, next: Function) {
|
||||||
const { voucherCode } = this.matchedBodyData(req);
|
const { licenseCode } = this.matchedBodyData(req);
|
||||||
this.logger.info('[voucher_payment] trying to validate voucher code.', { voucherCode });
|
this.logger.info('[license_payment] trying to validate license code.', { licenseCode });
|
||||||
|
|
||||||
const foundVoucher = await Voucher.query()
|
const foundLicense = await License.query()
|
||||||
.modify('filterActiveVoucher')
|
.modify('filterActiveLicense')
|
||||||
.where('voucher_code', voucherCode)
|
.where('license_code', licenseCode)
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (!foundVoucher) {
|
if (!foundLicense) {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
errors: [{ type: 'VOUCHER.CODE.IS.INVALID', code: 120 }],
|
errors: [{ type: 'LICENSE.CODE.IS.INVALID', code: 120 }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate the voucher period and plan period.
|
* Validate the license period and plan period.
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @param {Function} next
|
* @param {Function} next
|
||||||
*/
|
*/
|
||||||
async validateVoucherAndPlan(req: Request, res: Response, next: Function) {
|
async validateLicenseAndPlan(req: Request, res: Response, next: Function) {
|
||||||
const { planSlug, voucherCode } = this.matchedBodyData(req);
|
const { planSlug, licenseCode } = this.matchedBodyData(req);
|
||||||
this.logger.info('[voucher_payment] trying to validate voucher with the plan.', { voucherCode });
|
this.logger.info('[license_payment] trying to validate license with the plan.', { licenseCode });
|
||||||
|
|
||||||
const voucher = await Voucher.query().findOne('voucher_code', voucherCode);
|
const license = await License.query().findOne('license_code', licenseCode);
|
||||||
const plan = await Plan.query().findOne('slug', planSlug);
|
const plan = await Plan.query().findOne('slug', planSlug);
|
||||||
|
|
||||||
if (voucher.planId !== plan.id) {
|
if (license.planId !== plan.id) {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
errors: [{ type: 'VOUCHER.NOT.FOR.GIVEN.PLAN' }],
|
errors: [{ type: 'LICENSE.NOT.FOR.GIVEN.PLAN' }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the subscription payment via voucher code.
|
* Handle the subscription payment via license code.
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
* @return {Response}
|
* @return {Response}
|
||||||
*/
|
*/
|
||||||
async paymentViaVoucher(req: Request, res: Response, next: Function) {
|
async paymentViaLicense(req: Request, res: Response, next: Function) {
|
||||||
const { planSlug, voucherCode } = this.matchedBodyData(req);
|
const { planSlug, licenseCode } = this.matchedBodyData(req);
|
||||||
const { tenant } = req;
|
const { tenant } = req;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.subscriptionService
|
await this.subscriptionService
|
||||||
.subscriptionViaVoucher(tenant.id, planSlug, voucherCode);
|
.subscriptionViaLicense(tenant.id, planSlug, licenseCode);
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
code: 'PAYMENT.SUCCESSFULLY.MADE',
|
code: 'PAYMENT.SUCCESSFULLY.MADE',
|
||||||
message: 'Payment via voucher has been made successfully.',
|
message: 'Payment via license has been made successfully.',
|
||||||
});
|
});
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
const errorReasons = [];
|
const errorReasons = [];
|
||||||
@@ -3,7 +3,7 @@ import { Container, Service } from 'typedi';
|
|||||||
import JWTAuth from '@/http/middleware/jwtAuth';
|
import JWTAuth from '@/http/middleware/jwtAuth';
|
||||||
import TenancyMiddleware from '@/http/middleware/TenancyMiddleware';
|
import TenancyMiddleware from '@/http/middleware/TenancyMiddleware';
|
||||||
import AttachCurrentTenantUser from '@/http/middleware/AttachCurrentTenantUser';
|
import AttachCurrentTenantUser from '@/http/middleware/AttachCurrentTenantUser';
|
||||||
import PaymentViaVoucherController from '@/http/controllers/Subscription/PaymentViaVoucher';
|
import PaymentViaLicenseController from '@/http/controllers/Subscription/PaymentViaLicense';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class SubscriptionController {
|
export default class SubscriptionController {
|
||||||
@@ -17,7 +17,7 @@ export default class SubscriptionController {
|
|||||||
router.use(AttachCurrentTenantUser);
|
router.use(AttachCurrentTenantUser);
|
||||||
router.use(TenancyMiddleware);
|
router.use(TenancyMiddleware);
|
||||||
|
|
||||||
router.use('/voucher', Container.get(PaymentViaVoucherController).router());
|
router.use('/license', Container.get(PaymentViaLicenseController).router());
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import Media from '@/http/controllers/Media';
|
|||||||
import Ping from '@/http/controllers/Ping';
|
import Ping from '@/http/controllers/Ping';
|
||||||
import Agendash from '@/http/controllers/Agendash';
|
import Agendash from '@/http/controllers/Agendash';
|
||||||
import Subscription from '@/http/controllers/Subscription';
|
import Subscription from '@/http/controllers/Subscription';
|
||||||
import VouchersController from '@/http/controllers/Subscription/Vouchers';
|
import LicensesController from '@/http/controllers/Subscription/Licenses';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const app = Router();
|
const app = Router();
|
||||||
@@ -45,7 +45,7 @@ export default () => {
|
|||||||
app.use('/auth', Container.get(Authentication).router());
|
app.use('/auth', Container.get(Authentication).router());
|
||||||
app.use('/invite', Container.get(InviteUsers).nonAuthRouter());
|
app.use('/invite', Container.get(InviteUsers).nonAuthRouter());
|
||||||
app.use('/organization', Container.get(Organization).router());
|
app.use('/organization', Container.get(Organization).router());
|
||||||
app.use('/vouchers', Container.get(VouchersController).router());
|
app.use('/licenses', Container.get(LicensesController).router());
|
||||||
app.use('/subscription', Container.get(Subscription).router());
|
app.use('/subscription', Container.get(Subscription).router());
|
||||||
app.use('/ping', Container.get(Ping).router());
|
app.use('/ping', Container.get(Ping).router());
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
|
|
||||||
|
|
||||||
export interface IVoucher {
|
export interface ILicense {
|
||||||
id?: number,
|
id?: number,
|
||||||
voucherCode: string,
|
licenseCode: string,
|
||||||
voucherPeriod: number,
|
licensePeriod: number,
|
||||||
sent: boolean,
|
sent: boolean,
|
||||||
disabled: boolean,
|
disabled: boolean,
|
||||||
used: boolean,
|
used: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IVouchersFilter {
|
export interface ILicensesFilter {
|
||||||
active: boolean,
|
active: boolean,
|
||||||
disabld: boolean,
|
disabld: boolean,
|
||||||
used: boolean,
|
used: boolean,
|
||||||
@@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
export interface IPaymentModel {}
|
export interface IPaymentModel {}
|
||||||
|
|
||||||
export interface IVoucherPaymentModel extends IPaymentModel {
|
export interface ILicensePaymentModel extends IPaymentModel {
|
||||||
voucherCode: string;
|
licenseCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPaymentMethod {
|
export interface IPaymentMethod {
|
||||||
makePayment(paymentModel: IPaymentModel): Promise<boolean>;
|
makePayment(paymentModel: IPaymentModel): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IVoucherPaymentMethod {
|
export interface ILicensePaymentMethod {
|
||||||
makePayment(paymentModel: IVoucherPaymentModel): Promise<boolean>;
|
makePayment(paymentModel: ILicensePaymentModel): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPaymentContext<PaymentModel> {
|
export interface IPaymentContext<PaymentModel> {
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import {
|
|||||||
import { IInventoryCostMethod } from './InventoryCostMethod';
|
import { IInventoryCostMethod } from './InventoryCostMethod';
|
||||||
import { IItemEntry } from './ItemEntry';
|
import { IItemEntry } from './ItemEntry';
|
||||||
import { IItem } from './Item';
|
import { IItem } from './Item';
|
||||||
import { IVoucher, IVouchersFilter } from './Voucher';
|
import { ILicense, ILicensesFilter } from './License';
|
||||||
import { IItemCategory, IItemCategoryOTD } from './ItemCategory';
|
import { IItemCategory, IItemCategoryOTD } from './ItemCategory';
|
||||||
import {
|
import {
|
||||||
IPaymentModel,
|
IPaymentModel,
|
||||||
IVoucherPaymentModel,
|
ILicensePaymentModel,
|
||||||
IPaymentMethod,
|
IPaymentMethod,
|
||||||
IVoucherPaymentMethod,
|
ILicensePaymentMethod,
|
||||||
IPaymentContext,
|
IPaymentContext,
|
||||||
} from './Payment';
|
} from './Payment';
|
||||||
import {
|
import {
|
||||||
@@ -57,16 +57,16 @@ export {
|
|||||||
IInventoryCostMethod,
|
IInventoryCostMethod,
|
||||||
IItemEntry,
|
IItemEntry,
|
||||||
IItem,
|
IItem,
|
||||||
IVoucher,
|
ILicense,
|
||||||
IVouchersFilter,
|
ILicensesFilter,
|
||||||
IItemCategory,
|
IItemCategory,
|
||||||
IItemCategoryOTD,
|
IItemCategoryOTD,
|
||||||
|
|
||||||
IPaymentModel,
|
IPaymentModel,
|
||||||
IPaymentMethod,
|
IPaymentMethod,
|
||||||
IPaymentContext,
|
IPaymentContext,
|
||||||
IVoucherPaymentModel,
|
ILicensePaymentModel,
|
||||||
IVoucherPaymentMethod,
|
ILicensePaymentMethod,
|
||||||
|
|
||||||
ISaleInvoice,
|
ISaleInvoice,
|
||||||
ISaleInvoiceOTD,
|
ISaleInvoiceOTD,
|
||||||
|
|||||||
22
server/src/jobs/SendLicenseEmail.ts
Normal file
22
server/src/jobs/SendLicenseEmail.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { Container } from 'typedi';
|
||||||
|
import LicenseService from '@/services/Payment/License';
|
||||||
|
|
||||||
|
export default class SendLicenseViaEmailJob {
|
||||||
|
public async handler(job, done: Function): Promise<void> {
|
||||||
|
const Logger = Container.get('logger');
|
||||||
|
const licenseService = Container.get(LicenseService);
|
||||||
|
const { email, licenseCode } = job.attrs.data;
|
||||||
|
|
||||||
|
Logger.debug(`Send license via email - started: ${job.attrs.data}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await licenseService.mailMessages.sendMailLicense(licenseCode, email);
|
||||||
|
Logger.debug(`Send license via email - completed: ${job.attrs.data}`);
|
||||||
|
done();
|
||||||
|
} catch(e) {
|
||||||
|
console.log(e);
|
||||||
|
Logger.error(`Send license via email: ${job.attrs.data}, error: ${e}`);
|
||||||
|
done(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
server/src/jobs/SendLicensePhone.ts
Normal file
23
server/src/jobs/SendLicensePhone.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { Container } from 'typedi';
|
||||||
|
import LicenseService from '@/services/Payment/License';
|
||||||
|
|
||||||
|
export default class SendLicenseViaPhoneJob {
|
||||||
|
public async handler(job, done: Function): Promise<void> {
|
||||||
|
const { phoneNumber, licenseCode } = job.attrs.data;
|
||||||
|
|
||||||
|
const Logger = Container.get('logger');
|
||||||
|
const licenseService = Container.get(LicenseService);
|
||||||
|
|
||||||
|
Logger.debug(`Send license via phone number - started: ${job.attrs.data}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await licenseService.smsMessages.sendLicenseSMSMessage(phoneNumber, licenseCode);
|
||||||
|
Logger.debug(`Send license via phone number - completed: ${job.attrs.data}`);
|
||||||
|
done();
|
||||||
|
} catch(e) {
|
||||||
|
console.log(e);
|
||||||
|
Logger.error(`Send license via phone number: ${job.attrs.data}, error: ${e}`);
|
||||||
|
done(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { Container } from 'typedi';
|
|
||||||
import VoucherService from '@/services/Payment/Voucher';
|
|
||||||
|
|
||||||
export default class SendVoucherViaEmailJob {
|
|
||||||
public async handler(job, done: Function): Promise<void> {
|
|
||||||
const Logger = Container.get('logger');
|
|
||||||
const voucherService = Container.get(VoucherService);
|
|
||||||
const { email, voucherCode } = job.attrs.data;
|
|
||||||
|
|
||||||
Logger.debug(`Send voucher via email - started: ${job.attrs.data}`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await voucherService.mailMessages.sendMailVoucher(voucherCode, email);
|
|
||||||
Logger.debug(`Send voucher via email - completed: ${job.attrs.data}`);
|
|
||||||
done();
|
|
||||||
} catch(e) {
|
|
||||||
console.log(e);
|
|
||||||
Logger.error(`Send voucher via email: ${job.attrs.data}, error: ${e}`);
|
|
||||||
done(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { Container } from 'typedi';
|
|
||||||
import VoucherService from '@/services/Payment/Voucher';
|
|
||||||
|
|
||||||
export default class SendVoucherViaPhoneJob {
|
|
||||||
public async handler(job, done: Function): Promise<void> {
|
|
||||||
const { phoneNumber, voucherCode } = job.attrs.data;
|
|
||||||
|
|
||||||
const Logger = Container.get('logger');
|
|
||||||
const voucherService = Container.get(VoucherService);
|
|
||||||
|
|
||||||
Logger.debug(`Send voucher via phone number - started: ${job.attrs.data}`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await voucherService.smsMessages.sendVoucherSMSMessage(phoneNumber, voucherCode);
|
|
||||||
Logger.debug(`Send voucher via phone number - completed: ${job.attrs.data}`);
|
|
||||||
done();
|
|
||||||
} catch(e) {
|
|
||||||
console.log(e);
|
|
||||||
Logger.error(`Send voucher via phone number: ${job.attrs.data}, error: ${e}`);
|
|
||||||
done(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,8 @@ import WelcomeSMSJob from '@/jobs/WelcomeSMS';
|
|||||||
import ResetPasswordMailJob from '@/jobs/ResetPasswordMail';
|
import ResetPasswordMailJob from '@/jobs/ResetPasswordMail';
|
||||||
import ComputeItemCost from '@/jobs/ComputeItemCost';
|
import ComputeItemCost from '@/jobs/ComputeItemCost';
|
||||||
import RewriteInvoicesJournalEntries from '@/jobs/writeInvoicesJEntries';
|
import RewriteInvoicesJournalEntries from '@/jobs/writeInvoicesJEntries';
|
||||||
import SendVoucherViaPhoneJob from '@/jobs/SendVoucherPhone';
|
import SendLicenseViaPhoneJob from '@/jobs/SendLicensePhone';
|
||||||
import SendVoucherViaEmailJob from '@/jobs/SendVoucherEmail';
|
import SendLicenseViaEmailJob from '@/jobs/SendLicenseEmail';
|
||||||
import SendSMSNotificationSubscribeEnd from '@/jobs/SMSNotificationSubscribeEnd';
|
import SendSMSNotificationSubscribeEnd from '@/jobs/SMSNotificationSubscribeEnd';
|
||||||
import SendSMSNotificationTrialEnd from '@/jobs/SMSNotificationTrialEnd';
|
import SendSMSNotificationTrialEnd from '@/jobs/SMSNotificationTrialEnd';
|
||||||
import SendMailNotificationSubscribeEnd from '@/jobs/MailNotificationSubscribeEnd';
|
import SendMailNotificationSubscribeEnd from '@/jobs/MailNotificationSubscribeEnd';
|
||||||
@@ -34,14 +34,14 @@ export default ({ agenda }: { agenda: Agenda }) => {
|
|||||||
new RewriteInvoicesJournalEntries(agenda).handler,
|
new RewriteInvoicesJournalEntries(agenda).handler,
|
||||||
);
|
);
|
||||||
agenda.define(
|
agenda.define(
|
||||||
'send-voucher-via-phone',
|
'send-license-via-phone',
|
||||||
{ priority: 'high', concurrency: 1, },
|
{ priority: 'high', concurrency: 1, },
|
||||||
new SendVoucherViaPhoneJob().handler,
|
new SendLicenseViaPhoneJob().handler,
|
||||||
);
|
);
|
||||||
agenda.define(
|
agenda.define(
|
||||||
'send-voucher-via-email',
|
'send-license-via-email',
|
||||||
{ priority: 'high', concurrency: 1, },
|
{ priority: 'high', concurrency: 1, },
|
||||||
new SendVoucherViaEmailJob().handler,
|
new SendLicenseViaEmailJob().handler,
|
||||||
);
|
);
|
||||||
agenda.define(
|
agenda.define(
|
||||||
'send-sms-notification-subscribe-end',
|
'send-sms-notification-subscribe-end',
|
||||||
|
|||||||
104
server/src/services/Payment/License.ts
Normal file
104
server/src/services/Payment/License.ts
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import { Service, Container, Inject } from 'typedi';
|
||||||
|
import cryptoRandomString from 'crypto-random-string';
|
||||||
|
import { times } from 'lodash';
|
||||||
|
import { License } from "@/system/models";
|
||||||
|
import { ILicense } from '@/interfaces';
|
||||||
|
import LicenseMailMessages from '@/services/Payment/LicenseMailMessages';
|
||||||
|
import LicenseSMSMessages from '@/services/Payment/LicenseSMSMessages';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class LicenseService {
|
||||||
|
@Inject()
|
||||||
|
smsMessages: LicenseSMSMessages;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
mailMessages: LicenseMailMessages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the license code in the given period.
|
||||||
|
* @param {number} licensePeriod
|
||||||
|
* @return {Promise<ILicense>}
|
||||||
|
*/
|
||||||
|
async generateLicense(
|
||||||
|
licensePeriod: number,
|
||||||
|
periodInterval: string = 'days',
|
||||||
|
planId: number,
|
||||||
|
): ILicense {
|
||||||
|
let licenseCode: string;
|
||||||
|
let repeat: boolean = true;
|
||||||
|
|
||||||
|
console.log(License);
|
||||||
|
|
||||||
|
while(repeat) {
|
||||||
|
licenseCode = cryptoRandomString({ length: 10, type: 'numeric' });
|
||||||
|
const foundLicenses = await License.query().where('license_code', licenseCode);
|
||||||
|
|
||||||
|
if (foundLicenses.length === 0) {
|
||||||
|
repeat = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return License.query().insert({
|
||||||
|
licenseCode, licensePeriod, periodInterval, planId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} loop
|
||||||
|
* @param {number} licensePeriod
|
||||||
|
* @param {string} periodInterval
|
||||||
|
* @param {number} planId
|
||||||
|
*/
|
||||||
|
async generateLicenses(
|
||||||
|
loop = 1,
|
||||||
|
licensePeriod: numner,
|
||||||
|
periodInterval: string = 'days',
|
||||||
|
planId: number,
|
||||||
|
) {
|
||||||
|
const asyncOpers: Promise<any>[] = [];
|
||||||
|
|
||||||
|
times(loop, () => {
|
||||||
|
const generateOper = this.generateLicense(licensePeriod, periodInterval, planId);
|
||||||
|
asyncOpers.push(generateOper);
|
||||||
|
});
|
||||||
|
return Promise.all(asyncOpers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables the given license id on the storage.
|
||||||
|
* @param {number} licenseId
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
async disableLicense(licenseId: number) {
|
||||||
|
return License.markLicenseAsDisabled(licenseId, 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given license id from the storage.
|
||||||
|
* @param licenseId
|
||||||
|
*/
|
||||||
|
async deleteLicense(licenseId: number) {
|
||||||
|
return License.query().where('id', licenseId).delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends license code to the given customer via SMS or mail message.
|
||||||
|
* @param {string} licenseCode - License code
|
||||||
|
* @param {string} phoneNumber - Phone number
|
||||||
|
* @param {string} email - Email address.
|
||||||
|
*/
|
||||||
|
async sendLicenseToCustomer(licenseCode: string, phoneNumber: string, email: string) {
|
||||||
|
const agenda = Container.get('agenda');
|
||||||
|
|
||||||
|
// Mark the license as used.
|
||||||
|
await License.markLicenseAsSent(licenseCode);
|
||||||
|
|
||||||
|
if (email) {
|
||||||
|
await agenda.schedule('1 second', 'send-license-via-email', { licenseCode, email });
|
||||||
|
}
|
||||||
|
if (phoneNumber) {
|
||||||
|
await agenda.schedule('1 second', 'send-license-via-phone', { licenseCode, phoneNumber });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,22 +5,22 @@ import { Container } from 'typedi';
|
|||||||
|
|
||||||
export default class SubscriptionMailMessages {
|
export default class SubscriptionMailMessages {
|
||||||
/**
|
/**
|
||||||
* Send voucher code to the given mail address.
|
* Send license code to the given mail address.
|
||||||
* @param {string} voucherCode
|
* @param {string} licenseCode
|
||||||
* @param {email} email
|
* @param {email} email
|
||||||
*/
|
*/
|
||||||
public async sendMailVoucher(voucherCode: string, email: string) {
|
public async sendMailLicense(licenseCode: string, email: string) {
|
||||||
const Logger = Container.get('logger');
|
const Logger = Container.get('logger');
|
||||||
const Mail = Container.get('mail');
|
const Mail = Container.get('mail');
|
||||||
|
|
||||||
const filePath = path.join(global.rootPath, 'views/mail/VoucherReceive.html');
|
const filePath = path.join(global.rootPath, 'views/mail/LicenseReceive.html');
|
||||||
const template = fs.readFileSync(filePath, 'utf8');
|
const template = fs.readFileSync(filePath, 'utf8');
|
||||||
const rendered = Mustache.render(template, { voucherCode });
|
const rendered = Mustache.render(template, { licenseCode });
|
||||||
|
|
||||||
const mailOptions = {
|
const mailOptions = {
|
||||||
to: email,
|
to: email,
|
||||||
from: `${process.env.MAIL_FROM_NAME} ${process.env.MAIL_FROM_ADDRESS}`,
|
from: `${process.env.MAIL_FROM_NAME} ${process.env.MAIL_FROM_ADDRESS}`,
|
||||||
subject: 'Bigcapital Voucher',
|
subject: 'Bigcapital License',
|
||||||
html: rendered,
|
html: rendered,
|
||||||
};
|
};
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
14
server/src/services/Payment/LicensePaymentMethod.ts
Normal file
14
server/src/services/Payment/LicensePaymentMethod.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { License } from "@/system/models";
|
||||||
|
import PaymentMethod from '@/services/Payment/PaymentMethod';
|
||||||
|
import { IPaymentMethod, ILicensePaymentModel } from '@/interfaces';
|
||||||
|
|
||||||
|
export default class VocuherPaymentMethod extends PaymentMethod implements IPaymentMethod {
|
||||||
|
/**
|
||||||
|
* Payment subscription of organization via license code.
|
||||||
|
* @param {ILicensePaymentModel}
|
||||||
|
*/
|
||||||
|
async payment(licensePaymentModel: ILicensePaymentModel) {
|
||||||
|
// Mark the license code as used.
|
||||||
|
return License.markLicenseAsUsed(licensePaymentModel.licenseCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,12 +6,12 @@ export default class SubscriptionSMSMessages {
|
|||||||
smsClient: SMSClient;
|
smsClient: SMSClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends voucher code to the given phone number via SMS message.
|
* Sends license code to the given phone number via SMS message.
|
||||||
* @param {string} phoneNumber
|
* @param {string} phoneNumber
|
||||||
* @param {string} voucherCode
|
* @param {string} licenseCode
|
||||||
*/
|
*/
|
||||||
public async sendVoucherSMSMessage(phoneNumber: string, voucherCode: string) {
|
public async sendLicenseSMSMessage(phoneNumber: string, licenseCode: string) {
|
||||||
const message: string = `Your voucher card number: ${voucherCode}.`;
|
const message: string = `Your license card number: ${licenseCode}.`;
|
||||||
return this.smsClient.sendMessage(phoneNumber, message);
|
return this.smsClient.sendMessage(phoneNumber, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
import { Service, Container, Inject } from 'typedi';
|
|
||||||
import cryptoRandomString from 'crypto-random-string';
|
|
||||||
import { times } from 'lodash';
|
|
||||||
import { Voucher } from "@/system/models";
|
|
||||||
import { IVoucher } from '@/interfaces';
|
|
||||||
import VoucherMailMessages from '@/services/Payment/VoucherMailMessages';
|
|
||||||
import VoucherSMSMessages from '@/services/Payment/VoucherSMSMessages';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export default class VoucherService {
|
|
||||||
@Inject()
|
|
||||||
smsMessages: VoucherSMSMessages;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
mailMessages: VoucherMailMessages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the voucher code in the given period.
|
|
||||||
* @param {number} voucherPeriod
|
|
||||||
* @return {Promise<IVoucher>}
|
|
||||||
*/
|
|
||||||
async generateVoucher(
|
|
||||||
voucherPeriod: number,
|
|
||||||
periodInterval: string = 'days',
|
|
||||||
planId: number,
|
|
||||||
): IVoucher {
|
|
||||||
let voucherCode: string;
|
|
||||||
let repeat: boolean = true;
|
|
||||||
|
|
||||||
console.log(Voucher);
|
|
||||||
|
|
||||||
while(repeat) {
|
|
||||||
voucherCode = cryptoRandomString({ length: 10, type: 'numeric' });
|
|
||||||
const foundVouchers = await Voucher.query().where('voucher_code', voucherCode);
|
|
||||||
|
|
||||||
if (foundVouchers.length === 0) {
|
|
||||||
repeat = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Voucher.query().insert({
|
|
||||||
voucherCode, voucherPeriod, periodInterval, planId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {number} loop
|
|
||||||
* @param {number} voucherPeriod
|
|
||||||
* @param {string} periodInterval
|
|
||||||
* @param {number} planId
|
|
||||||
*/
|
|
||||||
async generateVouchers(
|
|
||||||
loop = 1,
|
|
||||||
voucherPeriod: numner,
|
|
||||||
periodInterval: string = 'days',
|
|
||||||
planId: number,
|
|
||||||
) {
|
|
||||||
const asyncOpers: Promise<any>[] = [];
|
|
||||||
|
|
||||||
times(loop, () => {
|
|
||||||
const generateOper = this.generateVoucher(voucherPeriod, periodInterval, planId);
|
|
||||||
asyncOpers.push(generateOper);
|
|
||||||
});
|
|
||||||
return Promise.all(asyncOpers);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disables the given voucher id on the storage.
|
|
||||||
* @param {number} voucherId
|
|
||||||
* @return {Promise}
|
|
||||||
*/
|
|
||||||
async disableVoucher(voucherId: number) {
|
|
||||||
return Voucher.markVoucherAsDisabled(voucherId, 'id');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the given voucher id from the storage.
|
|
||||||
* @param voucherId
|
|
||||||
*/
|
|
||||||
async deleteVoucher(voucherId: number) {
|
|
||||||
return Voucher.query().where('id', voucherId).delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends voucher code to the given customer via SMS or mail message.
|
|
||||||
* @param {string} voucherCode - Voucher code
|
|
||||||
* @param {string} phoneNumber - Phone number
|
|
||||||
* @param {string} email - Email address.
|
|
||||||
*/
|
|
||||||
async sendVoucherToCustomer(voucherCode: string, phoneNumber: string, email: string) {
|
|
||||||
const agenda = Container.get('agenda');
|
|
||||||
|
|
||||||
// Mark the voucher as used.
|
|
||||||
await Voucher.markVoucherAsSent(voucherCode);
|
|
||||||
|
|
||||||
if (email) {
|
|
||||||
await agenda.schedule('1 second', 'send-voucher-via-email', { voucherCode, email });
|
|
||||||
}
|
|
||||||
if (phoneNumber) {
|
|
||||||
await agenda.schedule('1 second', 'send-voucher-via-phone', { voucherCode, phoneNumber });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { Voucher } from "@/system/models";
|
|
||||||
import PaymentMethod from '@/services/Payment/PaymentMethod';
|
|
||||||
import { IPaymentMethod, IVoucherPaymentModel } from '@/interfaces';
|
|
||||||
|
|
||||||
export default class VocuherPaymentMethod extends PaymentMethod implements IPaymentMethod {
|
|
||||||
/**
|
|
||||||
* Payment subscription of organization via voucher code.
|
|
||||||
* @param {IVoucherPaymentModel}
|
|
||||||
*/
|
|
||||||
async payment(voucherPaymentModel: IVoucherPaymentModel) {
|
|
||||||
// Mark the voucher code as used.
|
|
||||||
return Voucher.markVoucherAsUsed(voucherPaymentModel.voucherCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject } from 'typedi';
|
||||||
import { Plan, Tenant, Voucher } from '@/system/models';
|
import { Plan, Tenant, License } from '@/system/models';
|
||||||
import Subscription from '@/services/Subscription/Subscription';
|
import Subscription from '@/services/Subscription/Subscription';
|
||||||
import VocuherPaymentMethod from '@/services/Payment/VoucherPaymentMethod';
|
import VocuherPaymentMethod from '@/services/Payment/LicensePaymentMethod';
|
||||||
import PaymentContext from '@/services/Payment';
|
import PaymentContext from '@/services/Payment';
|
||||||
import SubscriptionSMSMessages from '@/services/Subscription/SMSMessages';
|
import SubscriptionSMSMessages from '@/services/Subscription/SMSMessages';
|
||||||
import SubscriptionMailMessages from '@/services/Subscription/MailMessages';
|
import SubscriptionMailMessages from '@/services/Subscription/MailMessages';
|
||||||
@@ -15,30 +15,30 @@ export default class SubscriptionService {
|
|||||||
mailMessages: SubscriptionMailMessages;
|
mailMessages: SubscriptionMailMessages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the payment process via voucher code and than subscribe to
|
* Handles the payment process via license code and than subscribe to
|
||||||
* the given tenant.
|
* the given tenant.
|
||||||
*
|
*
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {String} planSlug
|
* @param {String} planSlug
|
||||||
* @param {string} voucherCode
|
* @param {string} licenseCode
|
||||||
*
|
*
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
async subscriptionViaVoucher(
|
async subscriptionViaLicense(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
planSlug: string,
|
planSlug: string,
|
||||||
voucherCode: string,
|
licenseCode: string,
|
||||||
subscriptionSlug: string = 'main',
|
subscriptionSlug: string = 'main',
|
||||||
) {
|
) {
|
||||||
const plan = await Plan.query().findOne('slug', planSlug);
|
const plan = await Plan.query().findOne('slug', planSlug);
|
||||||
const tenant = await Tenant.query().findById(tenantId);
|
const tenant = await Tenant.query().findById(tenantId);
|
||||||
const voucherModel = await Voucher.query().findOne('voucher_code', voucherCode);
|
const licenseModel = await License.query().findOne('license_code', licenseCode);
|
||||||
|
|
||||||
const paymentViaVoucher = new VocuherPaymentMethod();
|
const paymentViaLicense = new VocuherPaymentMethod();
|
||||||
const paymentContext = new PaymentContext(paymentViaVoucher);
|
const paymentContext = new PaymentContext(paymentViaLicense);
|
||||||
|
|
||||||
const subscription = new Subscription(paymentContext);
|
const subscription = new Subscription(paymentContext);
|
||||||
|
|
||||||
return subscription.subscribe(tenant, plan, voucherModel, subscriptionSlug);
|
return subscription.subscribe(tenant, plan, licenseModel, subscriptionSlug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
|
|
||||||
exports.up = function(knex) {
|
|
||||||
return knex.schema.createTable('subscription_licenses', table => {
|
|
||||||
table.increments();
|
|
||||||
table.string('key');
|
|
||||||
table.integer('license_period');
|
|
||||||
table.string('license_interval');
|
|
||||||
table.boolean('used').defaultTo(false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = function(knex) {
|
|
||||||
return knex.schema.dropTableIfExists('subscription_licenses');
|
|
||||||
};
|
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
|
|
||||||
exports.up = function(knex) {
|
exports.up = function(knex) {
|
||||||
return knex.schema.createTable('subscription_vouchers', table => {
|
return knex.schema.createTable('subscription_licenses', table => {
|
||||||
table.increments();
|
table.increments();
|
||||||
|
|
||||||
table.string('voucher_code').unique();
|
table.string('license_code').unique();
|
||||||
table.integer('plan_id').unsigned();
|
table.integer('plan_id').unsigned();
|
||||||
|
|
||||||
table.integer('voucher_period').unsigned();
|
table.integer('license_period').unsigned();
|
||||||
table.string('period_interval');
|
table.string('period_interval');
|
||||||
|
|
||||||
table.boolean('sent').defaultTo(false);
|
table.boolean('sent').defaultTo(false);
|
||||||
@@ -22,5 +22,5 @@ exports.up = function(knex) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.down = function(knex) {
|
exports.down = function(knex) {
|
||||||
return knex.schema.dropTableIfExists('subscription_vouchers');
|
return knex.schema.dropTableIfExists('subscription_licenses');
|
||||||
};
|
};
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Model, mixin } from 'objection';
|
import { Model, mixin } from 'objection';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import SystemModel from '@/system/models/SystemModel';
|
import SystemModel from '@/system/models/SystemModel';
|
||||||
import { IVouchersFilter } from '@/interfaces';
|
import { ILicensesFilter } from '@/interfaces';
|
||||||
|
|
||||||
export default class Voucher extends SystemModel {
|
export default class License extends SystemModel {
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
*/
|
*/
|
||||||
static get tableName() {
|
static get tableName() {
|
||||||
return 'subscription_vouchers';
|
return 'subscription_licenses';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,34 +23,34 @@ export default class Voucher extends SystemModel {
|
|||||||
*/
|
*/
|
||||||
static get modifiers() {
|
static get modifiers() {
|
||||||
return {
|
return {
|
||||||
// Filters active vouchers.
|
// Filters active licenses.
|
||||||
filterActiveVoucher(query) {
|
filterActiveLicense(query) {
|
||||||
query.where('disabled', false);
|
query.where('disabled', false);
|
||||||
query.where('used', false);
|
query.where('used', false);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Find voucher by its code or id.
|
// Find license by its code or id.
|
||||||
findByCodeOrId(query, id, code) {
|
findByCodeOrId(query, id, code) {
|
||||||
if (id) {
|
if (id) {
|
||||||
query.where('id', id);
|
query.where('id', id);
|
||||||
}
|
}
|
||||||
if (code) {
|
if (code) {
|
||||||
query.where('voucher_code', code);
|
query.where('license_code', code);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Filters vouchers list.
|
// Filters licenses list.
|
||||||
filter(builder, vouchersFilter) {
|
filter(builder, licensesFilter) {
|
||||||
if (vouchersFilter.active) {
|
if (licensesFilter.active) {
|
||||||
builder.modify('filterActiveVoucher')
|
builder.modify('filterActiveLicense')
|
||||||
}
|
}
|
||||||
if (vouchersFilter.disabled) {
|
if (licensesFilter.disabled) {
|
||||||
builder.where('disabled', true);
|
builder.where('disabled', true);
|
||||||
}
|
}
|
||||||
if (vouchersFilter.used) {
|
if (licensesFilter.used) {
|
||||||
builder.where('used', true);
|
builder.where('used', true);
|
||||||
}
|
}
|
||||||
if (vouchersFilter.sent) {
|
if (licensesFilter.sent) {
|
||||||
builder.where('sent', true);
|
builder.where('sent', true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ export default class Voucher extends SystemModel {
|
|||||||
relation: Model.BelongsToOneRelation,
|
relation: Model.BelongsToOneRelation,
|
||||||
modelClass: Plan.default,
|
modelClass: Plan.default,
|
||||||
join: {
|
join: {
|
||||||
from: 'subscription_vouchers.planId',
|
from: 'subscription_licenses.planId',
|
||||||
to: 'subscriptions_plans.id',
|
to: 'subscriptions_plans.id',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -76,24 +76,24 @@ export default class Voucher extends SystemModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the given voucher code from the storage.
|
* Deletes the given license code from the storage.
|
||||||
* @param {string} voucherCode
|
* @param {string} licenseCode
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
static deleteVoucher(voucherCode, viaAttribute = 'voucher_code') {
|
static deleteLicense(licenseCode, viaAttribute = 'license_code') {
|
||||||
return this.query()
|
return this.query()
|
||||||
.where(viaAttribute, voucherCode)
|
.where(viaAttribute, licenseCode)
|
||||||
.delete();
|
.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the given voucher code as disabled on the storage.
|
* Marks the given license code as disabled on the storage.
|
||||||
* @param {string} voucherCode
|
* @param {string} licenseCode
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
static markVoucherAsDisabled(voucherCode, viaAttribute = 'voucher_code') {
|
static markLicenseAsDisabled(licenseCode, viaAttribute = 'license_code') {
|
||||||
return this.query()
|
return this.query()
|
||||||
.where(viaAttribute, voucherCode)
|
.where(viaAttribute, licenseCode)
|
||||||
.patch({
|
.patch({
|
||||||
disabled: true,
|
disabled: true,
|
||||||
disabled_at: moment().toMySqlDateTime(),
|
disabled_at: moment().toMySqlDateTime(),
|
||||||
@@ -101,12 +101,12 @@ export default class Voucher extends SystemModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the given voucher code as sent on the storage.
|
* Marks the given license code as sent on the storage.
|
||||||
* @param {string} voucherCode
|
* @param {string} licenseCode
|
||||||
*/
|
*/
|
||||||
static markVoucherAsSent(voucherCode, viaAttribute = 'voucher_code') {
|
static markLicenseAsSent(licenseCode, viaAttribute = 'license_code') {
|
||||||
return this.query()
|
return this.query()
|
||||||
.where(viaAttribute, voucherCode)
|
.where(viaAttribute, licenseCode)
|
||||||
.patch({
|
.patch({
|
||||||
sent: true,
|
sent: true,
|
||||||
sent_at: moment().toMySqlDateTime(),
|
sent_at: moment().toMySqlDateTime(),
|
||||||
@@ -114,13 +114,13 @@ export default class Voucher extends SystemModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the given voucher code as used on the storage.
|
* Marks the given license code as used on the storage.
|
||||||
* @param {string} voucherCode
|
* @param {string} licenseCode
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
static markVoucherAsUsed(voucherCode, viaAttribute = 'voucher_code') {
|
static markLicenseAsUsed(licenseCode, viaAttribute = 'license_code') {
|
||||||
return this.query()
|
return this.query()
|
||||||
.where(viaAttribute, voucherCode)
|
.where(viaAttribute, licenseCode)
|
||||||
.patch({
|
.patch({
|
||||||
used: true,
|
used: true,
|
||||||
used_at: moment().toMySqlDateTime()
|
used_at: moment().toMySqlDateTime()
|
||||||
@@ -134,7 +134,7 @@ export default class Voucher extends SystemModel {
|
|||||||
*/
|
*/
|
||||||
isEqualPlanPeriod(plan) {
|
isEqualPlanPeriod(plan) {
|
||||||
return (this.invoicePeriod === plan.invoiceInterval &&
|
return (this.invoicePeriod === plan.invoiceInterval &&
|
||||||
voucher.voucherPeriod === voucher.periodInterval);
|
license.licensePeriod === license.periodInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import Plan from './Subscriptions/Plan';
|
import Plan from './Subscriptions/Plan';
|
||||||
import PlanFeature from './Subscriptions/PlanFeature';
|
import PlanFeature from './Subscriptions/PlanFeature';
|
||||||
import PlanSubscription from './Subscriptions/PlanSubscription';
|
import PlanSubscription from './Subscriptions/PlanSubscription';
|
||||||
import Voucher from './Subscriptions/Voucher';
|
import License from './Subscriptions/License';
|
||||||
import Tenant from './Tenant';
|
import Tenant from './Tenant';
|
||||||
import SystemUser from './SystemUser';
|
import SystemUser from './SystemUser';
|
||||||
import PasswordReset from './PasswordReset';
|
import PasswordReset from './PasswordReset';
|
||||||
@@ -12,7 +12,7 @@ export {
|
|||||||
Plan,
|
Plan,
|
||||||
PlanFeature,
|
PlanFeature,
|
||||||
PlanSubscription,
|
PlanSubscription,
|
||||||
Voucher,
|
License,
|
||||||
Tenant,
|
Tenant,
|
||||||
SystemUser,
|
SystemUser,
|
||||||
PasswordReset,
|
PasswordReset,
|
||||||
|
|||||||
@@ -372,9 +372,9 @@
|
|||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<p class="align-center">
|
<p class="align-center">
|
||||||
<h3>Voucher Code</h3>
|
<h3>License Code</h3>
|
||||||
</p>
|
</p>
|
||||||
<p class="mgb-1x">Voucher {{ voucherCode }},</p>
|
<p class="mgb-1x">License {{ licenseCode }},</p>
|
||||||
<p class="mgb-2-5x">Click On The link blow to reset your password.</p>
|
<p class="mgb-2-5x">Click On The link blow to reset your password.</p>
|
||||||
|
|
||||||
<p class="email-note">
|
<p class="email-note">
|
||||||
Reference in New Issue
Block a user