mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 14:17:22 +01:00
Group plan subscription (#8153)
* Added payment to groups and pay with group plan with Stripe * Added edit card for Stripe * Added stripe cancel * Added subscribe with Amazon payments * Added Amazon cancel for group subscription * Added group subscription with paypal * Added paypal cancel * Added ipn cancel for Group plan * Added a subscription tab and hid only the task tab when group is not subscribed * Fixed linting issues * Fixed tests * Added payment unit tests * Added back refresh after stripe payment * Fixed style issues * Limited grouop query fields and checked access * Abstracted subscription schema * Added year group plan and more access checks * Maded purchase fields private * Removed id and timestampes * Added else checks to ensure user subscription is not altered. Removed active field from group model * Added toJSONTransform function * Moved plan active check to other toJson function * Added check to see if purchaed has been populated * Added purchase details to private * Added correct data usage when paying for group sub
This commit is contained in:
committed by
Matteo Pagliazzi
parent
7f38c61c70
commit
d8c37f6e2d
@@ -165,12 +165,17 @@ api.getGroup = {
|
||||
throw new NotFound(res.t('groupNotFound'));
|
||||
}
|
||||
|
||||
group = Group.toJSONCleanChat(group, user);
|
||||
// Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833
|
||||
let leader = await User.findById(group.leader).select(nameFields).exec();
|
||||
if (leader) group.leader = leader.toJSON({minimize: true});
|
||||
let groupJson = Group.toJSONCleanChat(group, user);
|
||||
|
||||
res.respond(200, group);
|
||||
if (groupJson.leader === user._id) {
|
||||
groupJson.purchased.plan = group.purchased.plan.toObject();
|
||||
}
|
||||
|
||||
// Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833
|
||||
let leader = await User.findById(groupJson.leader).select(nameFields).exec();
|
||||
if (leader) groupJson.leader = leader.toJSON({minimize: true});
|
||||
|
||||
res.respond(200, groupJson);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../../../libs/errors';
|
||||
import amzLib from '../../../libs/amazonPayments';
|
||||
import {
|
||||
@@ -12,6 +13,10 @@ import payments from '../../../libs/payments';
|
||||
import moment from 'moment';
|
||||
import { model as Coupon } from '../../../models/coupon';
|
||||
import { model as User } from '../../../models/user';
|
||||
import {
|
||||
model as Group,
|
||||
basicFields as basicGroupFields,
|
||||
} from '../../../models/group';
|
||||
import cc from 'coupon-code';
|
||||
|
||||
let api = {};
|
||||
@@ -34,6 +39,7 @@ api.verifyAccessToken = {
|
||||
if (!accessToken) throw new BadRequest('Missing req.body.access_token');
|
||||
|
||||
await amzLib.getTokenInfo(accessToken);
|
||||
|
||||
res.respond(200, {});
|
||||
},
|
||||
};
|
||||
@@ -164,6 +170,7 @@ api.subscribe = {
|
||||
let sub = req.body.subscription ? shared.content.subscriptionBlocks[req.body.subscription] : false;
|
||||
let coupon = req.body.coupon;
|
||||
let user = res.locals.user;
|
||||
let groupId = req.body.groupId;
|
||||
|
||||
if (!sub) throw new BadRequest(res.t('missingSubscriptionCode'));
|
||||
if (!billingAgreementId) throw new BadRequest('Missing req.body.billingAgreementId');
|
||||
@@ -213,6 +220,7 @@ api.subscribe = {
|
||||
paymentMethod: 'Amazon Payments',
|
||||
sub,
|
||||
headers: req.headers,
|
||||
groupId,
|
||||
});
|
||||
|
||||
res.respond(200);
|
||||
@@ -231,7 +239,32 @@ api.subscribeCancel = {
|
||||
middlewares: [authWithUrl],
|
||||
async handler (req, res) {
|
||||
let user = res.locals.user;
|
||||
let billingAgreementId = user.purchased.plan.customerId;
|
||||
let groupId = req.query.groupId;
|
||||
|
||||
let billingAgreementId;
|
||||
let planId;
|
||||
let lastBillingDate;
|
||||
|
||||
if (groupId) {
|
||||
let groupFields = basicGroupFields.concat(' purchased');
|
||||
let group = await Group.getGroup({user, groupId, populateLeader: false, groupFields});
|
||||
|
||||
if (!group) {
|
||||
throw new NotFound(res.t('groupNotFound'));
|
||||
}
|
||||
|
||||
if (!group.leader === user._id) {
|
||||
throw new NotAuthorized(res.t('onlyGroupLeaderCanManageSubscription'));
|
||||
}
|
||||
|
||||
billingAgreementId = group.purchased.plan.customerId;
|
||||
planId = group.purchased.plan.planId;
|
||||
lastBillingDate = group.purchased.plan.lastBillingDate;
|
||||
} else {
|
||||
billingAgreementId = user.purchased.plan.customerId;
|
||||
planId = user.purchased.plan.planId;
|
||||
lastBillingDate = user.purchased.plan.lastBillingDate;
|
||||
}
|
||||
|
||||
if (!billingAgreementId) throw new NotAuthorized(res.t('missingSubscription'));
|
||||
|
||||
@@ -245,12 +278,13 @@ api.subscribeCancel = {
|
||||
});
|
||||
}
|
||||
|
||||
let subscriptionBlock = shared.content.subscriptionBlocks[user.purchased.plan.planId];
|
||||
let subscriptionBlock = shared.content.subscriptionBlocks[planId];
|
||||
let subscriptionLength = subscriptionBlock.months * 30;
|
||||
|
||||
await payments.cancelSubscription({
|
||||
user,
|
||||
nextBill: moment(user.purchased.plan.lastBillingDate).add({ days: subscriptionLength }),
|
||||
groupId,
|
||||
nextBill: moment(lastBillingDate).add({ days: subscriptionLength }),
|
||||
paymentMethod: 'Amazon Payments',
|
||||
headers: req.headers,
|
||||
});
|
||||
|
||||
@@ -11,6 +11,10 @@ import cc from 'coupon-code';
|
||||
import Bluebird from 'bluebird';
|
||||
import { model as Coupon } from '../../../models/coupon';
|
||||
import { model as User } from '../../../models/user';
|
||||
import {
|
||||
model as Group,
|
||||
basicFields as basicGroupFields,
|
||||
} from '../../../models/group';
|
||||
import {
|
||||
authWithUrl,
|
||||
authWithSession,
|
||||
@@ -18,6 +22,7 @@ import {
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../../../libs/errors';
|
||||
|
||||
const BASE_URL = nconf.get('BASE_URL');
|
||||
@@ -178,6 +183,7 @@ api.subscribe = {
|
||||
let billingAgreement = await paypalBillingAgreementCreate(billingAgreementAttributes);
|
||||
|
||||
req.session.paypalBlock = req.query.sub;
|
||||
req.session.groupId = req.query.groupId;
|
||||
let link = _.find(billingAgreement.links, { rel: 'approval_url' }).href;
|
||||
res.redirect(link);
|
||||
},
|
||||
@@ -196,11 +202,15 @@ api.subscribeSuccess = {
|
||||
async handler (req, res) {
|
||||
let user = res.locals.user;
|
||||
let block = shared.content.subscriptionBlocks[req.session.paypalBlock];
|
||||
let groupId = req.session.groupId;
|
||||
|
||||
delete req.session.paypalBlock;
|
||||
delete req.session.groupId;
|
||||
|
||||
let result = await paypalBillingAgreementExecute(req.query.token, {});
|
||||
await payments.createSubscription({
|
||||
user,
|
||||
groupId,
|
||||
customerId: result.id,
|
||||
paymentMethod: 'Paypal',
|
||||
sub: block,
|
||||
@@ -223,8 +233,26 @@ api.subscribeCancel = {
|
||||
middlewares: [authWithUrl],
|
||||
async handler (req, res) {
|
||||
let user = res.locals.user;
|
||||
let customerId = user.purchased.plan.customerId;
|
||||
if (!user.purchased.plan.customerId) throw new NotAuthorized(res.t('missingSubscription'));
|
||||
let groupId = req.query.groupId;
|
||||
|
||||
let customerId;
|
||||
if (groupId) {
|
||||
let groupFields = basicGroupFields.concat(' purchased');
|
||||
let group = await Group.getGroup({user, groupId, populateLeader: false, groupFields});
|
||||
|
||||
if (!group) {
|
||||
throw new NotFound(res.t('groupNotFound'));
|
||||
}
|
||||
|
||||
if (!group.leader === user._id) {
|
||||
throw new NotAuthorized(res.t('onlyGroupLeaderCanManageSubscription'));
|
||||
}
|
||||
customerId = group.purchased.plan.customerId;
|
||||
} else {
|
||||
customerId = user.purchased.plan.customerId;
|
||||
}
|
||||
|
||||
if (!customerId) throw new NotAuthorized(res.t('missingSubscription'));
|
||||
|
||||
let customer = await paypalBillingAgreementGet(customerId);
|
||||
|
||||
@@ -236,6 +264,7 @@ api.subscribeCancel = {
|
||||
await paypalBillingAgreementCancel(customerId, { note: res.t('cancelingSubscription') });
|
||||
await payments.cancelSubscription({
|
||||
user,
|
||||
groupId,
|
||||
paymentMethod: 'Paypal',
|
||||
nextBill: nextBillingDate,
|
||||
});
|
||||
@@ -265,6 +294,13 @@ api.ipn = {
|
||||
let user = await User.findOne({ 'purchased.plan.customerId': req.body.recurring_payment_id });
|
||||
if (user) {
|
||||
await payments.cancelSubscription({ user, paymentMethod: 'Paypal' });
|
||||
return;
|
||||
}
|
||||
|
||||
let groupFields = basicGroupFields.concat(' purchased');
|
||||
let group = await Group.findOne({ 'purchased.plan.customerId': req.body.recurring_payment_id }).select(groupFields).exec();
|
||||
if (group) {
|
||||
await payments.cancelSubscription({ groupId: group._id, paymentMethod: 'Paypal' });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3,11 +3,16 @@ import shared from '../../../../common';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../../../libs/errors';
|
||||
import { model as Coupon } from '../../../models/coupon';
|
||||
import payments from '../../../libs/payments';
|
||||
import nconf from 'nconf';
|
||||
import { model as User } from '../../../models/user';
|
||||
import {
|
||||
model as Group,
|
||||
basicFields as basicGroupFields,
|
||||
} from '../../../models/group';
|
||||
import cc from 'coupon-code';
|
||||
import {
|
||||
authWithHeaders,
|
||||
@@ -41,6 +46,7 @@ api.checkout = {
|
||||
let user = res.locals.user;
|
||||
let gift = req.query.gift ? JSON.parse(req.query.gift) : undefined;
|
||||
let sub = req.query.sub ? shared.content.subscriptionBlocks[req.query.sub] : false;
|
||||
let groupId = req.query.groupId;
|
||||
let coupon;
|
||||
let response;
|
||||
|
||||
@@ -84,6 +90,7 @@ api.checkout = {
|
||||
paymentMethod: 'Stripe',
|
||||
sub,
|
||||
headers: req.headers,
|
||||
groupId,
|
||||
});
|
||||
} else {
|
||||
let method = 'buyGems';
|
||||
@@ -124,8 +131,26 @@ api.subscribeEdit = {
|
||||
middlewares: [authWithHeaders()],
|
||||
async handler (req, res) {
|
||||
let token = req.body.id;
|
||||
let groupId = req.body.groupId;
|
||||
let user = res.locals.user;
|
||||
let customerId = user.purchased.plan.customerId;
|
||||
let customerId;
|
||||
|
||||
// If we are buying a group subscription
|
||||
if (groupId) {
|
||||
let groupFields = basicGroupFields.concat(' purchased');
|
||||
let group = await Group.getGroup({user, groupId, populateLeader: false, groupFields});
|
||||
|
||||
if (!group) {
|
||||
throw new NotFound(res.t('groupNotFound'));
|
||||
}
|
||||
|
||||
if (!group.leader === user._id) {
|
||||
throw new NotAuthorized(res.t('onlyGroupLeaderCanManageSubscription'));
|
||||
}
|
||||
customerId = group.purchased.plan.customerId;
|
||||
} else {
|
||||
customerId = user.purchased.plan.customerId;
|
||||
}
|
||||
|
||||
if (!customerId) throw new NotAuthorized(res.t('missingSubscription'));
|
||||
if (!token) throw new BadRequest('Missing req.body.id');
|
||||
@@ -150,13 +175,39 @@ api.subscribeCancel = {
|
||||
middlewares: [authWithUrl],
|
||||
async handler (req, res) {
|
||||
let user = res.locals.user;
|
||||
if (!user.purchased.plan.customerId) throw new NotAuthorized(res.t('missingSubscription'));
|
||||
let groupId = req.query.groupId;
|
||||
let customerId;
|
||||
|
||||
let customer = await stripe.customers.retrieve(user.purchased.plan.customerId);
|
||||
await stripe.customers.del(user.purchased.plan.customerId);
|
||||
if (groupId) {
|
||||
let groupFields = basicGroupFields.concat(' purchased');
|
||||
let group = await Group.getGroup({user, groupId, populateLeader: false, groupFields});
|
||||
|
||||
if (!group) {
|
||||
throw new NotFound(res.t('groupNotFound'));
|
||||
}
|
||||
|
||||
if (!group.leader === user._id) {
|
||||
throw new NotAuthorized(res.t('onlyGroupLeaderCanManageSubscription'));
|
||||
}
|
||||
customerId = group.purchased.plan.customerId;
|
||||
} else {
|
||||
customerId = user.purchased.plan.customerId;
|
||||
}
|
||||
|
||||
if (!customerId) throw new NotAuthorized(res.t('missingSubscription'));
|
||||
|
||||
let customer = await stripe.customers.retrieve(customerId);
|
||||
|
||||
let subscription = customer.subscription;
|
||||
if (!subscription) {
|
||||
subscription = customer.subscriptions.data[0];
|
||||
}
|
||||
|
||||
await stripe.customers.del(customerId);
|
||||
await payments.cancelSubscription({
|
||||
user,
|
||||
nextBill: customer.subscription.current_period_end * 1000, // timestamp in seconds
|
||||
groupId,
|
||||
nextBill: subscription.current_period_end * 1000, // timestamp in seconds
|
||||
paymentMethod: 'Stripe',
|
||||
});
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ module.exports = {
|
||||
confirmOrderReference,
|
||||
closeOrderReference,
|
||||
confirmBillingAgreement,
|
||||
setBillingAgreementDetails,
|
||||
getBillingAgreementDetails,
|
||||
setBillingAgreementDetails,
|
||||
closeBillingAgreement,
|
||||
authorizeOnBillingAgreement,
|
||||
authorize,
|
||||
|
||||
@@ -7,6 +7,14 @@ import {
|
||||
import moment from 'moment';
|
||||
import { sendNotification as sendPushNotification } from './pushNotifications';
|
||||
import shared from '../../common' ;
|
||||
import {
|
||||
model as Group,
|
||||
basicFields as basicGroupFields,
|
||||
} from '../models/group';
|
||||
import {
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from './errors';
|
||||
|
||||
let api = {};
|
||||
|
||||
@@ -32,10 +40,35 @@ function _dateDiff (earlyDate, lateDate) {
|
||||
|
||||
api.createSubscription = async function createSubscription (data) {
|
||||
let recipient = data.gift ? data.gift.member : data.user;
|
||||
let plan = recipient.purchased.plan;
|
||||
let block = shared.content.subscriptionBlocks[data.gift ? data.gift.subscription.key : data.sub.key];
|
||||
let months = Number(block.months);
|
||||
let today = new Date();
|
||||
let plan;
|
||||
let group;
|
||||
let groupId;
|
||||
let itemPurchased = 'Subscription';
|
||||
let purchaseType = 'subscribe';
|
||||
|
||||
// If we are buying a group subscription
|
||||
if (data.groupId) {
|
||||
let groupFields = basicGroupFields.concat(' purchased');
|
||||
group = await Group.getGroup({user: data.user, groupId: data.groupId, populateLeader: false, groupFields});
|
||||
|
||||
if (!group) {
|
||||
throw new NotFound(shared.i18n.t('groupNotFound'));
|
||||
}
|
||||
|
||||
if (!group.leader === data.user._id) {
|
||||
throw new NotAuthorized(shared.i18n.t('onlyGroupLeaderCanManageSubscription'));
|
||||
}
|
||||
|
||||
recipient = group;
|
||||
itemPurchased = 'Group-Subscription';
|
||||
purchaseType = 'group-subscribe';
|
||||
groupId = group._id;
|
||||
}
|
||||
|
||||
plan = recipient.purchased.plan;
|
||||
|
||||
if (data.gift) {
|
||||
if (plan.customerId && !plan.dateTerminated) { // User has active plan
|
||||
@@ -79,7 +112,9 @@ api.createSubscription = async function createSubscription (data) {
|
||||
plan.consecutive.trinkets += perks;
|
||||
}
|
||||
|
||||
revealMysteryItems(recipient);
|
||||
if (recipient !== group) {
|
||||
revealMysteryItems(recipient);
|
||||
}
|
||||
|
||||
if (!data.gift) {
|
||||
txnEmail(data.user, 'subscription-begins');
|
||||
@@ -87,9 +122,10 @@ api.createSubscription = async function createSubscription (data) {
|
||||
|
||||
analytics.trackPurchase({
|
||||
uuid: data.user._id,
|
||||
itemPurchased: 'Subscription',
|
||||
groupId,
|
||||
itemPurchased,
|
||||
sku: `${data.paymentMethod.toLowerCase()}-subscription`,
|
||||
purchaseType: 'subscribe',
|
||||
purchaseType,
|
||||
paymentMethod: data.paymentMethod,
|
||||
quantity: 1,
|
||||
gift: Boolean(data.gift),
|
||||
@@ -97,7 +133,7 @@ api.createSubscription = async function createSubscription (data) {
|
||||
headers: data.headers,
|
||||
});
|
||||
|
||||
data.user.purchased.txnCount++;
|
||||
if (!group) data.user.purchased.txnCount++;
|
||||
|
||||
if (data.gift) {
|
||||
let message = `\`Hello ${data.gift.member.profile.name}, ${data.user.profile.name} has sent you ${shared.content.subscriptionBlocks[data.gift.subscription.key].months} months of subscription!\``;
|
||||
@@ -128,13 +164,39 @@ api.createSubscription = async function createSubscription (data) {
|
||||
}
|
||||
}
|
||||
|
||||
await data.user.save();
|
||||
if (group) {
|
||||
await group.save();
|
||||
} else {
|
||||
await data.user.save();
|
||||
}
|
||||
|
||||
if (data.gift) await data.gift.member.save();
|
||||
};
|
||||
|
||||
// Sets their subscription to be cancelled later
|
||||
api.cancelSubscription = async function cancelSubscription (data) {
|
||||
let plan = data.user.purchased.plan;
|
||||
let plan;
|
||||
let group;
|
||||
let cancelType = 'unsubscribe';
|
||||
let groupId;
|
||||
|
||||
// If we are buying a group subscription
|
||||
if (data.groupId) {
|
||||
let groupFields = basicGroupFields.concat(' purchased');
|
||||
group = await Group.getGroup({user: data.user, groupId: data.groupId, populateLeader: false, groupFields});
|
||||
|
||||
if (!group) {
|
||||
throw new NotFound(shared.i18n.t('groupNotFound'));
|
||||
}
|
||||
|
||||
if (!group.leader === data.user._id) {
|
||||
throw new NotAuthorized(shared.i18n.t('onlyGroupLeaderCanManageSubscription'));
|
||||
}
|
||||
plan = group.purchased.plan;
|
||||
} else {
|
||||
plan = data.user.purchased.plan;
|
||||
}
|
||||
|
||||
let now = moment();
|
||||
let remaining = data.nextBill ? moment(data.nextBill).diff(new Date(), 'days') : 30;
|
||||
let extraDays = Math.ceil(30.5 * plan.extraMonths);
|
||||
@@ -149,12 +211,22 @@ api.cancelSubscription = async function cancelSubscription (data) {
|
||||
|
||||
plan.extraMonths = 0; // clear extra time. If they subscribe again, it'll be recalculated from p.dateTerminated
|
||||
|
||||
await data.user.save();
|
||||
if (group) {
|
||||
await group.save();
|
||||
} else {
|
||||
await data.user.save();
|
||||
}
|
||||
|
||||
txnEmail(data.user, 'cancel-subscription');
|
||||
|
||||
analytics.track('unsubscribe', {
|
||||
if (group) {
|
||||
cancelType = 'group-unsubscribe';
|
||||
groupId = group._id;
|
||||
}
|
||||
|
||||
analytics.track(cancelType, {
|
||||
uuid: data.user._id,
|
||||
groupId,
|
||||
gaCategory: 'commerce',
|
||||
gaLabel: data.paymentMethod,
|
||||
paymentMethod: data.paymentMethod,
|
||||
|
||||
@@ -23,6 +23,9 @@ import pusher from '../libs/pusher';
|
||||
import {
|
||||
syncableAttrs,
|
||||
} from '../libs/taskManager';
|
||||
import {
|
||||
schema as SubscriptionPlanSchema,
|
||||
} from './subscriptionPlan';
|
||||
|
||||
const questScrolls = shared.content.quests;
|
||||
const Schema = mongoose.Schema;
|
||||
@@ -91,7 +94,9 @@ export let schema = new Schema({
|
||||
rewards: [{type: String, ref: 'Task'}],
|
||||
},
|
||||
purchased: {
|
||||
active: {type: Boolean, default: false},
|
||||
plan: {type: SubscriptionPlanSchema, default: () => {
|
||||
return {};
|
||||
}},
|
||||
},
|
||||
}, {
|
||||
strict: true,
|
||||
@@ -100,6 +105,10 @@ export let schema = new Schema({
|
||||
|
||||
schema.plugin(baseModel, {
|
||||
noSet: ['_id', 'balance', 'quest', 'memberCount', 'chat', 'challengeCount', 'tasksOrder', 'purchased'],
|
||||
private: ['purchased.plan'],
|
||||
toJSONTransform (plainObj, originalDoc) {
|
||||
if (plainObj.purchased) plainObj.purchased.active = originalDoc.purchased.plan && originalDoc.purchased.plan.customerId;
|
||||
},
|
||||
});
|
||||
|
||||
// A list of additional fields that cannot be updated (but can be set on creation)
|
||||
|
||||
32
website/server/models/subscriptionPlan.js
Normal file
32
website/server/models/subscriptionPlan.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import mongoose from 'mongoose';
|
||||
import baseModel from '../libs/baseModel';
|
||||
|
||||
export let schema = new mongoose.Schema({
|
||||
planId: String,
|
||||
paymentMethod: String, // enum: ['Paypal','Stripe', 'Gift', 'Amazon Payments', '']}
|
||||
customerId: String, // Billing Agreement Id in case of Amazon Payments
|
||||
dateCreated: Date,
|
||||
dateTerminated: Date,
|
||||
dateUpdated: Date,
|
||||
extraMonths: {type: Number, default: 0},
|
||||
gemsBought: {type: Number, default: 0},
|
||||
mysteryItems: {type: Array, default: () => []},
|
||||
lastBillingDate: Date, // Used only for Amazon Payments to keep track of billing date
|
||||
consecutive: {
|
||||
count: {type: Number, default: 0},
|
||||
offset: {type: Number, default: 0}, // when gifted subs, offset++ for each month. offset-- each new-month (cron). count doesn't ++ until offset==0
|
||||
gemCapExtra: {type: Number, default: 0},
|
||||
trinkets: {type: Number, default: 0},
|
||||
},
|
||||
}, {
|
||||
strict: true,
|
||||
minimize: false, // So empty objects are returned
|
||||
_id: false,
|
||||
});
|
||||
|
||||
schema.plugin(baseModel, {
|
||||
noSet: ['_id'],
|
||||
timestamps: false,
|
||||
});
|
||||
|
||||
export let model = mongoose.model('SubscriptionPlan', schema);
|
||||
@@ -8,6 +8,9 @@ import { schema as WebhookSchema } from '../webhook';
|
||||
import {
|
||||
schema as UserNotificationSchema,
|
||||
} from '../userNotification';
|
||||
import {
|
||||
schema as SubscriptionPlanSchema,
|
||||
} from '../subscriptionPlan';
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
|
||||
@@ -144,24 +147,9 @@ let schema = new Schema({
|
||||
}},
|
||||
txnCount: {type: Number, default: 0},
|
||||
mobileChat: Boolean,
|
||||
plan: {
|
||||
planId: String,
|
||||
paymentMethod: String, // enum: ['Paypal','Stripe', 'Gift', 'Amazon Payments', '']}
|
||||
customerId: String, // Billing Agreement Id in case of Amazon Payments
|
||||
dateCreated: Date,
|
||||
dateTerminated: Date,
|
||||
dateUpdated: Date,
|
||||
extraMonths: {type: Number, default: 0},
|
||||
gemsBought: {type: Number, default: 0},
|
||||
mysteryItems: {type: Array, default: () => []},
|
||||
lastBillingDate: Date, // Used only for Amazon Payments to keep track of billing date
|
||||
consecutive: {
|
||||
count: {type: Number, default: 0},
|
||||
offset: {type: Number, default: 0}, // when gifted subs, offset++ for each month. offset-- each new-month (cron). count doesn't ++ until offset==0
|
||||
gemCapExtra: {type: Number, default: 0},
|
||||
trinkets: {type: Number, default: 0},
|
||||
},
|
||||
},
|
||||
plan: {type: SubscriptionPlanSchema, default: () => {
|
||||
return {};
|
||||
}},
|
||||
},
|
||||
|
||||
flags: {
|
||||
|
||||
Reference in New Issue
Block a user