mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 05:40:31 +00:00
fix: mark payment license as used after usage.
fix: fix system models with createdAt and updatedAt fields. fix: reset password token expiration time.
This commit is contained in:
@@ -203,7 +203,7 @@ export default class AuthenticationService {
|
||||
|
||||
// Delete all stored tokens of reset password that associate to the give email.
|
||||
this.logger.info('[send_reset_password] trying to delete all tokens by email.');
|
||||
await PasswordReset.query().where('email', email).delete();
|
||||
this.deletePasswordResetToken(email);
|
||||
|
||||
const token = uniqid();
|
||||
|
||||
@@ -230,28 +230,44 @@ export default class AuthenticationService {
|
||||
this.logger.info('[reset_password] token invalid.');
|
||||
throw new ServiceError('token_invalid');
|
||||
}
|
||||
// Different between tokne creation datetime and current time.
|
||||
if (moment().diff(tokenModel.createdAt, 'seconds') > config.resetPasswordSeconds) {
|
||||
this.logger.info('[reset_password] token expired.');
|
||||
|
||||
// Deletes the expired token by expired token email.
|
||||
await this.deletePasswordResetToken(tokenModel.email);
|
||||
throw new ServiceError('token_expired');
|
||||
}
|
||||
const user = await SystemUser.query().findOne('email', tokenModel.email)
|
||||
|
||||
if (!user) {
|
||||
throw new ServiceError('user_not_found');
|
||||
}
|
||||
const hashedPassword = await hashPassword(password);
|
||||
|
||||
|
||||
this.logger.info('[reset_password] saving a new hashed password.');
|
||||
await SystemUser.query()
|
||||
.where('email', tokenModel.email)
|
||||
.update({
|
||||
password: hashedPassword,
|
||||
});
|
||||
// Delete the reset password token.
|
||||
await PasswordReset.query().where('email', user.email).delete();
|
||||
.update({ password: hashedPassword });
|
||||
|
||||
// Deletes the used token.
|
||||
await this.deletePasswordResetToken(tokenModel.email);
|
||||
|
||||
// Triggers `onResetPassword` event.
|
||||
this.eventDispatcher.dispatch(events.auth.resetPassword, { user, token, password });
|
||||
|
||||
this.logger.info('[reset_password] reset password success.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the password reset token by the given email.
|
||||
* @param {string} email
|
||||
* @returns {Promise}
|
||||
*/
|
||||
private async deletePasswordResetToken(email: string) {
|
||||
this.logger.info('[reset_password] trying to delete all tokens by email.');
|
||||
return PasswordReset.query().where('email', email).delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates JWT token for the given user.
|
||||
* @param {ISystemUser} user
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
EventDispatcher,
|
||||
EventDispatcherInterface,
|
||||
} from '@/decorators/eventDispatcher';
|
||||
import { ServiceError, ServiceErrors } from "@/exceptions";
|
||||
import { ServiceError } from "@/exceptions";
|
||||
import { SystemUser, Invite, Tenant } from "@/system/models";
|
||||
import { Option } from '@/models';
|
||||
import { hashPassword } from '@/utils';
|
||||
@@ -49,8 +49,6 @@ export default class InviteUserService {
|
||||
this.logger.info('[aceept_invite] trying to hash the user password.');
|
||||
const hashedPassword = await hashPassword(inviteUserInput.password);
|
||||
|
||||
console.log(inviteToken);
|
||||
|
||||
this.logger.info('[accept_invite] trying to update user details.');
|
||||
const updateUserOper = SystemUser.query()
|
||||
.where('email', inviteToken.email)
|
||||
@@ -60,7 +58,7 @@ export default class InviteUserService {
|
||||
invite_accepted_at: moment().format('YYYY-MM-DD'),
|
||||
password: hashedPassword,
|
||||
});
|
||||
|
||||
|
||||
this.logger.info('[accept_invite] trying to delete the given token.');
|
||||
const deleteInviteTokenOper = Invite.query().where('token', inviteToken.token).delete();
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ export default class LicenseService {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} loop
|
||||
@@ -52,7 +51,7 @@ export default class LicenseService {
|
||||
*/
|
||||
async generateLicenses(
|
||||
loop = 1,
|
||||
licensePeriod: numner,
|
||||
licensePeriod: number,
|
||||
periodInterval: string = 'days',
|
||||
planId: number,
|
||||
) {
|
||||
@@ -67,7 +66,7 @@ export default class LicenseService {
|
||||
|
||||
/**
|
||||
* Disables the given license id on the storage.
|
||||
* @param {number} licenseId
|
||||
* @param {number} licenseId
|
||||
* @return {Promise}
|
||||
*/
|
||||
async disableLicense(licenseId: number) {
|
||||
|
||||
@@ -1,14 +1,47 @@
|
||||
import { License } from "@/system/models";
|
||||
import PaymentMethod from '@/services/Payment/PaymentMethod';
|
||||
import { Plan } from '@/system/models';
|
||||
import { IPaymentMethod, ILicensePaymentModel } from '@/interfaces';
|
||||
import { ILicensePaymentModel } from "@/interfaces";
|
||||
import { PaymentInputInvalid, PaymentAmountInvalidWithPlan } from '@/exceptions';
|
||||
|
||||
export default class VocuherPaymentMethod extends PaymentMethod implements IPaymentMethod {
|
||||
export default class LicensePaymentMethod extends PaymentMethod implements IPaymentMethod {
|
||||
/**
|
||||
* Payment subscription of organization via license code.
|
||||
* @param {ILicensePaymentModel}
|
||||
* @param {ILicensePaymentModel} licensePaymentModel -
|
||||
*/
|
||||
async payment(licensePaymentModel: ILicensePaymentModel) {
|
||||
async payment(licensePaymentModel: ILicensePaymentModel, plan: Plan) {
|
||||
const license = await this.getLicenseOrThrowInvalid(licensePaymentModel);
|
||||
this.validatePaymentAmountWithPlan(license, plan);
|
||||
|
||||
// Mark the license code as used.
|
||||
return License.markLicenseAsUsed(licensePaymentModel.licenseCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the license code activation on the storage.
|
||||
* @param {ILicensePaymentModel} licensePaymentModel -
|
||||
*/
|
||||
async getLicenseOrThrowInvalid(licensePaymentModel: ILicensePaymentModel) {
|
||||
const foundLicense = await License.query()
|
||||
.modify('filterActiveLicense')
|
||||
.where('license_code', licensePaymentModel.licenseCode)
|
||||
.first();
|
||||
|
||||
if (!foundLicense) {
|
||||
throw new PaymentInputInvalid();
|
||||
}
|
||||
return foundLicense;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the payment amount with given plan price.
|
||||
* @param {License} license
|
||||
* @param {Plan} plan
|
||||
*/
|
||||
validatePaymentAmountWithPlan(license: License, plan: Plan) {
|
||||
if (license.planId !== plan.id) {
|
||||
throw new PaymentAmountInvalidWithPlan();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IPaymentMethod, IPaymentContext } from "@/interfaces";
|
||||
import { Plan } from '@/system/models';
|
||||
|
||||
export default class PaymentContext<PaymentModel> implements IPaymentContext{
|
||||
paymentMethod: IPaymentMethod;
|
||||
@@ -15,7 +16,7 @@ export default class PaymentContext<PaymentModel> implements IPaymentContext{
|
||||
*
|
||||
* @param {<PaymentModel>} paymentModel
|
||||
*/
|
||||
makePayment(paymentModel: PaymentModel) {
|
||||
this.paymentMethod.makePayment(paymentModel);
|
||||
makePayment(paymentModel: PaymentModel, plan: Plan) {
|
||||
return this.paymentMethod.payment(paymentModel, plan);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Inject } from 'typedi';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Tenant, Plan } from '@/system/models';
|
||||
import { IPaymentContext } from '@/interfaces';
|
||||
import { NotAllowedChangeSubscriptionPlan } from '@/exceptions';
|
||||
import { NoPaymentModelWithPricedPlan } from '@/exceptions';
|
||||
|
||||
export default class Subscription<PaymentModel> {
|
||||
paymentContext: IPaymentContext|null;
|
||||
@@ -19,7 +20,7 @@ export default class Subscription<PaymentModel> {
|
||||
|
||||
/**
|
||||
* Subscripe to the given plan.
|
||||
* @param {Plan} plan
|
||||
* @param {Plan} plan
|
||||
* @throws {NotAllowedChangeSubscriptionPlan}
|
||||
*/
|
||||
async subscribe(
|
||||
@@ -28,8 +29,11 @@ export default class Subscription<PaymentModel> {
|
||||
paymentModel?: PaymentModel,
|
||||
subscriptionSlug: string = 'main',
|
||||
) {
|
||||
if (plan.price < 0) {
|
||||
await this.paymentContext.makePayment(paymentModel);
|
||||
this.validateIfPlanHasPriceNoPayment(plan, paymentModel);
|
||||
|
||||
// @todo
|
||||
if (plan.price > 0) {
|
||||
await this.paymentContext.makePayment(paymentModel, plan);
|
||||
}
|
||||
const subscription = await tenant.$relatedQuery('subscriptions')
|
||||
.modify('subscriptionBySlug', subscriptionSlug)
|
||||
@@ -39,8 +43,7 @@ export default class Subscription<PaymentModel> {
|
||||
if (subscription && subscription.active()) {
|
||||
throw new NotAllowedChangeSubscriptionPlan;
|
||||
|
||||
// In case there is already subscription associated to the given tenant.
|
||||
// renew it.
|
||||
// In case there is already subscription associated to the given tenant renew it.
|
||||
} else if(subscription && subscription.inactive()) {
|
||||
await subscription.renew(plan);
|
||||
|
||||
@@ -49,4 +52,15 @@ export default class Subscription<PaymentModel> {
|
||||
await tenant.newSubscription(subscriptionSlug, plan);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw error in plan has price and no payment model.
|
||||
* @param {Plan} plan -
|
||||
* @param {PaymentModel} paymentModel - payment input.
|
||||
*/
|
||||
validateIfPlanHasPriceNoPayment(plan: Plan, paymentModel: PaymentMode) {
|
||||
if (plan.price > 0 && !paymentModel) {
|
||||
throw new NoPaymentModelWithPricedPlan();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import { Plan, Tenant, License } from '@/system/models';
|
||||
import { Plan, Tenant } from '@/system/models';
|
||||
import Subscription from '@/services/Subscription/Subscription';
|
||||
import VocuherPaymentMethod from '@/services/Payment/LicensePaymentMethod';
|
||||
import LicensePaymentMethod from '@/services/Payment/LicensePaymentMethod';
|
||||
import PaymentContext from '@/services/Payment';
|
||||
import SubscriptionSMSMessages from '@/services/Subscription/SMSMessages';
|
||||
import SubscriptionMailMessages from '@/services/Subscription/MailMessages';
|
||||
import { ILicensePaymentModel } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export default class SubscriptionService {
|
||||
@@ -14,31 +15,37 @@ export default class SubscriptionService {
|
||||
@Inject()
|
||||
mailMessages: SubscriptionMailMessages;
|
||||
|
||||
@Inject('logger')
|
||||
logger: any;
|
||||
|
||||
/**
|
||||
* Handles the payment process via license code and than subscribe to
|
||||
* the given tenant.
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {String} planSlug
|
||||
* @param {string} licenseCode
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
async subscriptionViaLicense(
|
||||
tenantId: number,
|
||||
planSlug: string,
|
||||
licenseCode: string,
|
||||
paymentModel?: ILicensePaymentModel,
|
||||
subscriptionSlug: string = 'main',
|
||||
) {
|
||||
this.logger.info('[subscription_via_license] try to subscribe via given license.', {
|
||||
tenantId, paymentModel
|
||||
});
|
||||
const plan = await Plan.query().findOne('slug', planSlug);
|
||||
const tenant = await Tenant.query().findById(tenantId);
|
||||
const licenseModel = await License.query().findOne('license_code', licenseCode);
|
||||
|
||||
const paymentViaLicense = new VocuherPaymentMethod();
|
||||
const paymentViaLicense = new LicensePaymentMethod();
|
||||
const paymentContext = new PaymentContext(paymentViaLicense);
|
||||
|
||||
const subscription = new Subscription(paymentContext);
|
||||
|
||||
return subscription.subscribe(tenant, plan, licenseModel, subscriptionSlug);
|
||||
await subscription.subscribe(tenant, plan, paymentModel, subscriptionSlug);
|
||||
this.logger.info('[subscription_via_license] payment via license done successfully.', {
|
||||
tenantId, paymentModel
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user