Files
overleaf-cep/services/web/app/src/Features/Subscription/RecurlyClient.js
Brian Gough 52465d779e Merge pull request #13907 from overleaf/bg-managed-users-allow-cancelled-subscriptions
allow cancelled subscriptions for managed users

GitOrigin-RevId: 56262ce4bd4cc93d4e5ea92222c76a874d6cad1e
2023-07-20 08:05:08 +00:00

124 lines
3.7 KiB
JavaScript

const recurly = require('recurly')
const Settings = require('@overleaf/settings')
const logger = require('@overleaf/logger')
const { callbackify } = require('util')
const UserGetter = require('../User/UserGetter')
const recurlySettings = Settings.apis.recurly
const recurlyApiKey = recurlySettings ? recurlySettings.apiKey : undefined
const client = new recurly.Client(recurlyApiKey)
async function getAccountForUserId(userId) {
try {
return await client.getAccount(`code-${userId}`)
} catch (err) {
if (err instanceof recurly.errors.NotFoundError) {
// An expected error, we don't need to handle it, just return nothing
logger.debug({ userId }, 'no recurly account found for user')
} else {
throw err
}
}
}
async function createAccountForUserId(userId) {
const user = await UserGetter.promises.getUser(userId, {
_id: 1,
first_name: 1,
last_name: 1,
email: 1,
})
const accountCreate = {
code: user._id.toString(),
email: user.email,
firstName: user.first_name,
lastName: user.last_name,
}
const account = await client.createAccount(accountCreate)
logger.debug({ userId, account }, 'created recurly account')
return account
}
async function getSubscription(subscriptionId) {
return await client.getSubscription(subscriptionId)
}
async function changeSubscription(subscriptionId, body) {
const change = await client.createSubscriptionChange(subscriptionId, body)
logger.debug(
{ subscriptionId, changeId: change.id },
'created subscription change'
)
return change
}
async function changeSubscriptionByUuid(subscriptionUuid, ...args) {
return await changeSubscription('uuid-' + subscriptionUuid, ...args)
}
async function removeSubscriptionChange(subscriptionId) {
const removed = await client.removeSubscriptionChange(subscriptionId)
logger.debug({ subscriptionId }, 'removed pending subscription change')
return removed
}
async function removeSubscriptionChangeByUuid(subscriptionUuid) {
return await removeSubscriptionChange('uuid-' + subscriptionUuid)
}
async function reactivateSubscriptionByUuid(subscriptionUuid) {
return await client.reactivateSubscription('uuid-' + subscriptionUuid)
}
async function cancelSubscriptionByUuid(subscriptionUuid) {
try {
return await client.cancelSubscription('uuid-' + subscriptionUuid)
} catch (err) {
if (err instanceof recurly.errors.ValidationError) {
if (
err.message === 'Only active and future subscriptions can be canceled.'
) {
logger.debug(
{ subscriptionUuid },
'subscription cancellation failed, subscription not active'
)
}
} else {
throw err
}
}
}
function subscriptionIsCanceledOrExpired(subscription) {
const state = subscription?.recurlyStatus?.state
return state === 'canceled' || state === 'expired'
}
module.exports = {
errors: recurly.errors,
getAccountForUserId: callbackify(getAccountForUserId),
createAccountForUserId: callbackify(createAccountForUserId),
getSubscription: callbackify(getSubscription),
changeSubscription: callbackify(changeSubscription),
changeSubscriptionByUuid: callbackify(changeSubscriptionByUuid),
removeSubscriptionChange: callbackify(removeSubscriptionChange),
removeSubscriptionChangeByUuid: callbackify(removeSubscriptionChangeByUuid),
reactivateSubscriptionByUuid: callbackify(reactivateSubscriptionByUuid),
cancelSubscriptionByUuid: callbackify(cancelSubscriptionByUuid),
subscriptionIsCanceledOrExpired,
promises: {
getSubscription,
getAccountForUserId,
createAccountForUserId,
changeSubscription,
changeSubscriptionByUuid,
removeSubscriptionChange,
removeSubscriptionChangeByUuid,
reactivateSubscriptionByUuid,
cancelSubscriptionByUuid,
},
}