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,48 @@
import { Tenant, Plan } from '@/system/models';
import { IPaymentContext } from '@/interfaces';
import { NotAllowedChangeSubscriptionPlan } from '@/exceptions';
export default class Subscription<PaymentModel> {
paymentContext: IPaymentContext|null;
/**
* Constructor method.
* @param {IPaymentContext}
*/
constructor(payment?: IPaymentContext) {
this.paymentContext = payment;
}
/**
* Subscripe to the given plan.
* @param {Plan} plan
* @throws {NotAllowedChangeSubscriptionPlan}
*/
async subscribe(
tenant: Tenant,
plan: Plan,
paymentModel?: PaymentModel,
subscriptionSlug: string = 'main',
) {
if (plan.price < 0) {
await this.paymentContext.makePayment(paymentModel);
}
const subscription = await tenant.$relatedQuery('subscriptions')
.modify('subscriptionBySlug', subscriptionSlug)
.first();
// No allowed to re-new the the subscription while the subscription is active.
if (subscription && subscription.active()) {
throw new NotAllowedChangeSubscriptionPlan;
// In case there is already subscription associated to the given tenant.
// renew it.
} else if(subscription && subscription.inactive()) {
await subscription.renew(plan);
// No stored past tenant subscriptions create new one.
} else {
await tenant.newSubscription(subscriptionSlug, plan);
}
}
}

View File

@@ -0,0 +1,41 @@
import moment from 'moment';
export default class SubscriptionPeriod {
start: Date;
end: Date;
interval: string;
count: number;
/**
* Constructor method.
* @param {string} interval -
* @param {number} count -
* @param {Date} start -
*/
constructor(interval: string = 'month', count: number, start?: Date) {
this.interval = interval;
this.count = count;
this.start = start;
if (!start) {
this.start = moment().toDate();
}
this.end = moment(start).add(count, interval).toDate();
}
getStartDate() {
return this.start;
}
getEndDate() {
return this.end;
}
getInterval() {
return this.interval;
}
getIntervalCount() {
return this.interval;
}
}

View File

@@ -0,0 +1,36 @@
import { Service } from 'typedi';
import { Plan, Tenant, Voucher } from '@/system/models';
import Subscription from '@/services/Subscription/Subscription';
import VocuherPaymentMethod from '@/services/Payment/VoucherPaymentMethod';
import PaymentContext from '@/services/Payment';
@Service()
export default class SubscriptionService {
/**
* Handles the payment process via voucher code and than subscribe to
* the given tenant.
*
* @param {number} tenantId
* @param {String} planSlug
* @param {string} voucherCode
*
* @return {Promise}
*/
async subscriptionViaVoucher(
tenantId: number,
planSlug: string,
voucherCode: string,
subscriptionSlug: string = 'main',
) {
const plan = await Plan.query().findOne('slug', planSlug);
const tenant = await Tenant.query().findById(tenantId);
const voucherModel = await Voucher.query().findOne('voucher_code', voucherCode);
const paymentViaVoucher = new VocuherPaymentMethod();
const paymentContext = new PaymentContext(paymentViaVoucher);
const subscription = new Subscription(paymentContext);
return subscription.subscribe(tenant, plan, voucherModel, subscriptionSlug);
}
}

View File

@@ -1,22 +0,0 @@
export default (Model) => {
return class UserSubscription extends Model{
onTrial() {
}
getSubscription() {
}
newSubscription() {
}
isSubcribedTo(plan) {
}
}
};