feat: Payment system with voucher cards.

feat: Design with inversion dependency injection architecture.
feat: Prettier http middleware.
feat: Re-write items categories with preferred accounts.
This commit is contained in:
Ahmed Bouhuolia
2020-08-27 20:39:55 +02:00
parent e23b8d9947
commit e4270dc039
63 changed files with 2567 additions and 462 deletions

View File

@@ -0,0 +1,6 @@
import moment from 'moment';
import { IPaymentModel } from '@/interfaces';
export default class PaymentMethod implements IPaymentModel {
}

View File

@@ -0,0 +1,78 @@
import { Service, Container, Inject } from 'typedi';
import cryptoRandomString from 'crypto-random-string';
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;
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,
});
}
/**
* 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 });
}
}
}

View File

@@ -0,0 +1,36 @@
import fs from 'fs';
import path from 'path';
import Mustache from 'mustache';
import { Container } from 'typedi';
import mail from '@/services/mail';
export default class SubscriptionMailMessages {
/**
* Send voucher code to the given mail address.
* @param {string} voucherCode
* @param {email} email
*/
public async sendMailVoucher(voucherCode: string, email: string) {
const logger = Container.get('logger');
const filePath = path.join(global.rootPath, 'views/mail/VoucherReceive.html');
const template = fs.readFileSync(filePath, 'utf8');
const rendered = Mustache.render(template, { voucherCode });
const mailOptions = {
to: email,
from: `${process.env.MAIL_FROM_NAME} ${process.env.MAIL_FROM_ADDRESS}`,
subject: 'Bigcapital Voucher',
html: rendered,
};
return new Promise((resolve, reject) => {
mail.sendMail(mailOptions, (error) => {
if (error) {
reject(error);
return;
}
resolve();
});
});
}
}

View File

@@ -0,0 +1,14 @@
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);
}
}

View File

@@ -0,0 +1,17 @@
import { Container, Inject } from 'typedi';
import SMSClient from '@/services/SMSClient';
export default class SubscriptionSMSMessages {
@Inject('SMSClient')
smsClient: SMSClient;
/**
* Sends voucher code to the given phone number via SMS message.
* @param {string} phoneNumber
* @param {string} voucherCode
*/
public async sendVoucherSMSMessage(phoneNumber: string, voucherCode: string) {
const message: string = `Your voucher card number: ${voucherCode}.`;
return this.smsClient.sendMessage(phoneNumber, message);
}
}

View File

@@ -0,0 +1,21 @@
import { IPaymentMethod, IPaymentContext } from "@/interfaces";
export default class PaymentContext<PaymentModel> implements IPaymentContext{
paymentMethod: IPaymentMethod;
/**
* Constructor method.
* @param {IPaymentMethod} paymentMethod
*/
constructor(paymentMethod: IPaymentMethod) {
this.paymentMethod = paymentMethod;
}
/**
*
* @param {<PaymentModel>} paymentModel
*/
makePayment(paymentModel: PaymentModel) {
this.paymentMethod.makePayment(paymentModel);
}
}