import moment from 'moment'; import stripeModule from 'stripe'; import nconf from 'nconf'; import * as sender from '../../../../../../website/server/libs/email'; import api from '../../../../../../website/server/libs/payments/payments'; import amzLib from '../../../../../../website/server/libs/payments/amazon'; import paypalPayments from '../../../../../../website/server/libs/payments/paypal'; import stripePayments from '../../../../../../website/server/libs/payments/stripe'; import { model as User } from '../../../../../../website/server/models/user'; import { model as Group } from '../../../../../../website/server/models/group'; import { generateGroup, } from '../../../../../helpers/api-unit.helper'; import i18n from '../../../../../../website/common/script/i18n'; describe('Purchasing a group plan for group', () => { const EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_GOOGLE = 'Google_subscription'; const EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_IOS = 'iOS_subscription'; const EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL = 'normal_subscription'; const EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NONE = 'no_subscription'; let plan; let group; let user; let data; const stripe = stripeModule('test'); const groupLeaderName = 'sender'; const groupName = 'test group'; beforeEach(async () => { user = new User(); user.profile.name = groupLeaderName; await user.save(); group = generateGroup({ name: groupName, type: 'guild', privacy: 'private', leader: user._id, }); await group.save(); user.guilds.push(group._id); await user.save(); data = { user, sub: { key: 'basic_3mo', }, customerId: 'customer-id', paymentMethod: 'Payment Method', headers: { 'x-client': 'habitica-web', 'user-agent': '', }, }; plan = { planId: 'basic_3mo', customerId: 'customer-id', dateUpdated: new Date(), gemsBought: 0, paymentMethod: 'paymentMethod', extraMonths: 0, dateTerminated: null, lastBillingDate: new Date(), dateCreated: new Date(), mysteryItems: [], consecutive: { trinkets: 0, offset: 0, gemCapExtra: 0, }, }; const subscriptionId = 'subId'; sinon.stub(stripe.customers, 'del').resolves({}); const currentPeriodEndTimeStamp = moment().add(3, 'months').unix(); sinon.stub(stripe.customers, 'retrieve') .resolves({ subscriptions: { data: [{ id: subscriptionId, current_period_end: currentPeriodEndTimeStamp, }], // eslint-disable-line camelcase }, }); stripePayments.setStripeApi(stripe); sandbox.stub(sender, 'sendTxn'); }); afterEach(() => { stripe.customers.del.restore(); stripe.customers.retrieve.restore(); sender.sendTxn.restore(); }); it('creates a group plan', async () => { expect(group.purchased.plan.planId).to.not.exist; data.groupId = group._id; await api.createSubscription(data); const updatedGroup = await Group.findById(group._id).exec(); expect(updatedGroup.purchased.plan.planId).to.eql('basic_3mo'); expect(updatedGroup.purchased.plan.customerId).to.eql('customer-id'); expect(updatedGroup.purchased.plan.dateUpdated).to.exist; expect(updatedGroup.purchased.plan.gemsBought).to.eql(0); expect(updatedGroup.purchased.plan.paymentMethod).to.eql('Payment Method'); expect(updatedGroup.purchased.plan.extraMonths).to.eql(0); expect(updatedGroup.purchased.plan.dateTerminated).to.eql(null); expect(updatedGroup.purchased.plan.lastBillingDate).to.not.exist; expect(updatedGroup.purchased.plan.dateCreated).to.exist; }); it('does not create a group plan for a public guild', async () => { const publicGroup = generateGroup({ name: groupName, type: 'guild', privacy: 'public', leader: user._id, }); await publicGroup.save(); expect(publicGroup.purchased.plan.planId).to.not.exist; data.groupId = publicGroup._id; await expect(api.createSubscription(data)) .to.eventually.be.rejected.and.to.eql({ httpCode: 401, name: 'NotAuthorized', message: i18n.t('onlyPrivateGuildsCanUpgrade'), }); const updatedGroup = await Group.findById(publicGroup._id).exec(); expect(updatedGroup.purchased.plan.planId).to.not.exist; }); it('sends an email', async () => { expect(group.purchased.plan.planId).to.not.exist; data.groupId = group._id; await api.createSubscription(data); expect(sender.sendTxn).to.be.calledWith(user, 'group-subscription-begins'); }); it('sets extraMonths if plan has dateTerminated date', async () => { group.purchased.plan = plan; group.purchased.plan.dateTerminated = moment(new Date()).add(2, 'months'); await group.save(); expect(group.purchased.plan.extraMonths).to.eql(0); data.groupId = group._id; await api.createSubscription(data); const updatedGroup = await Group.findById(group._id).exec(); expect(updatedGroup.purchased.plan.extraMonths).to.within(1.9, 2); }); it('does not set negative extraMonths if plan has past dateTerminated date', async () => { group.purchased.plan = plan; group.purchased.plan.dateTerminated = moment(new Date()).subtract(2, 'months'); await group.save(); expect(group.purchased.plan.extraMonths).to.eql(0); data.groupId = group._id; await api.createSubscription(data); const updatedGroup = await Group.findById(group._id).exec(); expect(updatedGroup.purchased.plan.extraMonths).to.eql(0); }); it('grants all members of a group a subscription', async () => { expect(group.purchased.plan.planId).to.not.exist; data.groupId = group._id; await api.createSubscription(data); const updatedLeader = await User.findById(user._id).exec(); expect(updatedLeader.purchased.plan.planId).to.eql('group_plan_auto'); expect(updatedLeader.purchased.plan.customerId).to.eql('group-plan'); expect(updatedLeader.purchased.plan.dateUpdated).to.exist; expect(updatedLeader.purchased.plan.gemsBought).to.eql(0); expect(updatedLeader.purchased.plan.paymentMethod).to.eql('Group Plan'); expect(updatedLeader.purchased.plan.extraMonths).to.eql(0); expect(updatedLeader.purchased.plan.dateTerminated).to.eql(null); expect(updatedLeader.purchased.plan.lastBillingDate).to.not.exist; expect(updatedLeader.purchased.plan.dateCreated).to.exist; expect(updatedLeader.items.mounts['Jackalope-RoyalPurple']).to.be.true; }); it('sends an email to member of group who was not a subscriber', async () => { const recipient = new User(); recipient.profile.name = 'recipient'; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); expect(sender.sendTxn).to.be.calledThrice; const recipientCall = sender.sendTxn.getCalls().find(call => { const isRecipient = call.args[0]._id === recipient._id; const isJoin = call.args[1] === 'group-member-join'; return isRecipient && isJoin; }); expect(recipientCall.args[0]._id).to.equal(recipient._id); expect(recipientCall.args[1]).to.equal('group-member-join'); expect(recipientCall.args[2]).to.eql([ { name: 'LEADER', content: user.profile.name }, { name: 'GROUP_NAME', content: group.name }, { name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NONE }, ]); // confirm that the other email sent is appropriate: const leaderCall = sender.sendTxn.getCalls().find(call => { const isLeader = call.args[0]._id === group.leader; const isSubscriptionBegin = call.args[1] === 'group-subscription-begins'; return isLeader && isSubscriptionBegin; }); expect(leaderCall.args[0]._id).to.equal(group.leader); expect(leaderCall.args[1]).to.equal('group-subscription-begins'); }); it('sends one email to subscribed member of group, stating subscription is cancelled (Stripe)', async () => { const recipient = new User(); recipient.profile.name = 'recipient'; plan.key = 'basic_earned'; plan.paymentMethod = stripePayments.constants.PAYMENT_METHOD; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); expect(sender.sendTxn).to.be.calledThrice; const recipientCall = sender.sendTxn.getCalls().find(call => { const isRecipient = call.args[0]._id === recipient._id; const isJoin = call.args[1] === 'group-member-join'; return isRecipient && isJoin; }); expect(recipientCall.args[0]._id).to.equal(recipient._id); expect(recipientCall.args[1]).to.equal('group-member-join'); expect(recipientCall.args[2]).to.eql([ { name: 'LEADER', content: user.profile.name }, { name: 'GROUP_NAME', content: group.name }, { name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL }, ]); // confirm that the other email sent is not a cancel-subscription email: const leaderCall = sender.sendTxn.getCalls().find(call => { const isLeader = call.args[0]._id === group.leader; const isSubscriptionBegin = call.args[1] === 'group-subscription-begins'; return isLeader && isSubscriptionBegin; }); expect(leaderCall.args[0]._id).to.equal(group.leader); expect(leaderCall.args[1]).to.equal('group-subscription-begins'); }); it('sends one email to subscribed member of group, stating subscription is cancelled (Amazon)', async () => { sinon.stub(amzLib, 'getBillingAgreementDetails') .resolves({ BillingAgreementDetails: { BillingAgreementStatus: { State: 'Closed' }, }, }); const recipient = new User(); recipient.profile.name = 'recipient'; plan.planId = 'basic_earned'; plan.paymentMethod = amzLib.constants.PAYMENT_METHOD; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); expect(sender.sendTxn).to.be.calledThrice; const recipientCall = sender.sendTxn.getCalls().find(call => { const isRecipient = call.args[0]._id === recipient._id; const isJoin = call.args[1] === 'group-member-join'; return isRecipient && isJoin; }); expect(recipientCall.args[0]._id).to.equal(recipient._id); expect(recipientCall.args[1]).to.equal('group-member-join'); expect(recipientCall.args[2]).to.eql([ { name: 'LEADER', content: user.profile.name }, { name: 'GROUP_NAME', content: group.name }, { name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL }, ]); // confirm that the other email sent is not a cancel-subscription email: const leaderCall = sender.sendTxn.getCalls().find(call => { const isLeader = call.args[0]._id === group.leader; const isSubscriptionBegin = call.args[1] === 'group-subscription-begins'; return isLeader && isSubscriptionBegin; }); expect(leaderCall.args[0]._id).to.equal(group.leader); expect(leaderCall.args[1]).to.equal('group-subscription-begins'); amzLib.getBillingAgreementDetails.restore(); }); it('sends one email to subscribed member of group, stating subscription is cancelled (PayPal)', async () => { sinon.stub(paypalPayments, 'paypalBillingAgreementCancel').resolves({}); sinon.stub(paypalPayments, 'paypalBillingAgreementGet') .resolves({ agreement_details: { // eslint-disable-line camelcase next_billing_date: moment().add(3, 'months').toDate(), // eslint-disable-line camelcase cycles_completed: 1, // eslint-disable-line camelcase }, }); const recipient = new User(); recipient.profile.name = 'recipient'; plan.planId = 'basic_earned'; plan.paymentMethod = paypalPayments.constants.PAYMENT_METHOD; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); expect(sender.sendTxn).to.be.calledThrice; const recipientCall = sender.sendTxn.getCalls().find(call => { const isRecipient = call.args[0]._id === recipient._id; const isJoin = call.args[1] === 'group-member-join'; return isRecipient && isJoin; }); expect(recipientCall.args[0]._id).to.equal(recipient._id); expect(recipientCall.args[1]).to.equal('group-member-join'); expect(recipientCall.args[2]).to.eql([ { name: 'LEADER', content: user.profile.name }, { name: 'GROUP_NAME', content: group.name }, { name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_NORMAL }, ]); // confirm that the other email sent is not a cancel-subscription email: const leaderCall = sender.sendTxn.getCalls().find(call => { const isLeader = call.args[0]._id === group.leader; const isSubscriptionBegin = call.args[1] === 'group-subscription-begins'; return isLeader && isSubscriptionBegin; }); expect(leaderCall.args[0]._id).to.equal(group.leader); expect(leaderCall.args[1]).to.equal('group-subscription-begins'); paypalPayments.paypalBillingAgreementGet.restore(); paypalPayments.paypalBillingAgreementCancel.restore(); }); it('sends appropriate emails when subscribed member of group must manually cancel recurring Android subscription', async () => { const TECH_ASSISTANCE_EMAIL = nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL'); plan.customerId = 'random'; plan.paymentMethod = api.constants.GOOGLE_PAYMENT_METHOD; const recipient = new User(); recipient.profile.name = 'recipient'; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); expect(sender.sendTxn).to.have.callCount(4); const adminUserSubscriptionDetails = sender.sendTxn.args.find(sendTxnArgs => { const emailType = sendTxnArgs[1]; return emailType === 'admin-user-subscription-details'; }); expect(adminUserSubscriptionDetails).to.exist; expect(adminUserSubscriptionDetails[0].email).to.equal(TECH_ASSISTANCE_EMAIL); const groupMemberJoinOne = sender.sendTxn.args.find(sendTxnArgs => { const emailType = sendTxnArgs[1]; const emailRecipient = sendTxnArgs[0]; return emailType === 'group-member-join' && emailRecipient._id === recipient._id; }); expect(groupMemberJoinOne).to.exist; expect(groupMemberJoinOne[0]._id).to.equal(recipient._id); expect(groupMemberJoinOne[2]).to.eql([ { name: 'LEADER', content: groupLeaderName }, { name: 'GROUP_NAME', content: groupName }, { name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_GOOGLE }, ]); const groupMemberJoinTwo = sender.sendTxn.args.find(sendTxnArgs => { const emailType = sendTxnArgs[1]; const emailRecipient = sendTxnArgs[0]; return emailType === 'group-member-join' && emailRecipient._id === group.leader; }); expect(groupMemberJoinTwo).to.exist; expect(groupMemberJoinTwo[0]._id).to.equal(group.leader); const groupSubscriptionBegins = sender.sendTxn.args.find(sendTxnArgs => { const emailType = sendTxnArgs[1]; return emailType === 'group-subscription-begins'; }); expect(groupSubscriptionBegins).to.exist; expect(groupSubscriptionBegins[0]._id).to.equal(group.leader); }); it('sends appropriate emails when subscribed member of group must manually cancel recurring iOS subscription', async () => { const TECH_ASSISTANCE_EMAIL = nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL'); plan.customerId = 'random'; plan.paymentMethod = api.constants.IOS_PAYMENT_METHOD; const recipient = new User(); recipient.profile.name = 'recipient'; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); expect(sender.sendTxn).to.have.callCount(4); const adminUserSubscriptionDetails = sender.sendTxn.args.find(sendTxnArgs => { const emailType = sendTxnArgs[1]; return emailType === 'admin-user-subscription-details'; }); expect(adminUserSubscriptionDetails).to.exist; expect(adminUserSubscriptionDetails[0].email).to.equal(TECH_ASSISTANCE_EMAIL); const groupMemberJoinOne = sender.sendTxn.args.find(sendTxnArgs => { const emailType = sendTxnArgs[1]; const emailRecipient = sendTxnArgs[0]; return emailType === 'group-member-join' && emailRecipient._id === recipient._id; }); expect(groupMemberJoinOne).to.exist; expect(groupMemberJoinOne[0]._id).to.equal(recipient._id); expect(groupMemberJoinOne[2]).to.eql([ { name: 'LEADER', content: groupLeaderName }, { name: 'GROUP_NAME', content: groupName }, { name: 'PREVIOUS_SUBSCRIPTION_TYPE', content: EMAIL_TEMPLATE_SUBSCRIPTION_TYPE_IOS }, ]); const groupMemberJoinTwo = sender.sendTxn.args.find(sendTxnArgs => { const emailType = sendTxnArgs[1]; const emailRecipient = sendTxnArgs[0]; return emailType === 'group-member-join' && emailRecipient._id === group.leader; }); expect(groupMemberJoinTwo).to.exist; expect(groupMemberJoinTwo[0]._id).to.equal(group.leader); const groupSubscriptionBegins = sender.sendTxn.args.find(sendTxnArgs => { const emailType = sendTxnArgs[1]; return emailType === 'group-subscription-begins'; }); expect(groupSubscriptionBegins).to.exist; expect(groupSubscriptionBegins[0]._id).to.equal(group.leader); }); it('adds months to members with existing gift subscription', async () => { const recipient = new User(); recipient.profile.name = 'recipient'; recipient.purchased.plan = plan; recipient.guilds.push(group._id); plan.planId = 'basic_earned'; plan.paymentMethod = 'paymentMethod'; data.gift = { member: recipient, subscription: { key: 'basic_earned', months: 1, }, }; await api.createSubscription(data); await recipient.save(); data.gift = undefined; data.groupId = group._id; await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.planId).to.eql('group_plan_auto'); expect(updatedUser.purchased.plan.customerId).to.eql('group-plan'); expect(updatedUser.purchased.plan.dateUpdated).to.exist; expect(updatedUser.purchased.plan.gemsBought).to.eql(0); expect(updatedUser.purchased.plan.paymentMethod).to.eql('Group Plan'); expect(updatedUser.purchased.plan.extraMonths).to.within(1, 3); expect(updatedUser.purchased.plan.dateTerminated).to.eql(null); expect(updatedUser.purchased.plan.lastBillingDate).to.not.exist; expect(updatedUser.purchased.plan.dateCreated).to.exist; }); it('adds months to members with existing multi-month gift subscription', async () => { const recipient = new User(); recipient.profile.name = 'recipient'; recipient.purchased.plan = plan; recipient.guilds.push(group._id); data.gift = { member: recipient, subscription: { key: 'basic_3mo', months: 3, }, }; await api.createSubscription(data); await recipient.save(); data.gift = undefined; data.groupId = group._id; await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.planId).to.eql('group_plan_auto'); expect(updatedUser.purchased.plan.customerId).to.eql('group-plan'); expect(updatedUser.purchased.plan.dateUpdated).to.exist; expect(updatedUser.purchased.plan.gemsBought).to.eql(0); expect(updatedUser.purchased.plan.paymentMethod).to.eql('Group Plan'); expect(updatedUser.purchased.plan.extraMonths).to.within(3, 5); expect(updatedUser.purchased.plan.dateTerminated).to.eql(null); expect(updatedUser.purchased.plan.lastBillingDate).to.not.exist; expect(updatedUser.purchased.plan.dateCreated).to.exist; }); it('adds months to members with existing recurring subscription (Stripe)', async () => { const recipient = new User(); recipient.profile.name = 'recipient'; plan.key = 'basic_earned'; plan.paymentMethod = stripePayments.constants.PAYMENT_METHOD; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.extraMonths).to.within(2, 3); }); it('adds months to members with existing recurring subscription (Amazon)', async () => { sinon.stub(amzLib, 'getBillingAgreementDetails') .resolves({ BillingAgreementDetails: { BillingAgreementStatus: { State: 'Closed' }, }, }); const recipient = new User(); recipient.profile.name = 'recipient'; plan.planId = 'basic_earned'; plan.paymentMethod = amzLib.constants.PAYMENT_METHOD; plan.lastBillingDate = moment().add(3, 'months'); recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.extraMonths).to.within(3, 5); }); it('adds months to members with existing recurring subscription (Paypal)', async () => { sinon.stub(paypalPayments, 'paypalBillingAgreementCancel').resolves({}); sinon.stub(paypalPayments, 'paypalBillingAgreementGet') .resolves({ agreement_details: { // eslint-disable-line camelcase next_billing_date: moment().add(3, 'months').toDate(), // eslint-disable-line camelcase cycles_completed: 1, // eslint-disable-line camelcase }, }); const recipient = new User(); recipient.profile.name = 'recipient'; plan.planId = 'basic_earned'; plan.paymentMethod = paypalPayments.constants.PAYMENT_METHOD; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.extraMonths).to.within(2, 3); paypalPayments.paypalBillingAgreementGet.restore(); paypalPayments.paypalBillingAgreementCancel.restore(); }); it('adds months to members with existing recurring subscription (Android)'); it('adds months to members with existing recurring subscription (iOS)'); it('adds months to members who already cancelled but not yet terminated recurring subscription', async () => { const recipient = new User(); recipient.profile.name = 'recipient'; plan.key = 'basic_earned'; plan.paymentMethod = stripePayments.constants.PAYMENT_METHOD; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await recipient.cancelSubscription(); await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.extraMonths).to.within(2, 3); }); it('adds months to members who already cancelled but not yet terminated group plan subscription', async () => { const recipient = new User(); recipient.profile.name = 'recipient'; plan.key = 'basic_earned'; plan.paymentMethod = api.constants.GROUP_PLAN_PAYMENT_METHOD; plan.extraMonths = 2.94; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await recipient.cancelSubscription(); await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.extraMonths).to.within(3, 4); }); it('resets date terminated if user has old subscription', async () => { const recipient = new User(); recipient.profile.name = 'recipient'; plan.key = 'basic_earned'; plan.paymentMethod = stripePayments.constants.PAYMENT_METHOD; plan.dateTerminated = moment().subtract(1, 'days').toDate(); recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.dateTerminated).to.not.exist; }); it('adds months to members with existing recurring subscription and includes existing extraMonths', async () => { const recipient = new User(); recipient.profile.name = 'recipient'; plan.key = 'basic_earned'; plan.paymentMethod = stripePayments.constants.PAYMENT_METHOD; plan.extraMonths = 5; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.extraMonths).to.within(7, 9); }); it('adds months to members with existing recurring subscription and ignores existing negative extraMonths', async () => { const recipient = new User(); recipient.profile.name = 'recipient'; plan.key = 'basic_earned'; plan.paymentMethod = stripePayments.constants.PAYMENT_METHOD; plan.extraMonths = -5; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.extraMonths).to.within(2, 3); }); it('does not override gemsBought, mysteryItems, dateCreated, and consective fields', async () => { const planCreatedDate = moment().toDate(); const mysteryItem = { title: 'item' }; const mysteryItems = [mysteryItem]; const consecutive = { trinkets: 4, gemCapExtra: 20, offset: 1, count: 13, }; const recipient = new User(); recipient.profile.name = 'recipient'; plan.key = 'basic_earned'; plan.gemsBought = 3; plan.dateCreated = planCreatedDate; plan.mysteryItems = mysteryItems; plan.consecutive = consecutive; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.gemsBought).to.equal(3); expect(updatedUser.purchased.plan.mysteryItems[0]).to.eql(mysteryItem); expect(updatedUser.purchased.plan.consecutive.count).to.equal(consecutive.count); expect(updatedUser.purchased.plan.consecutive.offset).to.equal(consecutive.offset); expect(updatedUser.purchased.plan.consecutive.gemCapExtra).to.equal(consecutive.gemCapExtra); expect(updatedUser.purchased.plan.consecutive.trinkets).to.equal(consecutive.trinkets); expect(updatedUser.purchased.plan.dateCreated).to.eql(planCreatedDate); }); it('does not modify a user with a group subscription when they join another group', async () => { const recipient = new User(); recipient.profile.name = 'recipient'; plan.key = 'basic_earned'; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); let updatedUser = await User.findById(recipient._id).exec(); const firstDateCreated = updatedUser.purchased.plan.dateCreated; const extraMonthsBeforeSecond = updatedUser.purchased.plan.extraMonths; const group2 = generateGroup({ name: 'test group2', type: 'guild', privacy: 'private', leader: user._id, }); data.groupId = group2._id; await group2.save(); recipient.guilds.push(group2._id); await recipient.save(); user.guilds.push(group2._id); await user.save(); await api.createSubscription(data); updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.planId).to.eql('group_plan_auto'); expect(updatedUser.purchased.plan.customerId).to.eql('group-plan'); expect(updatedUser.purchased.plan.dateUpdated).to.exist; expect(updatedUser.purchased.plan.gemsBought).to.eql(0); expect(updatedUser.purchased.plan.paymentMethod).to.eql('Group Plan'); expect(updatedUser.purchased.plan.extraMonths).to.eql(extraMonthsBeforeSecond); expect(updatedUser.purchased.plan.dateTerminated).to.eql(null); expect(updatedUser.purchased.plan.lastBillingDate).to.not.exist; expect(updatedUser.purchased.plan.dateCreated).to.eql(firstDateCreated); }); it('does not remove a user who is in two groups plans and leaves one', async () => { const recipient = new User(); recipient.profile.name = 'recipient'; plan.key = 'basic_earned'; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); let updatedUser = await User.findById(recipient._id).exec(); const firstDateCreated = updatedUser.purchased.plan.dateCreated; const extraMonthsBeforeSecond = updatedUser.purchased.plan.extraMonths; const group2 = generateGroup({ name: 'test group2', type: 'guild', privacy: 'private', leader: user._id, }); data.groupId = group2._id; await group2.save(); recipient.guilds.push(group2._id); await recipient.save(); user.guilds.push(group2._id); await user.save(); await api.createSubscription(data); const updatedGroup = await Group.findById(group._id).exec(); updatedGroup.memberCount = 2; await updatedGroup.save(); await updatedGroup.leave(recipient); updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.planId).to.eql('group_plan_auto'); expect(updatedUser.purchased.plan.customerId).to.eql('group-plan'); expect(updatedUser.purchased.plan.dateUpdated).to.exist; expect(updatedUser.purchased.plan.gemsBought).to.eql(0); expect(updatedUser.purchased.plan.paymentMethod).to.eql('Group Plan'); expect(updatedUser.purchased.plan.extraMonths).to.eql(extraMonthsBeforeSecond); expect(updatedUser.purchased.plan.dateTerminated).to.eql(null); expect(updatedUser.purchased.plan.lastBillingDate).to.not.exist; expect(updatedUser.purchased.plan.dateCreated).to.eql(firstDateCreated); }); it('does not modify a user with an unlimited subscription', async () => { plan.key = 'basic_earned'; plan.customerId = api.constants.UNLIMITED_CUSTOMER_ID; const recipient = new User(); recipient.profile.name = 'recipient'; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.planId).to.eql('basic_3mo'); expect(updatedUser.purchased.plan.customerId).to.eql(api.constants.UNLIMITED_CUSTOMER_ID); expect(updatedUser.purchased.plan.dateUpdated).to.exist; expect(updatedUser.purchased.plan.gemsBought).to.eql(0); expect(updatedUser.purchased.plan.paymentMethod).to.eql('paymentMethod'); expect(updatedUser.purchased.plan.extraMonths).to.eql(0); expect(updatedUser.purchased.plan.dateTerminated).to.eql(null); expect(updatedUser.purchased.plan.lastBillingDate).to.exist; expect(updatedUser.purchased.plan.dateCreated).to.exist; }); it('does not modify a user with an Android subscription', async () => { plan.customerId = 'random'; plan.paymentMethod = api.constants.GOOGLE_PAYMENT_METHOD; const recipient = new User(); recipient.profile.name = 'recipient'; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.planId).to.eql('basic_3mo'); expect(updatedUser.purchased.plan.customerId).to.eql('random'); expect(updatedUser.purchased.plan.dateUpdated).to.exist; expect(updatedUser.purchased.plan.gemsBought).to.eql(0); expect(updatedUser.purchased.plan.paymentMethod).to.eql(api.constants.GOOGLE_PAYMENT_METHOD); expect(updatedUser.purchased.plan.extraMonths).to.eql(0); expect(updatedUser.purchased.plan.dateTerminated).to.eql(null); expect(updatedUser.purchased.plan.lastBillingDate).to.exist; expect(updatedUser.purchased.plan.dateCreated).to.exist; }); it('does not modify a user with an iOS subscription', async () => { plan.customerId = 'random'; plan.paymentMethod = api.constants.IOS_PAYMENT_METHOD; const recipient = new User(); recipient.profile.name = 'recipient'; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.planId).to.eql('basic_3mo'); expect(updatedUser.purchased.plan.customerId).to.eql('random'); expect(updatedUser.purchased.plan.dateUpdated).to.exist; expect(updatedUser.purchased.plan.gemsBought).to.eql(0); expect(updatedUser.purchased.plan.paymentMethod).to.eql(api.constants.IOS_PAYMENT_METHOD); expect(updatedUser.purchased.plan.extraMonths).to.eql(0); expect(updatedUser.purchased.plan.dateTerminated).to.eql(null); expect(updatedUser.purchased.plan.lastBillingDate).to.exist; expect(updatedUser.purchased.plan.dateCreated).to.exist; }); it('updates a user with a cancelled but active group subscription', async () => { plan.key = 'basic_earned'; plan.customerId = api.constants.GROUP_PLAN_CUSTOMER_ID; plan.dateTerminated = moment().add(1, 'months'); const recipient = new User(); recipient.profile.name = 'recipient'; recipient.purchased.plan = plan; recipient.guilds.push(group._id); await recipient.save(); data.groupId = group._id; await api.createSubscription(data); const updatedUser = await User.findById(recipient._id).exec(); expect(updatedUser.purchased.plan.planId).to.eql('group_plan_auto'); expect(updatedUser.purchased.plan.customerId).to.eql(api.constants.GROUP_PLAN_CUSTOMER_ID); expect(updatedUser.purchased.plan.dateUpdated).to.exist; expect(updatedUser.purchased.plan.gemsBought).to.eql(0); expect(updatedUser.purchased.plan.paymentMethod).to.eql('Group Plan'); expect(updatedUser.purchased.plan.extraMonths).to.within(0, 2); expect(updatedUser.purchased.plan.dateTerminated).to.eql(null); expect(updatedUser.purchased.plan.lastBillingDate).to.not.exist; expect(updatedUser.purchased.plan.dateCreated).to.exist; }); });