From 9b1a7268753f5536749632e771c005b3474bb6ff Mon Sep 17 00:00:00 2001 From: SabreCat Date: Wed, 2 Nov 2022 09:00:29 -0500 Subject: [PATCH] Revert "feat(subs): allow upgrade/downgrade on iOS" This reverts commit 27440772f0541e0db97cdb83c21b46815ff37f5a. --- test/api/unit/libs/payments/apple.test.js | 103 +-------- test/api/unit/libs/payments/payments.test.js | 213 +----------------- website/server/libs/payments/apple.js | 36 +-- website/server/libs/payments/subscriptions.js | 10 +- 4 files changed, 16 insertions(+), 346 deletions(-) diff --git a/test/api/unit/libs/payments/apple.test.js b/test/api/unit/libs/payments/apple.test.js index 1c6c112b7a..1aefe2cb51 100644 --- a/test/api/unit/libs/payments/apple.test.js +++ b/test/api/unit/libs/payments/apple.test.js @@ -218,7 +218,6 @@ describe('Apple Payments', () => { headers = {}; receipt = `{"token": "${token}"}`; nextPaymentProcessing = moment.utc().add({ days: 2 }); - user = new User(); iapSetupStub = sinon.stub(iap, 'setup') .resolves(); @@ -299,7 +298,6 @@ describe('Apple Payments', () => { expirationDate: moment.utc().add({ day: 1 }).toDate(), productId: option.sku, transactionId: token, - originalTransactionId: token, }]); sub = common.content.subscriptionBlocks[option.subKey]; @@ -323,111 +321,12 @@ describe('Apple Payments', () => { nextPaymentProcessing, }); }); - if (option !== subOptions[3]) { - const newOption = subOptions[3]; - it(`upgrades a subscription from ${option.sku} to ${newOption.sku}`, async () => { - const oldSub = common.content.subscriptionBlocks[option.subKey]; - user.profile.name = 'sender'; - user.purchased.plan.paymentMethod = applePayments.constants.PAYMENT_METHOD_APPLE; - user.purchased.plan.customerId = token; - user.purchased.plan.planId = option.subKey; - user.purchased.plan.additionalData = receipt; - iap.getPurchaseData.restore(); - iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData') - .returns([{ - expirationDate: moment.utc().add({ day: 2 }).toDate(), - productId: newOption.sku, - transactionId: `${token}new`, - originalTransactionId: token, - }]); - sub = common.content.subscriptionBlocks[newOption.subKey]; - - await applePayments.subscribe(newOption.sku, - user, - receipt, - headers, - nextPaymentProcessing); - - expect(iapSetupStub).to.be.calledOnce; - expect(iapValidateStub).to.be.calledOnce; - expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt); - expect(iapIsValidatedStub).to.be.calledOnce; - expect(iapIsValidatedStub).to.be.calledWith({}); - expect(iapGetPurchaseDataStub).to.be.calledOnce; - - expect(paymentsCreateSubscritionStub).to.be.calledOnce; - expect(paymentsCreateSubscritionStub).to.be.calledWith({ - user, - customerId: token, - paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE, - sub, - headers, - additionalData: receipt, - nextPaymentProcessing, - updatedFrom: oldSub, - }); - }); - } - if (option !== subOptions[0]) { - const newOption = subOptions[0]; - it(`downgrades a subscription from ${option.sku} to ${newOption.sku}`, async () => { - const oldSub = common.content.subscriptionBlocks[option.subKey]; - user.profile.name = 'sender'; - user.purchased.plan.paymentMethod = applePayments.constants.PAYMENT_METHOD_APPLE; - user.purchased.plan.customerId = token; - user.purchased.plan.planId = option.subKey; - user.purchased.plan.additionalData = receipt; - iap.getPurchaseData.restore(); - iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData') - .returns([{ - expirationDate: moment.utc().add({ day: 2 }).toDate(), - productId: newOption.sku, - transactionId: `${token}new`, - originalTransactionId: token, - }]); - sub = common.content.subscriptionBlocks[newOption.subKey]; - - await applePayments.subscribe(newOption.sku, - user, - receipt, - headers, - nextPaymentProcessing); - - expect(iapSetupStub).to.be.calledOnce; - expect(iapValidateStub).to.be.calledOnce; - expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt); - expect(iapIsValidatedStub).to.be.calledOnce; - expect(iapIsValidatedStub).to.be.calledWith({}); - expect(iapGetPurchaseDataStub).to.be.calledOnce; - - expect(paymentsCreateSubscritionStub).to.be.calledOnce; - expect(paymentsCreateSubscritionStub).to.be.calledWith({ - user, - customerId: token, - paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE, - sub, - headers, - additionalData: receipt, - nextPaymentProcessing, - updatedFrom: oldSub, - }); - }); - } }); - it('errors when a user is using the same subscription', async () => { + it('errors when a user is already subscribed', async () => { payments.createSubscription.restore(); user = new User(); await user.save(); - iap.getPurchaseData.restore(); - - iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData') - .returns([{ - expirationDate: moment.utc().add({ day: 1 }).toDate(), - productId: sku, - transactionId: token, - originalTransactionId: token, - }]); await applePayments.subscribe(sku, user, receipt, headers, nextPaymentProcessing); diff --git a/test/api/unit/libs/payments/payments.test.js b/test/api/unit/libs/payments/payments.test.js index 75500e28f5..48d6325546 100644 --- a/test/api/unit/libs/payments/payments.test.js +++ b/test/api/unit/libs/payments/payments.test.js @@ -445,89 +445,6 @@ describe('payments/index', () => { }, }); }); - - context('Upgrades subscription', () => { - it('from basic_earned to basic_6mo', async () => { - data.sub.key = 'basic_earned'; - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.customerId).to.eql('customer-id'); - const created = user.purchased.plan.dateCreated; - const updated = user.purchased.plan.dateUpdated; - - data.sub.key = 'basic_6mo'; - data.updatedFrom = { key: 'basic_earned' }; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.dateCreated).to.eql(created); - expect(user.purchased.plan.dateUpdated).to.not.eql(updated); - expect(user.purchased.plan.customerId).to.eql('customer-id'); - }); - - it('from basic_3mo to basic_12mo', async () => { - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_3mo'); - expect(user.purchased.plan.customerId).to.eql('customer-id'); - const created = user.purchased.plan.dateCreated; - const updated = user.purchased.plan.dateUpdated; - - data.sub.key = 'basic_12mo'; - data.updatedFrom = { key: 'basic_3mo' }; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.dateCreated).to.eql(created); - expect(user.purchased.plan.dateUpdated).to.not.eql(updated); - expect(user.purchased.plan.customerId).to.eql('customer-id'); - }); - }); - - context('Downgrades subscription', () => { - it('from basic_6mo to basic_earned', async () => { - data.sub.key = 'basic_6mo'; - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.customerId).to.eql('customer-id'); - const created = user.purchased.plan.dateCreated; - const updated = user.purchased.plan.dateUpdated; - - data.sub.key = 'basic_earned'; - data.updatedFrom = { key: 'basic_6mo' }; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.dateCreated).to.eql(created); - expect(user.purchased.plan.dateUpdated).to.not.eql(updated); - expect(user.purchased.plan.customerId).to.eql('customer-id'); - }); - - it('from basic_12mo to basic_3mo', async () => { - expect(user.purchased.plan.planId).to.not.exist; - - data.sub.key = 'basic_12mo'; - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.customerId).to.eql('customer-id'); - const created = user.purchased.plan.dateCreated; - const updated = user.purchased.plan.dateUpdated; - - data.sub.key = 'basic_3mo'; - data.updatedFrom = { key: 'basic_12mo' }; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_3mo'); - expect(user.purchased.plan.dateCreated).to.eql(created); - expect(user.purchased.plan.dateUpdated).to.not.eql(updated); - expect(user.purchased.plan.customerId).to.eql('customer-id'); - }); - }); }); context('Block subscription perks', () => { @@ -551,6 +468,7 @@ describe('payments/index', () => { it('adds 10 to plan.consecutive.gemCapExtra for 6 month block', async () => { data.sub.key = 'basic_6mo'; + await api.createSubscription(data); expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10); @@ -558,6 +476,7 @@ describe('payments/index', () => { it('adds 20 to plan.consecutive.gemCapExtra for 12 month block', async () => { data.sub.key = 'basic_12mo'; + await api.createSubscription(data); expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20); @@ -593,134 +512,6 @@ describe('payments/index', () => { expect(user.purchased.plan.consecutive.trinkets).to.eql(4); }); - - context('Upgrades subscription', () => { - it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => { - data.sub.key = 'basic_earned'; - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0); - - data.sub.key = 'basic_6mo'; - data.updatedFrom = { key: 'basic_earned' }; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10); - }); - - it('Adds 15 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => { - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_3mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5); - - data.sub.key = 'basic_12mo'; - data.updatedFrom = { key: 'basic_3mo' }; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20); - }); - - it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => { - data.sub.key = 'basic_earned'; - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(0); - - data.sub.key = 'basic_6mo'; - data.updatedFrom = { key: 'basic_earned' }; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); - }); - - it('Adds 3 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => { - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_3mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(1); - - data.sub.key = 'basic_12mo'; - data.updatedFrom = { key: 'basic_3mo' }; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(4); - }); - }); - - context('Downgrades subscription', () => { - it('does not remove from plan.consecutive.gemCapExtra from basic_6mo to basic_earned', async () => { - data.sub.key = 'basic_6mo'; - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10); - - data.sub.key = 'basic_earned'; - data.updatedFrom = { key: 'basic_6mo' }; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10); - }); - - it('does not remove from plan.consecutive.gemCapExtra from basic_12mo to basic_3mo', async () => { - expect(user.purchased.plan.planId).to.not.exist; - - data.sub.key = 'basic_12mo'; - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20); - - data.sub.key = 'basic_3mo'; - data.updatedFrom = { key: 'basic_12mo' }; - await api.createSubscription(data); - expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20); - }); - - it('does not remove from plan.consecutive.trinkets from basic_6mo to basic_earned', async () => { - data.sub.key = 'basic_6mo'; - expect(user.purchased.plan.planId).to.not.exist; - - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_6mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); - - data.sub.key = 'basic_earned'; - data.updatedFrom = { key: 'basic_6mo' }; - await api.createSubscription(data); - expect(user.purchased.plan.planId).to.eql('basic_earned'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(2); - }); - - it('does not remove from plan.consecutive.trinkets from basic_12mo to basic_3mo', async () => { - expect(user.purchased.plan.planId).to.not.exist; - - data.sub.key = 'basic_12mo'; - await api.createSubscription(data); - - expect(user.purchased.plan.planId).to.eql('basic_12mo'); - expect(user.purchased.plan.consecutive.trinkets).to.eql(4); - - data.sub.key = 'basic_3mo'; - data.updatedFrom = { key: 'basic_12mo' }; - await api.createSubscription(data); - expect(user.purchased.plan.consecutive.trinkets).to.eql(4); - }); - }); }); context('Mystery Items', () => { diff --git a/website/server/libs/payments/apple.js b/website/server/libs/payments/apple.js index e91729a7e0..8c0fd44184 100644 --- a/website/server/libs/payments/apple.js +++ b/website/server/libs/payments/apple.js @@ -106,6 +106,10 @@ api.verifyGemPurchase = async function verifyGemPurchase (options) { }; api.subscribe = async function subscribe (sku, user, receipt, headers, nextPaymentProcessing) { + if (user && user.isSubscribed()) { + throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED); + } + if (!sku) throw new BadRequest(shared.i18n.t('missingSubscriptionCode')); let subCode; @@ -136,49 +140,33 @@ api.subscribe = async function subscribe (sku, user, receipt, headers, nextPayme throw new NotAuthorized(api.constants.RESPONSE_NO_ITEM_PURCHASED); } - let originalTransactionId; - let newTransactionId; + let transactionId; for (const purchaseData of purchaseDataList) { const dateTerminated = new Date(Number(purchaseData.expirationDate)); if (purchaseData.productId === sku && dateTerminated > new Date()) { - originalTransactionId = purchaseData.originalTransactionId; - newTransactionId = purchaseData.transactionId; + transactionId = purchaseData.transactionId; break; } } - if (originalTransactionId) { - let existingSub; - if (user && user.isSubscribed()) { - if (user.purchased.plan.customerId !== originalTransactionId) { - throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED); - } - existingSub = shared.content.subscriptionBlocks[user.purchased.plan.planId]; - } + if (transactionId) { const existingUser = await User.findOne({ - 'purchased.plan.customerId': originalTransactionId, + 'purchased.plan.customerId': transactionId, }).exec(); - if (existingUser - && (originalTransactionId === newTransactionId || existingUser._id !== user._id)) { - throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED); - } + if (existingUser) throw new NotAuthorized(this.constants.RESPONSE_ALREADY_USED); nextPaymentProcessing = nextPaymentProcessing || moment.utc().add({ days: 2 }); // eslint-disable-line max-len, no-param-reassign - const data = { + await payments.createSubscription({ user, - customerId: originalTransactionId, + customerId: transactionId, paymentMethod: this.constants.PAYMENT_METHOD_APPLE, sub, headers, nextPaymentProcessing, additionalData: receipt, - }; - if (existingSub) { - data.updatedFrom = existingSub; - } - await payments.createSubscription(data); + }); } else { throw new NotAuthorized(api.constants.RESPONSE_INVALID_RECEIPT); } diff --git a/website/server/libs/payments/subscriptions.js b/website/server/libs/payments/subscriptions.js index 068197b839..80401f21de 100644 --- a/website/server/libs/payments/subscriptions.js +++ b/website/server/libs/payments/subscriptions.js @@ -71,15 +71,7 @@ async function createSubscription (data) { ? data.gift.subscription.key : data.sub.key]; const autoRenews = data.autoRenews !== undefined ? data.autoRenews : true; - const updatedFrom = data.updatedFrom - ? shared.content.subscriptionBlocks[data.updatedFrom.key] - : undefined; - let months; - if (updatedFrom && Number(updatedFrom.months) !== 1) { - months = Math.max(0, Number(block.months) - Number(updatedFrom.months)); - } else { - months = Number(block.months); - } + const months = Number(block.months); const today = new Date(); let group; let groupId;