const async = require('async') const RecurlyWrapper = require('./RecurlyWrapper') const { User } = require('../../models/User') const { promisifyAll } = require('../../util/promises') const logger = require('logger-sharelatex') const SubscriptionUpdater = require('./SubscriptionUpdater') const LimitationsManager = require('./LimitationsManager') const EmailHandler = require('../Email/EmailHandler') const Analytics = require('../Analytics/AnalyticsManager') const SubscriptionHandler = { validateNoSubscriptionInRecurly(userId, callback) { if (callback == null) { callback = function () {} } RecurlyWrapper.listAccountActiveSubscriptions( userId, function (error, subscriptions) { if (subscriptions == null) { subscriptions = [] } if (error != null) { return callback(error) } if (subscriptions.length > 0) { SubscriptionUpdater.syncSubscription( subscriptions[0], userId, function (error) { if (error != null) { return callback(error) } callback(null, false) } ) } else { callback(null, true) } } ) }, createSubscription(user, subscriptionDetails, recurlyTokenIds, callback) { SubscriptionHandler.validateNoSubscriptionInRecurly( user._id, function (error, valid) { if (error != null) { return callback(error) } if (!valid) { return callback(new Error('user already has subscription in recurly')) } RecurlyWrapper.createSubscription( user, subscriptionDetails, recurlyTokenIds, function (error, recurlySubscription) { if (error != null) { return callback(error) } return SubscriptionUpdater.syncSubscription( recurlySubscription, user._id, function (error) { if (error != null) { return callback(error) } return callback() } ) } ) } ) }, updateSubscription(user, planCode, couponCode, callback) { LimitationsManager.userHasV2Subscription( user, function (err, hasSubscription, subscription) { if (err) { logger.warn( { err, user_id: user._id, hasSubscription }, 'there was an error checking user v2 subscription' ) } if (!hasSubscription) { return callback() } else { return async.series( [ function (cb) { if (couponCode == null) { return cb() } RecurlyWrapper.getSubscription( subscription.recurlySubscription_id, { includeAccount: true }, function (err, usersSubscription) { if (err != null) { return callback(err) } RecurlyWrapper.redeemCoupon( usersSubscription.account.account_code, couponCode, cb ) } ) }, cb => RecurlyWrapper.updateSubscription( subscription.recurlySubscription_id, { plan_code: planCode, timeframe: 'now' }, function (error, recurlySubscription) { if (error != null) { return callback(error) } SubscriptionUpdater.syncSubscription( recurlySubscription, user._id, cb ) } ) ], callback ) } } ) }, cancelSubscription(user, callback) { LimitationsManager.userHasV2Subscription( user, function (err, hasSubscription, subscription) { if (err) { logger.warn( { err, user_id: user._id, hasSubscription }, 'there was an error checking user v2 subscription' ) } if (hasSubscription) { RecurlyWrapper.cancelSubscription( subscription.recurlySubscription_id, function (error) { if (error != null) { return callback(error) } const emailOpts = { to: user.email, first_name: user.first_name } const ONE_HOUR_IN_MS = 1000 * 60 * 60 setTimeout( () => EmailHandler.sendEmail( 'canceledSubscription', emailOpts, err => { if (err != null) { logger.warn( { err }, 'failed to send confirmation email for subscription cancellation' ) } } ), ONE_HOUR_IN_MS ) Analytics.recordEvent(user._id, 'subscription-canceled') callback() } ) } else { callback() } } ) }, reactivateSubscription(user, callback) { LimitationsManager.userHasV2Subscription( user, function (err, hasSubscription, subscription) { if (err) { logger.warn( { err, user_id: user._id, hasSubscription }, 'there was an error checking user v2 subscription' ) } if (hasSubscription) { RecurlyWrapper.reactivateSubscription( subscription.recurlySubscription_id, function (error) { if (error != null) { return callback(error) } EmailHandler.sendEmail( 'reactivatedSubscription', { to: user.email }, err => { if (err != null) { logger.warn( { err }, 'failed to send reactivation confirmation email' ) } } ) Analytics.recordEvent(user._id, 'subscription-reactivated') callback() } ) } else { callback() } } ) }, syncSubscription(recurlySubscription, requesterData, callback) { RecurlyWrapper.getSubscription( recurlySubscription.uuid, { includeAccount: true }, function (error, recurlySubscription) { if (error != null) { return callback(error) } User.findById( recurlySubscription.account.account_code, { _id: 1 }, function (error, user) { if (error != null) { return callback(error) } if (user == null) { return callback(new Error('no user found')) } SubscriptionUpdater.syncSubscription( recurlySubscription, user != null ? user._id : undefined, requesterData, callback ) } ) } ) }, // attempt to collect past due invoice for customer. Only do that when a) the // customer is using Paypal and b) there is only one past due invoice. // This is used because Recurly doesn't always attempt collection of paast due // invoices after Paypal billing info were updated. attemptPaypalInvoiceCollection(recurlyAccountCode, callback) { RecurlyWrapper.getBillingInfo(recurlyAccountCode, (error, billingInfo) => { if (error) { return callback(error) } if (!billingInfo.paypal_billing_agreement_id) { // this is not a Paypal user return callback() } RecurlyWrapper.getAccountPastDueInvoices( recurlyAccountCode, (error, pastDueInvoices) => { if (error) { return callback(error) } if (pastDueInvoices.length !== 1) { // no past due invoices, or more than one. Ignore. return callback() } RecurlyWrapper.attemptInvoiceCollection( pastDueInvoices[0].invoice_number, callback ) } ) }) }, extendTrial(subscription, daysToExend, callback) { return RecurlyWrapper.extendTrial( subscription.recurlySubscription_id, daysToExend, callback ) } } SubscriptionHandler.promises = promisifyAll(SubscriptionHandler) module.exports = SubscriptionHandler