|
|
@@ -9,7 +9,6 @@ const { Subscription } = require('../../models/Subscription')
|
|
|
const { User } = require('../../models/User')
|
|
|
const RecurlyClient = require('./RecurlyClient')
|
|
|
const PlansLocator = require('./PlansLocator')
|
|
|
-const SubscriptionHandler = require('./SubscriptionHandler')
|
|
|
const TeamInvitesHandler = require('./TeamInvitesHandler')
|
|
|
const GroupPlansData = require('./GroupPlansData')
|
|
|
const Modules = require('../../infrastructure/Modules')
|
|
|
@@ -86,22 +85,24 @@ async function ensureSubscriptionIsActive(subscription) {
|
|
|
}
|
|
|
|
|
|
async function ensureSubscriptionCollectionMethodIsNotManual(
|
|
|
- recurlySubscription
|
|
|
+ paymentProviderSubscription
|
|
|
) {
|
|
|
- if (recurlySubscription.isCollectionMethodManual) {
|
|
|
+ if (paymentProviderSubscription.isCollectionMethodManual) {
|
|
|
throw new ManuallyCollectedError(
|
|
|
'This subscription is being collected manually',
|
|
|
{
|
|
|
- recurlySubscription_id: recurlySubscription.id,
|
|
|
+ subscription_id: paymentProviderSubscription.id,
|
|
|
}
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-async function ensureSubscriptionHasNoPendingChanges(recurlySubscription) {
|
|
|
- if (recurlySubscription.pendingChange) {
|
|
|
+async function ensureSubscriptionHasNoPendingChanges(
|
|
|
+ paymentProviderSubscription
|
|
|
+) {
|
|
|
+ if (paymentProviderSubscription.pendingChange) {
|
|
|
throw new PendingChangeError('This subscription has a pending change', {
|
|
|
- recurlySubscription_id: recurlySubscription.id,
|
|
|
+ subscription_id: paymentProviderSubscription.id,
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
@@ -136,47 +137,50 @@ async function getUsersGroupSubscriptionDetails(userId) {
|
|
|
|
|
|
const plan = PlansLocator.findLocalPlanInSettings(subscription.planCode)
|
|
|
|
|
|
- const recurlySubscription = await RecurlyClient.promises.getSubscription(
|
|
|
- subscription.recurlySubscription_id
|
|
|
+ const response = await Modules.promises.hooks.fire(
|
|
|
+ 'getPaymentFromRecord',
|
|
|
+ subscription
|
|
|
)
|
|
|
|
|
|
+ const { subscription: paymentProviderSubscription } = response[0]
|
|
|
+
|
|
|
return {
|
|
|
userId,
|
|
|
subscription,
|
|
|
- recurlySubscription,
|
|
|
+ paymentProviderSubscription,
|
|
|
plan,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-async function checkBillingInfoExistence(recurlySubscription, userId) {
|
|
|
+async function checkBillingInfoExistence(paymentProviderSubscription, userId) {
|
|
|
// Verify the billing info only if the collection method is not manual (e.g. automatic)
|
|
|
- if (!recurlySubscription.isCollectionMethodManual) {
|
|
|
+ if (!paymentProviderSubscription.isCollectionMethodManual) {
|
|
|
// Check if the user has missing billing details
|
|
|
- await RecurlyClient.promises.getPaymentMethod(userId)
|
|
|
+ await Modules.promises.hooks.fire('getPaymentMethod', userId)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
async function _addSeatsSubscriptionChange(userId, adding) {
|
|
|
- const { subscription, recurlySubscription, plan } =
|
|
|
+ const { subscription, paymentProviderSubscription, plan } =
|
|
|
await getUsersGroupSubscriptionDetails(userId)
|
|
|
await ensureFlexibleLicensingEnabled(plan)
|
|
|
await ensureSubscriptionIsActive(subscription)
|
|
|
- await ensureSubscriptionHasNoPendingChanges(recurlySubscription)
|
|
|
- await checkBillingInfoExistence(recurlySubscription, userId)
|
|
|
+ await ensureSubscriptionHasNoPendingChanges(paymentProviderSubscription)
|
|
|
+ await checkBillingInfoExistence(paymentProviderSubscription, userId)
|
|
|
await ensureSubscriptionHasNoPastDueInvoice(subscription)
|
|
|
|
|
|
const currentAddonQuantity =
|
|
|
- recurlySubscription.addOns.find(
|
|
|
+ paymentProviderSubscription.addOns.find(
|
|
|
addOn => addOn.code === MEMBERS_LIMIT_ADD_ON_CODE
|
|
|
)?.quantity ?? 0
|
|
|
// Keeps only the new total quantity of addon
|
|
|
const nextAddonQuantity = currentAddonQuantity + adding
|
|
|
|
|
|
let changeRequest
|
|
|
- if (recurlySubscription.hasAddOn(MEMBERS_LIMIT_ADD_ON_CODE)) {
|
|
|
+ if (paymentProviderSubscription.hasAddOn(MEMBERS_LIMIT_ADD_ON_CODE)) {
|
|
|
// Not providing a custom price as once the subscription is locked
|
|
|
// to an add-on at a given price, it will use it for subsequent payments
|
|
|
- changeRequest = recurlySubscription.getRequestForAddOnUpdate(
|
|
|
+ changeRequest = paymentProviderSubscription.getRequestForAddOnUpdate(
|
|
|
MEMBERS_LIMIT_ADD_ON_CODE,
|
|
|
nextAddonQuantity
|
|
|
)
|
|
|
@@ -185,7 +189,7 @@ async function _addSeatsSubscriptionChange(userId, adding) {
|
|
|
const pattern =
|
|
|
/^group_(collaborator|professional)_(2|3|4|5|10|20|50)_(educational|enterprise)$/
|
|
|
const [, planCode, size, usage] = plan.planCode.match(pattern)
|
|
|
- const currency = recurlySubscription.currency
|
|
|
+ const currency = paymentProviderSubscription.currency
|
|
|
const planPriceInCents =
|
|
|
GroupPlansData[usage][planCode][currency][size].price_in_cents
|
|
|
const legacyUnitPriceInCents =
|
|
|
@@ -194,7 +198,7 @@ async function _addSeatsSubscriptionChange(userId, adding) {
|
|
|
|
|
|
if (
|
|
|
_shouldUseLegacyPricing(
|
|
|
- recurlySubscription.planPrice,
|
|
|
+ paymentProviderSubscription.planPrice,
|
|
|
planPriceInCents / 100,
|
|
|
usage,
|
|
|
size
|
|
|
@@ -203,7 +207,7 @@ async function _addSeatsSubscriptionChange(userId, adding) {
|
|
|
unitPrice = legacyUnitPriceInCents / 100
|
|
|
}
|
|
|
|
|
|
- changeRequest = recurlySubscription.getRequestForAddOnPurchase(
|
|
|
+ changeRequest = paymentProviderSubscription.getRequestForAddOnPurchase(
|
|
|
MEMBERS_LIMIT_ADD_ON_CODE,
|
|
|
nextAddonQuantity,
|
|
|
unitPrice
|
|
|
@@ -213,7 +217,7 @@ async function _addSeatsSubscriptionChange(userId, adding) {
|
|
|
return {
|
|
|
changeRequest,
|
|
|
currentAddonQuantity,
|
|
|
- recurlySubscription,
|
|
|
+ paymentProviderSubscription,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -237,37 +241,38 @@ function _shouldUseLegacyPricing(
|
|
|
async function previewAddSeatsSubscriptionChange(userId, adding) {
|
|
|
const { changeRequest, currentAddonQuantity } =
|
|
|
await _addSeatsSubscriptionChange(userId, adding)
|
|
|
- const subscriptionChange =
|
|
|
- await RecurlyClient.promises.previewSubscriptionChange(changeRequest)
|
|
|
- const subscriptionChangePreview =
|
|
|
- await SubscriptionController.makeChangePreview(
|
|
|
- {
|
|
|
- type: 'add-on-update',
|
|
|
- addOn: {
|
|
|
- code: MEMBERS_LIMIT_ADD_ON_CODE,
|
|
|
- quantity: subscriptionChange.nextAddOns.find(
|
|
|
- addon => addon.code === MEMBERS_LIMIT_ADD_ON_CODE
|
|
|
- ).quantity,
|
|
|
- prevQuantity: currentAddonQuantity,
|
|
|
- },
|
|
|
+ const response = await Modules.promises.hooks.fire(
|
|
|
+ 'previewSubscriptionChangeRequest',
|
|
|
+ changeRequest
|
|
|
+ )
|
|
|
+ const subscriptionChange = response[0]
|
|
|
+ const subscriptionChangePreview = SubscriptionController.makeChangePreview(
|
|
|
+ {
|
|
|
+ type: 'add-on-update',
|
|
|
+ addOn: {
|
|
|
+ code: MEMBERS_LIMIT_ADD_ON_CODE,
|
|
|
+ quantity: subscriptionChange.nextAddOns.find(
|
|
|
+ addon => addon.code === MEMBERS_LIMIT_ADD_ON_CODE
|
|
|
+ ).quantity,
|
|
|
+ prevQuantity: currentAddonQuantity,
|
|
|
},
|
|
|
- subscriptionChange
|
|
|
- )
|
|
|
+ },
|
|
|
+ subscriptionChange
|
|
|
+ )
|
|
|
|
|
|
return subscriptionChangePreview
|
|
|
}
|
|
|
|
|
|
async function createAddSeatsSubscriptionChange(userId, adding, poNumber) {
|
|
|
- const { changeRequest, recurlySubscription } =
|
|
|
+ const { changeRequest, paymentProviderSubscription } =
|
|
|
await _addSeatsSubscriptionChange(userId, adding)
|
|
|
|
|
|
- if (recurlySubscription.isCollectionMethodManual) {
|
|
|
- await updateSubscriptionPaymentTerms(userId, recurlySubscription, poNumber)
|
|
|
+ if (paymentProviderSubscription.isCollectionMethodManual) {
|
|
|
+ await updateSubscriptionPaymentTerms(paymentProviderSubscription, poNumber)
|
|
|
}
|
|
|
-
|
|
|
- await RecurlyClient.promises.applySubscriptionChangeRequest(changeRequest)
|
|
|
- await SubscriptionHandler.promises.syncSubscription(
|
|
|
- { uuid: recurlySubscription.id },
|
|
|
+ await Modules.promises.hooks.fire(
|
|
|
+ 'applySubscriptionChangeRequestAndSync',
|
|
|
+ changeRequest,
|
|
|
userId
|
|
|
)
|
|
|
|
|
|
@@ -275,21 +280,30 @@ async function createAddSeatsSubscriptionChange(userId, adding, poNumber) {
|
|
|
}
|
|
|
|
|
|
async function updateSubscriptionPaymentTerms(
|
|
|
- userId,
|
|
|
- recurlySubscription,
|
|
|
+ paymentProviderSubscription,
|
|
|
poNumber
|
|
|
) {
|
|
|
+ if (paymentProviderSubscription.service?.includes('stripe')) {
|
|
|
+ // TODO: Implement Stripe payment terms update
|
|
|
+ throw new OError(
|
|
|
+ 'Updating payment terms is not supported for Stripe subscriptions',
|
|
|
+ {
|
|
|
+ subscriptionId: paymentProviderSubscription.id,
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
const [termsAndConditions] = await Modules.promises.hooks.fire(
|
|
|
'generateTermsAndConditions',
|
|
|
- { currency: recurlySubscription.currency, poNumber }
|
|
|
+ { currency: paymentProviderSubscription.currency, poNumber }
|
|
|
)
|
|
|
|
|
|
const updateRequest = poNumber
|
|
|
- ? recurlySubscription.getRequestForPoNumberAndTermsAndConditionsUpdate(
|
|
|
+ ? paymentProviderSubscription.getRequestForPoNumberAndTermsAndConditionsUpdate(
|
|
|
poNumber,
|
|
|
termsAndConditions
|
|
|
)
|
|
|
- : recurlySubscription.getRequestForTermsAndConditionsUpdate(
|
|
|
+ : paymentProviderSubscription.getRequestForTermsAndConditionsUpdate(
|
|
|
termsAndConditions
|
|
|
)
|
|
|
|