diff --git a/services/web/app/src/Features/Subscription/RecurlyWrapper.js b/services/web/app/src/Features/Subscription/RecurlyWrapper.js
index 830b3a047e..ebd8e64d43 100644
--- a/services/web/app/src/Features/Subscription/RecurlyWrapper.js
+++ b/services/web/app/src/Features/Subscription/RecurlyWrapper.js
@@ -3,12 +3,11 @@ const request = require('request')
const Settings = require('@overleaf/settings')
const xml2js = require('xml2js')
const logger = require('@overleaf/logger')
-const Async = require('async')
const Errors = require('../Errors/Errors')
const SubscriptionErrors = require('./Errors')
-const { promisify } = require('util')
+const { callbackify } = require('@overleaf/promise-utils')
-function updateAccountEmailAddress(accountId, newEmail, callback) {
+async function updateAccountEmailAddress(accountId, newEmail) {
const data = {
email: newEmail,
}
@@ -16,93 +15,76 @@ function updateAccountEmailAddress(accountId, newEmail, callback) {
try {
requestBody = RecurlyWrapper._buildXml('account', data)
} catch (error) {
- return callback(
- OError.tag(error, 'error building xml', { accountId, newEmail })
- )
+ throw OError.tag(error, 'error building xml', { accountId, newEmail })
}
- RecurlyWrapper.apiRequest(
- {
- url: `accounts/${accountId}`,
- method: 'PUT',
- body: requestBody,
- },
- (error, response, body) => {
- if (error) {
- return callback(error)
- }
- RecurlyWrapper._parseAccountXml(body, callback)
- }
- )
+ const { body } = await RecurlyWrapper.promises.apiRequest({
+ url: `accounts/${accountId}`,
+ method: 'PUT',
+ body: requestBody,
+ })
+ return await RecurlyWrapper.promises._parseAccountXml(body)
}
-const RecurlyWrapper = {
- apiUrl: Settings.apis.recurly.url || 'https://api.recurly.com/v2',
-
+const promises = {
_paypal: {
- checkAccountExists(cache, next) {
+ async checkAccountExists(cache) {
const { user } = cache
logger.debug(
{ userId: user._id },
'checking if recurly account exists for user'
)
- RecurlyWrapper.apiRequest(
- {
+ let response, body
+ try {
+ ;({ response, body } = await RecurlyWrapper.promises.apiRequest({
url: `accounts/${user._id}`,
method: 'GET',
expect404: true,
- },
- function (error, response, responseBody) {
- if (error) {
- OError.tag(
- error,
- 'error response from recurly while checking account',
- {
- user_id: user._id,
- }
- )
- return next(error)
+ }))
+ } catch (error) {
+ OError.tag(
+ error,
+ 'error response from recurly while checking account',
+ {
+ user_id: user._id,
}
- if (response.statusCode === 404) {
- // actually not an error in this case, just no existing account
- logger.debug(
- { userId: user._id },
- 'user does not currently exist in recurly, proceed'
- )
- cache.userExists = false
- return next(null, cache)
- }
- logger.debug({ userId: user._id }, 'user appears to exist in recurly')
- RecurlyWrapper._parseAccountXml(
- responseBody,
- function (err, account) {
- if (err) {
- OError.tag(err, 'error parsing account', {
- user_id: user._id,
- })
- return next(err)
- }
- cache.userExists = true
- cache.account = account
- next(null, cache)
- }
- )
- }
- )
+ )
+ throw error
+ }
+ if (response.statusCode === 404) {
+ // actually not an error in this case, just no existing account
+ logger.debug(
+ { userId: user._id },
+ 'user does not currently exist in recurly, proceed'
+ )
+ cache.userExists = false
+ return cache
+ }
+ logger.debug({ userId: user._id }, 'user appears to exist in recurly')
+ try {
+ const account = await RecurlyWrapper.promises._parseAccountXml(body)
+ cache.userExists = true
+ cache.account = account
+ return cache
+ } catch (err) {
+ OError.tag(err, 'error parsing account', {
+ user_id: user._id,
+ })
+ throw err
+ }
},
- createAccount(cache, next) {
+ async createAccount(cache) {
const { user } = cache
const { subscriptionDetails } = cache
if (cache.userExists) {
- return next(null, cache)
+ return cache
}
- let address
- try {
- address = getAddressFromSubscriptionDetails(subscriptionDetails, false)
- } catch (error) {
- return next(error)
- }
+ const address = getAddressFromSubscriptionDetails(
+ subscriptionDetails,
+ false
+ )
+
const data = {
account_code: user._id,
email: user.email,
@@ -114,97 +96,78 @@ const RecurlyWrapper = {
try {
requestBody = RecurlyWrapper._buildXml('account', data)
} catch (error) {
- return next(
- OError.tag(error, 'error building xml', { user_id: user._id })
- )
+ throw OError.tag(error, 'error building xml', { user_id: user._id })
}
- RecurlyWrapper.apiRequest(
- {
+ let body
+ try {
+ ;({ body } = await RecurlyWrapper.promises.apiRequest({
url: 'accounts',
method: 'POST',
body: requestBody,
- },
- (error, response, responseBody) => {
- if (error) {
- OError.tag(
- error,
- 'error response from recurly while creating account',
- {
- user_id: user._id,
- }
- )
- return next(error)
- }
- RecurlyWrapper._parseAccountXml(
- responseBody,
- function (err, account) {
- if (err) {
- OError.tag(err, 'error creating account', {
- user_id: user._id,
- })
- return next(err)
- }
- cache.account = account
- next(null, cache)
- }
- )
- }
- )
+ }))
+ } catch (error) {
+ OError.tag(
+ error,
+ 'error response from recurly while creating account',
+ { user_id: user._id }
+ )
+ throw error
+ }
+ try {
+ cache.account = await RecurlyWrapper.promises._parseAccountXml(body)
+ return cache
+ } catch (err) {
+ OError.tag(err, 'error creating account', {
+ user_id: user._id,
+ })
+ throw err
+ }
},
- createBillingInfo(cache, next) {
+ async createBillingInfo(cache) {
const { user } = cache
const { recurlyTokenIds } = cache
logger.debug({ userId: user._id }, 'creating billing info in recurly')
const accountCode = cache?.account?.account_code
if (!accountCode) {
- return next(new Error('no account code at createBillingInfo stage'))
+ throw new Error('no account code at createBillingInfo stage')
}
const data = { token_id: recurlyTokenIds.billing }
let requestBody
try {
requestBody = RecurlyWrapper._buildXml('billing_info', data)
} catch (error) {
- return next(
- OError.tag(error, 'error building xml', { user_id: user._id })
- )
+ throw OError.tag(error, 'error building xml', { user_id: user._id })
}
- RecurlyWrapper.apiRequest(
- {
+ let body
+ try {
+ ;({ body } = await RecurlyWrapper.promises.apiRequest({
url: `accounts/${accountCode}/billing_info`,
method: 'POST',
body: requestBody,
- },
- (error, response, responseBody) => {
- if (error) {
- OError.tag(
- error,
- 'error response from recurly while creating billing info',
- {
- user_id: user._id,
- }
- )
- return next(error)
- }
- RecurlyWrapper._parseBillingInfoXml(
- responseBody,
- function (err, billingInfo) {
- if (err) {
- OError.tag(err, 'error creating billing info', {
- user_id: user._id,
- accountCode,
- })
- return next(err)
- }
- cache.billingInfo = billingInfo
- next(null, cache)
- }
- )
- }
- )
+ }))
+ } catch (error) {
+ OError.tag(
+ error,
+ 'error response from recurly while creating billing info',
+ { user_id: user._id }
+ )
+ throw error
+ }
+ try {
+ cache.billingInfo =
+ await RecurlyWrapper.promises._parseBillingInfoXml(body)
+ return cache
+ } catch (err) {
+ OError.tag(err, 'error creating billing info', {
+ user_id: user._id,
+ accountCode,
+ })
+ throw err
+ }
},
- setAddressAndCompanyBillingInfo(cache, next) {
+ async setAddressAndCompanyBillingInfo(cache) {
const { user } = cache
const { subscriptionDetails } = cache
logger.debug(
@@ -213,20 +176,15 @@ const RecurlyWrapper = {
)
const accountCode = cache?.account?.account_code
if (!accountCode) {
- return next(
- new Error('no account code at setAddressAndCompanyBillingInfo stage')
+ throw new Error(
+ 'no account code at setAddressAndCompanyBillingInfo stage'
)
}
- let addressAndCompanyBillingInfo
- try {
- addressAndCompanyBillingInfo = getAddressFromSubscriptionDetails(
- subscriptionDetails,
- true
- )
- } catch (error) {
- return next(error)
- }
+ const addressAndCompanyBillingInfo = getAddressFromSubscriptionDetails(
+ subscriptionDetails,
+ true
+ )
let requestBody
try {
@@ -235,45 +193,36 @@ const RecurlyWrapper = {
addressAndCompanyBillingInfo
)
} catch (error) {
- return next(
- OError.tag(error, 'error building xml', { user_id: user._id })
- )
+ throw OError.tag(error, 'error building xml', { user_id: user._id })
}
- RecurlyWrapper.apiRequest(
- {
+ let body
+ try {
+ ;({ body } = await RecurlyWrapper.promises.apiRequest({
url: `accounts/${accountCode}/billing_info`,
method: 'PUT',
body: requestBody,
- },
- (error, response, responseBody) => {
- if (error) {
- OError.tag(
- error,
- 'error response from recurly while setting address',
- {
- user_id: user._id,
- }
- )
- return next(error)
- }
- RecurlyWrapper._parseBillingInfoXml(
- responseBody,
- function (err, billingInfo) {
- if (err) {
- OError.tag(err, 'error updating billing info', {
- user_id: user._id,
- })
- return next(err)
- }
- cache.billingInfo = billingInfo
- next(null, cache)
- }
- )
+ }))
+ } catch (error) {
+ OError.tag(error, 'error response from recurly while setting address', {
+ user_id: user._id,
+ })
+ throw error
+ }
+ try {
+ cache.billingInfo =
+ await RecurlyWrapper.promises._parseBillingInfoXml(body)
+ return cache
+ } catch (err) {
+ if (err) {
+ OError.tag(err, 'error updating billing info', {
+ user_id: user._id,
+ })
+ throw err
}
- )
+ }
},
- createSubscription(cache, next) {
+ async createSubscription(cache) {
const { user } = cache
const { subscriptionDetails } = cache
logger.debug({ userId: user._id }, 'creating subscription in recurly')
@@ -294,96 +243,80 @@ const RecurlyWrapper = {
try {
requestBody = RecurlyWrapper._buildXml('subscription', data)
} catch (error) {
- return next(
- OError.tag(error, 'error building xml', { user_id: user._id })
- )
+ throw OError.tag(error, 'error building xml', { user_id: user._id })
}
- RecurlyWrapper.apiRequest(
- {
+ let body
+ try {
+ ;({ body } = await RecurlyWrapper.promises.apiRequest({
url: 'subscriptions',
method: 'POST',
body: requestBody,
- },
- (error, response, responseBody) => {
- if (error) {
- OError.tag(
- error,
- 'error response from recurly while creating subscription',
- {
- user_id: user._id,
- }
- )
- return next(error)
- }
- RecurlyWrapper._parseSubscriptionXml(
- responseBody,
- function (err, subscription) {
- if (err) {
- OError.tag(err, 'error creating subscription', {
- user_id: user._id,
- })
- return next(err)
- }
- cache.subscription = subscription
- next(null, cache)
- }
- )
- }
- )
+ }))
+ } catch (error) {
+ OError.tag(
+ error,
+ 'error response from recurly while creating subscription',
+ { user_id: user._id }
+ )
+ throw error
+ }
+ try {
+ cache.subscription =
+ await RecurlyWrapper.promises._parseSubscriptionXml(body)
+ return cache
+ } catch (err) {
+ OError.tag(err, 'error creating subscription', {
+ user_id: user._id,
+ })
+ throw err
+ }
},
},
- _createPaypalSubscription(
- user,
- subscriptionDetails,
- recurlyTokenIds,
- callback
- ) {
+ async _createPaypalSubscription(user, subscriptionDetails, recurlyTokenIds) {
logger.debug(
{ userId: user._id },
'starting process of creating paypal subscription'
)
- // We use `async.waterfall` to run each of these actions in sequence
+ // We use waterfall through each of these actions in sequence
// passing a `cache` object along the way. The cache is initialized
// with required data, and `async.apply` to pass the cache to the first function
const cache = { user, recurlyTokenIds, subscriptionDetails }
- Async.waterfall(
- [
- Async.apply(RecurlyWrapper._paypal.checkAccountExists, cache),
- RecurlyWrapper._paypal.createAccount,
- RecurlyWrapper._paypal.createBillingInfo,
- RecurlyWrapper._paypal.setAddressAndCompanyBillingInfo,
- RecurlyWrapper._paypal.createSubscription,
- ],
- function (err, result) {
- if (err) {
- OError.tag(err, 'error in paypal subscription creation process', {
- user_id: user._id,
- })
- return callback(err)
- }
- if (!result.subscription) {
- err = new Error('no subscription object in result')
- OError.tag(err, 'error in paypal subscription creation process', {
- user_id: user._id,
- })
- return callback(err)
- }
- logger.debug(
- { userId: user._id },
- 'done creating paypal subscription for user'
+ let result
+ try {
+ result = await RecurlyWrapper.promises._paypal.checkAccountExists(cache)
+ result = await RecurlyWrapper.promises._paypal.createAccount(result)
+ result = await RecurlyWrapper.promises._paypal.createBillingInfo(result)
+ result =
+ await RecurlyWrapper.promises._paypal.setAddressAndCompanyBillingInfo(
+ result
)
- callback(null, result.subscription)
- }
+ result = await RecurlyWrapper.promises._paypal.createSubscription(result)
+ } catch (err) {
+ OError.tag(err, 'error in paypal subscription creation process', {
+ user_id: user._id,
+ })
+ throw err
+ }
+ if (!result.subscription) {
+ const err = new Error('no subscription object in result')
+ OError.tag(err, 'error in paypal subscription creation process', {
+ user_id: user._id,
+ })
+ throw err
+ }
+ logger.debug(
+ { userId: user._id },
+ 'done creating paypal subscription for user'
)
+ return result.subscription
},
- _createCreditCardSubscription(
+ async _createCreditCardSubscription(
user,
subscriptionDetails,
- recurlyTokenIds,
- callback
+ recurlyTokenIds
) {
const data = {
plan_code: subscriptionDetails.plan_code,
@@ -412,45 +345,40 @@ const RecurlyWrapper = {
try {
requestBody = RecurlyWrapper._buildXml('subscription', data)
} catch (error) {
- return callback(
- OError.tag(error, 'error building xml', { user_id: user._id })
- )
+ throw OError.tag(error, 'error building xml', { user_id: user._id })
}
- RecurlyWrapper.apiRequest(
- {
- url: 'subscriptions',
- method: 'POST',
- body: requestBody,
- expect422: true,
- },
- (error, response, responseBody) => {
- if (error) {
- return callback(error)
- }
+ const { response, body } = await RecurlyWrapper.promises.apiRequest({
+ url: 'subscriptions',
+ method: 'POST',
+ body: requestBody,
+ expect422: true,
+ })
- if (response.statusCode === 422) {
- RecurlyWrapper._handle422Response(responseBody, callback)
- } else {
- RecurlyWrapper._parseSubscriptionXml(responseBody, callback)
- }
- }
- )
+ if (response.statusCode === 422) {
+ return await RecurlyWrapper.promises._handle422Response(body)
+ } else {
+ return await RecurlyWrapper.promises._parseSubscriptionXml(body)
+ }
},
- createSubscription(user, subscriptionDetails, recurlyTokenIds, callback) {
+ async createSubscription(user, subscriptionDetails, recurlyTokenIds) {
const { isPaypal } = subscriptionDetails
logger.debug(
{ userId: user._id, isPaypal },
'setting up subscription in recurly'
)
const fn = isPaypal
- ? RecurlyWrapper._createPaypalSubscription
- : RecurlyWrapper._createCreditCardSubscription
- return fn(user, subscriptionDetails, recurlyTokenIds, callback)
+ ? RecurlyWrapper.promises._createPaypalSubscription
+ : RecurlyWrapper.promises._createCreditCardSubscription
+ return fn(user, subscriptionDetails, recurlyTokenIds)
},
- apiRequest(options, callback) {
+ /**
+ * @param options - the options to pass to the request library
+ * @returns {Promise<{ response: unknown, body: string}>}
+ */
+ apiRequest(options) {
options.url = RecurlyWrapper.apiUrl + '/' + options.url
options.headers = {
Authorization: `Basic ${Buffer.from(
@@ -463,55 +391,48 @@ const RecurlyWrapper = {
const { expect404, expect422 } = options
delete options.expect404
delete options.expect422
- request(options, function (error, response, body) {
- if (
- !error &&
- response.statusCode !== 200 &&
- response.statusCode !== 201 &&
- response.statusCode !== 204 &&
- (response.statusCode !== 404 || !expect404) &&
- (response.statusCode !== 422 || !expect422)
- ) {
- if (options.headers.Authorization) {
- options.headers.Authorization = 'REDACTED'
+ return new Promise((resolve, reject) => {
+ request(options, function (error, response, body) {
+ if (
+ !error &&
+ response.statusCode !== 200 &&
+ response.statusCode !== 201 &&
+ response.statusCode !== 204 &&
+ (response.statusCode !== 404 || !expect404) &&
+ (response.statusCode !== 422 || !expect422)
+ ) {
+ if (options.headers.Authorization) {
+ options.headers.Authorization = 'REDACTED'
+ }
+ logger.warn(
+ {
+ err: error,
+ body,
+ options,
+ statusCode: response ? response.statusCode : undefined,
+ },
+ 'error returned from recurly'
+ )
+ error = new OError(
+ `Recurly API returned with status code: ${response.statusCode}`,
+ { statusCode: response.statusCode }
+ )
+ reject(error)
}
- logger.warn(
- {
- err: error,
- body,
- options,
- statusCode: response ? response.statusCode : undefined,
- },
- 'error returned from recurly'
- )
- error = new OError(
- `Recurly API returned with status code: ${response.statusCode}`,
- { statusCode: response.statusCode }
- )
- }
- callback(error, response, body)
+ resolve({ response, body })
+ })
})
},
- getSubscriptions(accountId, callback) {
- RecurlyWrapper.apiRequest(
- {
- url: `accounts/${accountId}/subscriptions`,
- },
- (error, response, body) => {
- if (error) {
- return callback(error)
- }
- RecurlyWrapper._parseXml(body, callback)
- }
- )
+ async getSubscriptions(accountId) {
+ const { body } = await RecurlyWrapper.promises.apiRequest({
+ url: `accounts/${accountId}/subscriptions`,
+ })
+ return await RecurlyWrapper.promises._parseXml(body)
},
- getSubscription(subscriptionId, options, callback) {
+ async getSubscription(subscriptionId, options) {
let url
- if (!callback) {
- callback = options
- }
if (!options) {
options = {}
}
@@ -522,54 +443,34 @@ const RecurlyWrapper = {
url = `subscriptions/${subscriptionId}`
}
- RecurlyWrapper.apiRequest(
- {
- url,
- },
- (error, response, body) => {
- if (error) {
- return callback(error)
- }
- RecurlyWrapper._parseSubscriptionXml(
- body,
- (error, recurlySubscription) => {
- if (error) {
- return callback(error)
- }
- if (options.includeAccount) {
- let accountId
- if (
- recurlySubscription.account &&
- recurlySubscription.account.url
- ) {
- accountId =
- recurlySubscription.account.url.match(/accounts\/(.*)/)[1]
- } else {
- return callback(
- new Error("I don't understand the response from Recurly")
- )
- }
+ const { body } = await RecurlyWrapper.promises.apiRequest({
+ url,
+ })
- RecurlyWrapper.getAccount(accountId, function (error, account) {
- if (error) {
- return callback(error)
- }
- recurlySubscription.account = account
- callback(null, recurlySubscription)
- })
- } else {
- callback(null, recurlySubscription)
- }
- }
- )
+ const recurlySubscription =
+ await RecurlyWrapper.promises._parseSubscriptionXml(body)
+
+ if (options.includeAccount) {
+ let accountId
+ if (recurlySubscription.account && recurlySubscription.account.url) {
+ accountId = recurlySubscription.account.url.match(/accounts\/(.*)/)[1]
+ } else {
+ throw new Error("I don't understand the response from Recurly")
}
- )
+
+ recurlySubscription.account =
+ await RecurlyWrapper.promises.getAccount(accountId)
+
+ return recurlySubscription
+ } else {
+ return recurlySubscription
+ }
},
- getPaginatedEndpoint(resource, queryParams, callback) {
+ async getPaginatedEndpoint(resource, queryParams) {
queryParams.per_page = queryParams.per_page || 200
let allItems = []
- const getPage = (cursor = null) => {
+ const getPage = async (cursor = null) => {
const opts = {
url: resource,
qs: queryParams,
@@ -577,139 +478,86 @@ const RecurlyWrapper = {
if (cursor) {
opts.qs.cursor = cursor
}
- return RecurlyWrapper.apiRequest(opts, (error, response, body) => {
- if (error) {
- return callback(error)
- }
- return RecurlyWrapper._parseXml(body, function (err, data) {
- if (err) {
- logger.warn({ err }, 'could not get accounts')
- return callback(err)
- }
- const items = data[resource]
- allItems = allItems.concat(items)
- logger.debug(
- `got another ${items.length}, total now ${allItems.length}`
- )
- const match = response.headers.link?.match(
- /cursor=([0-9.]+%3A[0-9.]+)&/
- )
- cursor = match && match[1]
- if (cursor) {
- cursor = decodeURIComponent(cursor)
- return getPage(cursor)
- } else {
- callback(err, allItems)
- }
- })
- })
+ const { response, body } = await RecurlyWrapper.promises.apiRequest(opts)
+
+ const data = await RecurlyWrapper.promises._parseXml(body)
+
+ const items = data[resource]
+ allItems = allItems.concat(items)
+ logger.debug(`got another ${items.length}, total now ${allItems.length}`)
+ const match = response.headers.link?.match(/cursor=([0-9.]+%3A[0-9.]+)&/)
+ cursor = match && match[1]
+ if (cursor) {
+ cursor = decodeURIComponent(cursor)
+ return getPage(cursor)
+ } else {
+ return allItems
+ }
}
- getPage()
+ await getPage()
+
+ return allItems
},
- getAccount(accountId, callback) {
- RecurlyWrapper.apiRequest(
- {
- url: `accounts/${accountId}`,
- },
- (error, response, body) => {
- if (error) {
- return callback(error)
- }
- RecurlyWrapper._parseAccountXml(body, callback)
- }
- )
+ async getAccount(accountId) {
+ const { body } = await RecurlyWrapper.promises.apiRequest({
+ url: `accounts/${accountId}`,
+ })
+ return await RecurlyWrapper.promises._parseAccountXml(body)
},
updateAccountEmailAddress,
- getAccountActiveCoupons(accountId, callback) {
- RecurlyWrapper.apiRequest(
- {
- url: `accounts/${accountId}/redemptions`,
- },
- (error, response, body) => {
- if (error) {
- return callback(error)
- }
- RecurlyWrapper._parseRedemptionsXml(
- body,
- function (error, redemptions) {
- if (error) {
- return callback(error)
- }
- const activeRedemptions = redemptions.filter(
- redemption => redemption.state === 'active'
- )
- const couponCodes = activeRedemptions.map(
- redemption => redemption.coupon_code
- )
- Async.map(
- couponCodes,
- RecurlyWrapper.getCoupon,
- function (error, coupons) {
- if (error) {
- return callback(error)
- }
- return callback(null, coupons)
- }
- )
- }
- )
- }
+ async getAccountActiveCoupons(accountId) {
+ const { body } = await RecurlyWrapper.promises.apiRequest({
+ url: `accounts/${accountId}/redemptions`,
+ })
+
+ const redemptions = await RecurlyWrapper.promises._parseRedemptionsXml(body)
+
+ const activeRedemptions = redemptions.filter(
+ redemption => redemption.state === 'active'
+ )
+ const couponCodes = activeRedemptions.map(
+ redemption => redemption.coupon_code
+ )
+
+ return await Promise.all(
+ couponCodes.map(couponCode =>
+ RecurlyWrapper.promises.getCoupon(couponCode)
+ )
)
},
- getCoupon(couponCode, callback) {
+ async getCoupon(couponCode) {
const opts = { url: `coupons/${couponCode}` }
- RecurlyWrapper.apiRequest(opts, (error, response, body) => {
- if (error) {
- return callback(error)
- }
- RecurlyWrapper._parseCouponXml(body, callback)
+ const { body } = await RecurlyWrapper.promises.apiRequest(opts)
+ return await RecurlyWrapper.promises._parseCouponXml(body)
+ },
+
+ async getBillingInfo(accountId) {
+ const { body } = await RecurlyWrapper.promises.apiRequest({
+ url: `accounts/${accountId}/billing_info`,
+ })
+ return await RecurlyWrapper.promises._parseXml(body)
+ },
+
+ async getAccountPastDueInvoices(accountId) {
+ const { body } = await RecurlyWrapper.promises.apiRequest({
+ url: `accounts/${accountId}/invoices?state=past_due`,
+ })
+ return await RecurlyWrapper.promises._parseInvoicesXml(body)
+ },
+
+ async attemptInvoiceCollection(invoiceId) {
+ return await RecurlyWrapper.promises.apiRequest({
+ url: `invoices/${invoiceId}/collect`,
+ method: 'put',
})
},
- getBillingInfo(accountId, callback) {
- RecurlyWrapper.apiRequest(
- {
- url: `accounts/${accountId}/billing_info`,
- },
- (error, response, body) => {
- if (error) {
- return callback(error)
- }
- RecurlyWrapper._parseXml(body, callback)
- }
- )
- },
-
- getAccountPastDueInvoices(accountId, callback) {
- RecurlyWrapper.apiRequest(
- {
- url: `accounts/${accountId}/invoices?state=past_due`,
- },
- (error, response, body) => {
- if (error) {
- return callback(error)
- }
- RecurlyWrapper._parseInvoicesXml(body, callback)
- }
- )
- },
-
- attemptInvoiceCollection(invoiceId, callback) {
- RecurlyWrapper.apiRequest(
- {
- url: `invoices/${invoiceId}/collect`,
- method: 'put',
- },
- callback
- )
- },
-
- updateSubscription(subscriptionId, options, callback) {
+ async updateSubscription(subscriptionId, options) {
logger.debug(
{ subscriptionId, options },
'telling recurly to update subscription'
@@ -722,33 +570,23 @@ const RecurlyWrapper = {
try {
requestBody = RecurlyWrapper._buildXml('subscription', data)
} catch (error) {
- return callback(
- OError.tag(error, 'error building xml', { subscriptionId })
- )
+ throw OError.tag(error, 'error building xml', { subscriptionId })
}
- RecurlyWrapper.apiRequest(
- {
- url: `subscriptions/${subscriptionId}`,
- method: 'put',
- body: requestBody,
- },
- (error, response, responseBody) => {
- if (error) {
- return callback(error)
- }
- RecurlyWrapper._parseSubscriptionXml(responseBody, callback)
- }
- )
+ const { body } = await RecurlyWrapper.promises.apiRequest({
+ url: `subscriptions/${subscriptionId}`,
+ method: 'put',
+ body: requestBody,
+ })
+ return await RecurlyWrapper.promises._parseSubscriptionXml(body)
},
- createFixedAmmountCoupon(
+ async createFixedAmmountCoupon(
couponCode,
name,
currencyCode,
discountInCents,
- planCode,
- callback
+ planCode
) {
const data = {
coupon_code: couponCode,
@@ -765,45 +603,33 @@ const RecurlyWrapper = {
try {
requestBody = RecurlyWrapper._buildXml('coupon', data)
} catch (error) {
- return callback(
- OError.tag(error, 'error building xml', {
- couponCode,
- name,
- })
- )
+ throw OError.tag(error, 'error building xml', {
+ couponCode,
+ name,
+ })
}
logger.debug({ couponCode, requestBody }, 'creating coupon')
- RecurlyWrapper.apiRequest(
- {
+ try {
+ await RecurlyWrapper.promises.apiRequest({
url: 'coupons',
method: 'post',
body: requestBody,
- },
- (error, response, responseBody) => {
- if (error) {
- logger.warn({ err: error, couponCode }, 'error creating coupon')
- }
- callback(error)
- }
- )
+ })
+ } catch (error) {
+ logger.warn({ err: error, couponCode }, 'error creating coupon')
+ throw error
+ }
},
- lookupCoupon(couponCode, callback) {
- RecurlyWrapper.apiRequest(
- {
- url: `coupons/${couponCode}`,
- },
- (error, response, body) => {
- if (error) {
- return callback(error)
- }
- RecurlyWrapper._parseXml(body, callback)
- }
- )
+ async lookupCoupon(couponCode) {
+ const { body } = await RecurlyWrapper.promises.apiRequest({
+ url: `coupons/${couponCode}`,
+ })
+ return await RecurlyWrapper.promises._parseCouponXml(body)
},
- redeemCoupon(accountCode, couponCode, callback) {
+ async redeemCoupon(accountCode, couponCode) {
const data = {
account_code: accountCode,
currency: 'USD',
@@ -812,37 +638,32 @@ const RecurlyWrapper = {
try {
requestBody = RecurlyWrapper._buildXml('redemption', data)
} catch (error) {
- return callback(
- OError.tag(error, 'error building xml', {
- accountCode,
- couponCode,
- })
- )
+ throw OError.tag(error, 'error building xml', {
+ accountCode,
+ couponCode,
+ })
}
logger.debug(
{ accountCode, couponCode, requestBody },
'redeeming coupon for user'
)
- RecurlyWrapper.apiRequest(
- {
+ try {
+ await RecurlyWrapper.promises.apiRequest({
url: `coupons/${couponCode}/redeem`,
method: 'post',
body: requestBody,
- },
- (error, response, responseBody) => {
- if (error) {
- logger.warn(
- { err: error, accountCode, couponCode },
- 'error redeeming coupon'
- )
- }
- callback(error)
- }
- )
+ })
+ } catch (error) {
+ logger.warn(
+ { err: error, accountCode, couponCode },
+ 'error redeeming coupon'
+ )
+ throw error
+ }
},
- extendTrial(subscriptionId, daysUntilExpire, callback) {
+ async extendTrial(subscriptionId, daysUntilExpire) {
if (daysUntilExpire == null) {
daysUntilExpire = 7
}
@@ -852,128 +673,121 @@ const RecurlyWrapper = {
{ subscriptionId, daysUntilExpire },
'Exending Free trial for user'
)
- RecurlyWrapper.apiRequest(
- {
+ try {
+ await RecurlyWrapper.promises.apiRequest({
url: `/subscriptions/${subscriptionId}/postpone?next_bill_date=${nextRenewalDate}&bulk=false`,
method: 'put',
- },
- (error, response, responseBody) => {
- if (error) {
- logger.warn(
- { err: error, subscriptionId, daysUntilExpire },
- 'error exending trial'
- )
- }
- callback(error)
- }
- )
+ })
+ } catch (error) {
+ logger.warn(
+ { err: error, subscriptionId, daysUntilExpire },
+ 'error exending trial'
+ )
+ throw error
+ }
},
- listAccountActiveSubscriptions(accountId, callback) {
- RecurlyWrapper.apiRequest(
- {
- url: `accounts/${accountId}/subscriptions`,
- qs: {
- state: 'active',
+ async listAccountActiveSubscriptions(accountId) {
+ const { response, body } = await RecurlyWrapper.promises.apiRequest({
+ url: `accounts/${accountId}/subscriptions`,
+ qs: {
+ state: 'active',
+ },
+ expect404: true,
+ })
+ if (response.statusCode === 404) {
+ return []
+ } else {
+ return await RecurlyWrapper.promises._parseSubscriptionsXml(body)
+ }
+ },
+
+ async _handle422Response(body) {
+ const data = await RecurlyWrapper.promises._parseErrorsXml(body)
+ let errorData = {}
+ if (data.transaction_error) {
+ errorData = {
+ message: data.transaction_error.merchant_message,
+ info: {
+ category: data.transaction_error.error_category,
+ gatewayCode: data.transaction_error.gateway_error_code,
+ public: {
+ code: data.transaction_error.error_code,
+ message: data.transaction_error.customer_message,
+ },
},
- expect404: true,
- },
- function (error, response, body) {
- if (error) {
- return callback(error)
- }
- if (response.statusCode === 404) {
- callback(null, [])
- } else {
- RecurlyWrapper._parseSubscriptionsXml(body, callback)
- }
}
+ if (data.transaction_error.three_d_secure_action_token_id) {
+ errorData.info.public.threeDSecureActionTokenId =
+ data.transaction_error.three_d_secure_action_token_id
+ }
+ } else if (data.error && data.error._) {
+ // fallback for errors that don't have a `transaction_error` field, but
+ // instead a `error` field with a message (e.g. VATMOSS errors)
+ errorData = {
+ info: {
+ public: {
+ message: data.error._,
+ },
+ },
+ }
+ }
+ throw new SubscriptionErrors.RecurlyTransactionError(errorData)
+ },
+
+ async _parseSubscriptionsXml(xml) {
+ return await RecurlyWrapper.promises._parseXmlAndGetAttribute(
+ xml,
+ 'subscriptions'
+ )
+ },
+ async _parseSubscriptionXml(xml) {
+ return await RecurlyWrapper.promises._parseXmlAndGetAttribute(
+ xml,
+ 'subscription'
+ )
+ },
+ async _parseAccountXml(xml) {
+ return await RecurlyWrapper.promises._parseXmlAndGetAttribute(
+ xml,
+ 'account'
+ )
+ },
+ async _parseBillingInfoXml(xml) {
+ return await RecurlyWrapper.promises._parseXmlAndGetAttribute(
+ xml,
+ 'billing_info'
+ )
+ },
+ async _parseRedemptionsXml(xml) {
+ return await RecurlyWrapper.promises._parseXmlAndGetAttribute(
+ xml,
+ 'redemptions'
+ )
+ },
+ async _parseCouponXml(xml) {
+ return await RecurlyWrapper.promises._parseXmlAndGetAttribute(xml, 'coupon')
+ },
+ async _parseErrorsXml(xml) {
+ return await RecurlyWrapper.promises._parseXmlAndGetAttribute(xml, 'errors')
+ },
+ async _parseInvoicesXml(xml) {
+ return await RecurlyWrapper.promises._parseXmlAndGetAttribute(
+ xml,
+ 'invoices'
)
},
- _handle422Response(body, callback) {
- RecurlyWrapper._parseErrorsXml(body, (error, data) => {
- if (error) {
- return callback(error)
- }
-
- let errorData = {}
- if (data.transaction_error) {
- errorData = {
- message: data.transaction_error.merchant_message,
- info: {
- category: data.transaction_error.error_category,
- gatewayCode: data.transaction_error.gateway_error_code,
- public: {
- code: data.transaction_error.error_code,
- message: data.transaction_error.customer_message,
- },
- },
- }
- if (data.transaction_error.three_d_secure_action_token_id) {
- errorData.info.public.threeDSecureActionTokenId =
- data.transaction_error.three_d_secure_action_token_id
- }
- } else if (data.error && data.error._) {
- // fallback for errors that don't have a `transaction_error` field, but
- // instead a `error` field with a message (e.g. VATMOSS errors)
- errorData = {
- info: {
- public: {
- message: data.error._,
- },
- },
- }
- }
- callback(new SubscriptionErrors.RecurlyTransactionError(errorData))
- })
- },
- _parseSubscriptionsXml(xml, callback) {
- RecurlyWrapper._parseXmlAndGetAttribute(xml, 'subscriptions', callback)
+ async _parseXmlAndGetAttribute(xml, attribute) {
+ const data = await RecurlyWrapper.promises._parseXml(xml)
+ if (data && data[attribute] != null) {
+ return data[attribute]
+ } else {
+ throw new Error("I don't understand the response from Recurly")
+ }
},
- _parseSubscriptionXml(xml, callback) {
- RecurlyWrapper._parseXmlAndGetAttribute(xml, 'subscription', callback)
- },
-
- _parseAccountXml(xml, callback) {
- RecurlyWrapper._parseXmlAndGetAttribute(xml, 'account', callback)
- },
-
- _parseBillingInfoXml(xml, callback) {
- RecurlyWrapper._parseXmlAndGetAttribute(xml, 'billing_info', callback)
- },
-
- _parseRedemptionsXml(xml, callback) {
- RecurlyWrapper._parseXmlAndGetAttribute(xml, 'redemptions', callback)
- },
-
- _parseCouponXml(xml, callback) {
- RecurlyWrapper._parseXmlAndGetAttribute(xml, 'coupon', callback)
- },
-
- _parseErrorsXml(xml, callback) {
- RecurlyWrapper._parseXmlAndGetAttribute(xml, 'errors', callback)
- },
-
- _parseInvoicesXml(xml, callback) {
- RecurlyWrapper._parseXmlAndGetAttribute(xml, 'invoices', callback)
- },
-
- _parseXmlAndGetAttribute(xml, attribute, callback) {
- RecurlyWrapper._parseXml(xml, function (error, data) {
- if (error) {
- return callback(error)
- }
- if (data && data[attribute] != null) {
- callback(null, data[attribute])
- } else {
- callback(new Error("I don't understand the response from Recurly"))
- }
- })
- },
-
- _parseXml(xml, callback) {
+ _parseXml(xml) {
function convertDataTypes(data) {
let key, value
if (data && data.$) {
@@ -1017,43 +831,54 @@ const RecurlyWrapper = {
explicitArray: false,
emptyTag: '',
})
- parser.parseString(xml, function (error, data) {
- if (error) {
- return callback(error)
- }
- const result = convertDataTypes(data)
- callback(null, result)
- })
- },
-
- _buildXml(rootName, data) {
- const options = {
- headless: true,
- renderOpts: {
- pretty: true,
- indent: '\t',
- },
- rootName,
- }
- const builder = new xml2js.Builder(options)
- return builder.buildObject(data)
+ return new Promise((resolve, reject) =>
+ parser.parseString(xml, function (error, data) {
+ if (error) {
+ return reject(error)
+ }
+ const result = convertDataTypes(data)
+ resolve(result)
+ })
+ )
},
}
+function _buildXml(rootName, data) {
+ const options = {
+ headless: true,
+ renderOpts: {
+ pretty: true,
+ indent: '\t',
+ },
+ rootName,
+ }
+ const builder = new xml2js.Builder(options)
+ return builder.buildObject(data)
+}
+
+const RecurlyWrapper = {
+ apiUrl: Settings.apis.recurly.url || 'https://api.recurly.com/v2',
+ _buildXml,
+ _parseXml: callbackify(promises._parseXml),
+ // This one needs to be callbackified manually because we need to transform {response, body} to (err, response, body)
+ attemptInvoiceCollection: (invoiceId, callback) => {
+ promises
+ .attemptInvoiceCollection(invoiceId)
+ .then(({ response, body }) => callback(null, response, body))
+ .catch(callback)
+ },
+ createFixedAmmountCoupon: callbackify(promises.createFixedAmmountCoupon),
+ getAccountActiveCoupons: callbackify(promises.getAccountActiveCoupons),
+ getBillingInfo: callbackify(promises.getBillingInfo),
+ getPaginatedEndpoint: callbackify(promises.getPaginatedEndpoint),
+ getSubscription: callbackify(promises.getSubscription),
+ getSubscriptions: callbackify(promises.getSubscriptions),
+ updateAccountEmailAddress: callbackify(promises.updateAccountEmailAddress),
+}
+
RecurlyWrapper.promises = {
- attemptInvoiceCollection: promisify(RecurlyWrapper.attemptInvoiceCollection),
- createSubscription: promisify(RecurlyWrapper.createSubscription),
- extendTrial: promisify(RecurlyWrapper.extendTrial),
- getBillingInfo: promisify(RecurlyWrapper.getBillingInfo),
- getAccountPastDueInvoices: promisify(
- RecurlyWrapper.getAccountPastDueInvoices
- ),
- getSubscription: promisify(RecurlyWrapper.getSubscription),
- listAccountActiveSubscriptions: promisify(
- RecurlyWrapper.listAccountActiveSubscriptions
- ),
- redeemCoupon: promisify(RecurlyWrapper.redeemCoupon),
- updateAccountEmailAddress: promisify(updateAccountEmailAddress),
+ ...promises,
+ updateAccountEmailAddress,
}
module.exports = RecurlyWrapper
diff --git a/services/web/scripts/recurly/collect_paypal_past_due_invoice.js b/services/web/scripts/recurly/collect_paypal_past_due_invoice.js
index 89a5702bb4..9dbe32744b 100644
--- a/services/web/scripts/recurly/collect_paypal_past_due_invoice.js
+++ b/services/web/scripts/recurly/collect_paypal_past_due_invoice.js
@@ -76,7 +76,7 @@ const main = async () => {
'invoices',
{ state: 'past_due' },
(error, invoices) => {
- logger.info('invoices', invoices.length)
+ logger.info('invoices', invoices?.length)
if (error) {
return callback(error)
}
@@ -90,7 +90,7 @@ const main = async () => {
const INVOICES_COLLECTED_SUCCESS = []
const USERS_COLLECTED = []
- return new Promise(resolve => {
+ return new Promise((resolve, reject) => {
attemptInvoicesCollection(error => {
logger.info(
`DONE (DRY_RUN=${DRY_RUN}). ${INVOICES_COLLECTED.length} invoices collection attempts for ${USERS_COLLECTED.length} users. ${INVOICES_COLLECTED_SUCCESS.length} successful collections`
@@ -105,11 +105,11 @@ const main = async () => {
)
if (error) {
- throw error
+ reject(error)
}
if (INVOICES_COLLECTED_SUCCESS.length === 0) {
- throw new Error('No invoices collected')
+ reject(new Error('No invoices collected'))
}
resolve({
diff --git a/services/web/test/acceptance/src/CollectPayPalPastDueInvoiceTest.js b/services/web/test/acceptance/src/CollectPayPalPastDueInvoiceTest.js
index 57ba6b85ef..cfb200d0cf 100644
--- a/services/web/test/acceptance/src/CollectPayPalPastDueInvoiceTest.js
+++ b/services/web/test/acceptance/src/CollectPayPalPastDueInvoiceTest.js
@@ -155,44 +155,32 @@ const invoiceCollectXml = `
`
-// from our logs
-const invoiceCollectErrXml2 = `
-
-
- not_found
- Couldn't find BillingInfo with account_code = abcdef87654321
-
-`
-
describe('CollectPayPalPastDueInvoice', function () {
let apiRequestStub
const fakeApiRequests = invoiceIdsAndReturnCode => {
- apiRequestStub = sinon.stub(RecurlyWrapper, 'apiRequest')
- apiRequestStub.callsFake((options, callback) => {
+ apiRequestStub = sinon.stub(RecurlyWrapper.promises, 'apiRequest')
+ apiRequestStub.callsFake(options => {
switch (options.url) {
case 'invoices':
- callback(
- null,
- { statusCode: 200, headers: {} },
- invoicesXml(invoiceIdsAndReturnCode)
- )
- return
+ return {
+ response: { statusCode: 200, headers: {} },
+ body: invoicesXml(invoiceIdsAndReturnCode),
+ }
case 'accounts/200/billing_info':
case 'accounts/404/billing_info':
- callback(null, { statusCode: 200, headers: {} }, billingInfoXml)
- return
+ return {
+ response: { statusCode: 200, headers: {} },
+ body: billingInfoXml,
+ }
case 'invoices/200/collect':
- callback(null, { statusCode: 200, headers: {} }, invoiceCollectXml)
- return
+ return {
+ response: { statusCode: 200, headers: {} },
+ body: invoiceCollectXml,
+ }
case 'invoices/404/collect':
- callback(
- new OError(`Recurly API returned with status code: 404`, {
- statusCode: 404,
- }),
- { statusCode: 404, headers: {} },
- invoiceCollectErrXml2
- )
- return
+ throw new OError(`Recurly API returned with status code: 404`, {
+ statusCode: 404,
+ })
default:
throw new Error(`Unexpected URL: ${options.url}`)
}
diff --git a/services/web/test/unit/src/Subscription/RecurlyWrapperTests.js b/services/web/test/unit/src/Subscription/RecurlyWrapperTests.js
index 3eb1008178..225f78c2fc 100644
--- a/services/web/test/unit/src/Subscription/RecurlyWrapperTests.js
+++ b/services/web/test/unit/src/Subscription/RecurlyWrapperTests.js
@@ -90,11 +90,18 @@ const fixtures = {
'',
}
-const mockApiRequest = function (options, callback) {
+const mockApiRequest = function (options) {
if (fixtures[options.url]) {
- callback(null, { statusCode: 200 }, fixtures[options.url])
+ return {
+ err: null,
+ response: { statusCode: 200 },
+ body: fixtures[options.url],
+ }
} else {
- callback(new Error('Not found'), { statusCode: 404 })
+ return {
+ err: new Error('Not found'),
+ response: { statusCode: 404 },
+ }
}
}
@@ -138,122 +145,130 @@ describe('RecurlyWrapper', function () {
})
describe('getSubscription', function () {
- describe('with proper subscription id', function () {
- beforeEach(function (done) {
- this.apiRequest = sinon
- .stub(this.RecurlyWrapper, 'apiRequest')
- .callsFake(mockApiRequest)
- this.RecurlyWrapper.getSubscription(
- '44f83d7cba354d5b84812419f923ea96',
- (error, recurlySubscription) => {
- if (error) return done(error)
- this.recurlySubscription = recurlySubscription
- done()
+ for (const functionType of ['promise', 'callback']) {
+ describe(`as ${functionType}`, function () {
+ beforeEach(function () {
+ this.recurlySubscription = 'RESET'
+ this.getSubscription = (...params) => {
+ if (functionType === 'promise') {
+ return this.RecurlyWrapper.promises.getSubscription(...params)
+ }
+ if (functionType === 'callback') {
+ return new Promise((resolve, reject) =>
+ this.RecurlyWrapper.getSubscription(
+ ...params,
+ (err, subscription) =>
+ err ? reject(err) : resolve(subscription)
+ )
+ )
+ }
+ throw Error('Invalid function type')
}
- )
- })
- afterEach(function () {
- this.RecurlyWrapper.apiRequest.restore()
- })
+ })
- it('should look up the subscription at the normal API end point', function () {
- this.apiRequest.args[0][0].url.should.equal(
- 'subscriptions/44f83d7cba354d5b84812419f923ea96'
- )
- })
+ describe('with proper subscription id', function () {
+ beforeEach(async function () {
+ this.apiRequest = sinon
+ .stub(this.RecurlyWrapper.promises, 'apiRequest')
+ .callsFake(mockApiRequest)
+ this.recurlySubscription = await this.getSubscription(
+ '44f83d7cba354d5b84812419f923ea96'
+ )
+ })
+ afterEach(function () {
+ this.RecurlyWrapper.promises.apiRequest.restore()
+ })
- it('should return the subscription', function () {
- this.recurlySubscription.uuid.should.equal(
- '44f83d7cba354d5b84812419f923ea96'
- )
- })
- })
+ it('should look up the subscription at the normal API end point', function () {
+ this.apiRequest.args[0][0].url.should.equal(
+ 'subscriptions/44f83d7cba354d5b84812419f923ea96'
+ )
+ })
- describe('with ReculyJS token', function () {
- beforeEach(function (done) {
- this.apiRequest = sinon
- .stub(this.RecurlyWrapper, 'apiRequest')
- .callsFake(mockApiRequest)
- this.RecurlyWrapper.getSubscription(
- '70db44b10f5f4b238669480c9903f6f5',
- { recurlyJsResult: true },
- (error, recurlySubscription) => {
- if (error) return done(error)
- this.recurlySubscription = recurlySubscription
- done()
- }
- )
- })
- afterEach(function () {
- this.RecurlyWrapper.apiRequest.restore()
- })
+ it('should return the subscription', function () {
+ this.recurlySubscription.uuid.should.equal(
+ '44f83d7cba354d5b84812419f923ea96'
+ )
+ })
+ })
- it('should return the subscription', function () {
- this.recurlySubscription.uuid.should.equal(
- '44f83d7cba354d5b84812419f923ea96'
- )
- })
+ describe('with ReculyJS token', function () {
+ beforeEach(async function () {
+ this.apiRequest = sinon
+ .stub(this.RecurlyWrapper.promises, 'apiRequest')
+ .callsFake(mockApiRequest)
+ this.recurlySubscription = await this.getSubscription(
+ '70db44b10f5f4b238669480c9903f6f5',
+ { recurlyJsResult: true }
+ )
+ })
+ afterEach(function () {
+ this.RecurlyWrapper.promises.apiRequest.restore()
+ })
- it('should look up the subscription at the RecurlyJS API end point', function () {
- this.apiRequest.args[0][0].url.should.equal(
- 'recurly_js/result/70db44b10f5f4b238669480c9903f6f5'
- )
- })
- })
+ it('should return the subscription', function () {
+ this.recurlySubscription.uuid.should.equal(
+ '44f83d7cba354d5b84812419f923ea96'
+ )
+ })
- describe('with includeAccount', function () {
- beforeEach(function (done) {
- this.apiRequest = sinon
- .stub(this.RecurlyWrapper, 'apiRequest')
- .callsFake(mockApiRequest)
- this.RecurlyWrapper.getSubscription(
- '44f83d7cba354d5b84812419f923ea96',
- { includeAccount: true },
- (error, recurlySubscription) => {
- if (error) return done(error)
- this.recurlySubscription = recurlySubscription
- done()
- }
- )
- })
- afterEach(function () {
- this.RecurlyWrapper.apiRequest.restore()
- })
+ it('should look up the subscription at the RecurlyJS API end point', function () {
+ this.apiRequest.args[0][0].url.should.equal(
+ 'recurly_js/result/70db44b10f5f4b238669480c9903f6f5'
+ )
+ })
+ })
- it('should request the account from the API', function () {
- this.apiRequest.args[1][0].url.should.equal('accounts/104')
- })
+ describe('with includeAccount', function () {
+ beforeEach(async function () {
+ this.apiRequest = sinon
+ .stub(this.RecurlyWrapper.promises, 'apiRequest')
+ .callsFake(mockApiRequest)
+ this.recurlySubscription = await this.getSubscription(
+ '44f83d7cba354d5b84812419f923ea96',
+ { includeAccount: true }
+ )
+ })
+ afterEach(function () {
+ this.RecurlyWrapper.promises.apiRequest.restore()
+ })
- it('should populate the account attribute', function () {
- this.recurlySubscription.account.account_code.should.equal('104')
+ it('should request the account from the API', function () {
+ this.apiRequest.args[1][0].url.should.equal('accounts/104')
+ })
+
+ it('should populate the account attribute', function () {
+ this.recurlySubscription.account.account_code.should.equal('104')
+ })
+ })
})
- })
+ }
})
describe('updateAccountEmailAddress', function () {
- beforeEach(function (done) {
+ beforeEach(async function () {
this.recurlyAccountId = 'account-id-123'
this.newEmail = 'example@overleaf.com'
this.apiRequest = sinon
- .stub(this.RecurlyWrapper, 'apiRequest')
- .callsFake((options, callback) => {
+ .stub(this.RecurlyWrapper.promises, 'apiRequest')
+ .callsFake(options => {
this.requestOptions = options
- callback(null, {}, fixtures['accounts/104'])
+ return {
+ err: null,
+ response: {},
+ body: fixtures['accounts/104'],
+ }
})
- this.RecurlyWrapper.updateAccountEmailAddress(
- this.recurlyAccountId,
- this.newEmail,
- (error, recurlyAccount) => {
- if (error) return done(error)
- this.recurlyAccount = recurlyAccount
- done()
- }
- )
+ this.recurlyAccount =
+ await this.RecurlyWrapper.promises.updateAccountEmailAddress(
+ this.recurlyAccountId,
+ this.newEmail
+ )
})
afterEach(function () {
- this.RecurlyWrapper.apiRequest.restore()
+ this.RecurlyWrapper.promises.apiRequest.restore()
})
it('sends correct XML', function () {
@@ -275,61 +290,59 @@ describe('RecurlyWrapper', function () {
})
describe('updateAccountEmailAddress, with invalid XML', function () {
- beforeEach(function (done) {
+ beforeEach(async function (done) {
this.recurlyAccountId = 'account-id-123'
this.newEmail = '\uD800@example.com'
this.apiRequest = sinon
- .stub(this.RecurlyWrapper, 'apiRequest')
- .callsFake((options, callback) => {
+ .stub(this.RecurlyWrapper.promises, 'apiRequest')
+ .callsFake(options => {
this.requestOptions = options
- callback(null, {}, fixtures['accounts/104'])
+ return {
+ err: null,
+ response: {},
+ body: fixtures['accounts/104'],
+ }
})
done()
})
afterEach(function () {
- this.RecurlyWrapper.apiRequest.restore()
+ this.RecurlyWrapper.promises.apiRequest.restore()
})
it('should produce an error', function (done) {
- this.RecurlyWrapper.updateAccountEmailAddress(
- this.recurlyAccountId,
- this.newEmail,
- (error, recurlyAccount) => {
+ this.RecurlyWrapper.promises
+ .updateAccountEmailAddress(this.recurlyAccountId, this.newEmail)
+ .catch(error => {
expect(error).to.exist
expect(error.message.startsWith('Invalid character')).to.equal(true)
expect(this.apiRequest.called).to.equal(false)
done()
- }
- )
+ })
})
})
describe('updateSubscription', function () {
- beforeEach(function (done) {
+ beforeEach(async function () {
this.recurlySubscriptionId = 'subscription-id-123'
this.apiRequest = sinon
- .stub(this.RecurlyWrapper, 'apiRequest')
- .callsFake((options, callback) => {
+ .stub(this.RecurlyWrapper.promises, 'apiRequest')
+ .callsFake(options => {
this.requestOptions = options
- callback(
- null,
- {},
- fixtures['subscriptions/44f83d7cba354d5b84812419f923ea96']
- )
+ return {
+ error: null,
+ response: {},
+ body: fixtures['subscriptions/44f83d7cba354d5b84812419f923ea96'],
+ }
})
- this.RecurlyWrapper.updateSubscription(
- this.recurlySubscriptionId,
- { plan_code: 'silver', timeframe: 'now' },
- (error, recurlySubscription) => {
- if (error) return done(error)
- this.recurlySubscription = recurlySubscription
- done()
- }
- )
+ this.recurlySubscription =
+ await this.RecurlyWrapper.promises.updateSubscription(
+ this.recurlySubscriptionId,
+ { plan_code: 'silver', timeframe: 'now' }
+ )
})
afterEach(function () {
- this.RecurlyWrapper.apiRequest.restore()
+ this.RecurlyWrapper.promises.apiRequest.restore()
})
it('sends correct XML', function () {
@@ -354,25 +367,24 @@ describe('RecurlyWrapper', function () {
})
describe('redeemCoupon', function () {
- beforeEach(function (done) {
+ beforeEach(async function () {
this.recurlyAccountId = 'account-id-123'
this.coupon_code = '312321312'
this.apiRequest = sinon
- .stub(this.RecurlyWrapper, 'apiRequest')
- .callsFake((options, callback) => {
+ .stub(this.RecurlyWrapper.promises, 'apiRequest')
+ .callsFake(options => {
options.url.should.equal(`coupons/${this.coupon_code}/redeem`)
options.method.should.equal('post')
- callback()
+ return {}
})
- this.RecurlyWrapper.redeemCoupon(
+ await this.RecurlyWrapper.promises.redeemCoupon(
this.recurlyAccountId,
- this.coupon_code,
- done
+ this.coupon_code
)
})
afterEach(function () {
- this.RecurlyWrapper.apiRequest.restore()
+ this.RecurlyWrapper.promises.apiRequest.restore()
})
it('sends correct XML', function () {
@@ -388,29 +400,26 @@ describe('RecurlyWrapper', function () {
})
describe('createFixedAmmountCoupon', function () {
- beforeEach(function (done) {
+ beforeEach(async function () {
this.couponCode = 'a-coupon-code'
this.couponName = 'a-coupon-name'
this.currencyCode = 'EUR'
this.discount = 1337
this.planCode = 'a-plan-code'
this.apiRequest = sinon
- .stub(this.RecurlyWrapper, 'apiRequest')
- .callsFake((options, callback) => {
- callback()
- })
- this.RecurlyWrapper.createFixedAmmountCoupon(
+ .stub(this.RecurlyWrapper.promises, 'apiRequest')
+ .resolves()
+ await this.RecurlyWrapper.promises.createFixedAmmountCoupon(
this.couponCode,
this.couponName,
this.currencyCode,
this.discount,
- this.planCode,
- done
+ this.planCode
)
})
afterEach(function () {
- this.RecurlyWrapper.apiRequest.restore()
+ this.RecurlyWrapper.promises.apiRequest.restore()
})
it('sends correct XML', function () {
@@ -457,12 +466,11 @@ describe('RecurlyWrapper', function () {
billing: 'a-token-id',
threeDSecureActionResult: 'a-3d-token-id',
}
- this.call = callback => {
- this.RecurlyWrapper.createSubscription(
+ this.call = () => {
+ return this.RecurlyWrapper.promises.createSubscription(
this.user,
this.subscriptionDetails,
- this.recurlyTokenIds,
- callback
+ this.recurlyTokenIds
)
}
})
@@ -471,50 +479,37 @@ describe('RecurlyWrapper', function () {
beforeEach(function () {
this.subscriptionDetails.isPaypal = true
this._createPaypalSubscription = sinon.stub(
- this.RecurlyWrapper,
+ this.RecurlyWrapper.promises,
'_createPaypalSubscription'
)
- this._createPaypalSubscription.callsArgWith(3, null, this.subscription)
+ this._createPaypalSubscription.resolves(this.subscription)
})
afterEach(function () {
this._createPaypalSubscription.restore()
})
- it('should not produce an error', function (done) {
- this.call((err, sub) => {
- expect(err).to.equal(null)
- expect(err).to.not.be.instanceof(Error)
- done()
- })
+ it('should not produce an error', async function () {
+ await expect(this.call()).to.be.fulfilled
})
- it('should produce a subscription object', function (done) {
- this.call((err, sub) => {
- if (err) return done(err)
- expect(sub).to.deep.equal(this.subscription)
- done()
- })
+ it('should produce a subscription object', async function () {
+ const sub = await this.call()
+ expect(sub).to.deep.equal(this.subscription)
})
- it('should call _createPaypalSubscription', function (done) {
- this.call((err, sub) => {
- if (err) return done(err)
- this._createPaypalSubscription.callCount.should.equal(1)
- done()
- })
+ it('should call _createPaypalSubscription', async function () {
+ await this.call()
+ this._createPaypalSubscription.callCount.should.equal(1)
})
describe('when _createPaypalSubscription produces an error', function () {
beforeEach(function () {
- this._createPaypalSubscription.callsArgWith(3, new Error('woops'))
+ this._createPaypalSubscription.rejects(new Error('woops'))
})
- it('should produce an error', function (done) {
- this.call((err, sub) => {
- expect(err).to.be.instanceof(Error)
- done()
- })
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejectedWith('woops')
})
})
})
@@ -523,54 +518,37 @@ describe('RecurlyWrapper', function () {
beforeEach(function () {
this.subscriptionDetails.isPaypal = false
this._createCreditCardSubscription = sinon.stub(
- this.RecurlyWrapper,
+ this.RecurlyWrapper.promises,
'_createCreditCardSubscription'
)
- this._createCreditCardSubscription.callsArgWith(
- 3,
- null,
- this.subscription
- )
+ this._createCreditCardSubscription.resolves(this.subscription)
})
afterEach(function () {
this._createCreditCardSubscription.restore()
})
- it('should not produce an error', function (done) {
- this.call((err, sub) => {
- expect(err).to.equal(null)
- expect(err).to.not.be.instanceof(Error)
- done()
- })
+ it('should not produce an error', async function () {
+ await this.call()
})
- it('should produce a subscription object', function (done) {
- this.call((err, sub) => {
- if (err) return done(err)
- expect(sub).to.deep.equal(this.subscription)
- done()
- })
+ it('should produce a subscription object', async function () {
+ const sub = await this.call()
+ expect(sub).to.deep.equal(this.subscription)
})
- it('should call _createCreditCardSubscription', function (done) {
- this.call((err, sub) => {
- if (err) return done(err)
- this._createCreditCardSubscription.callCount.should.equal(1)
- done()
- })
+ it('should call _createCreditCardSubscription', async function () {
+ await this.call()
+ this._createCreditCardSubscription.callCount.should.equal(1)
})
describe('when _createCreditCardSubscription produces an error', function () {
beforeEach(function () {
- this._createCreditCardSubscription.callsArgWith(3, new Error('woops'))
+ this._createCreditCardSubscription.rejects(new Error('woops'))
})
- it('should produce an error', function (done) {
- this.call((err, sub) => {
- expect(err).to.be.instanceof(Error)
- done()
- })
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejectedWith('woops')
})
})
})
@@ -606,21 +584,23 @@ describe('RecurlyWrapper', function () {
billing: 'a-token-id',
threeDSecureActionResult: 'a-3d-token-id',
}
- this.apiRequest = sinon.stub(this.RecurlyWrapper, 'apiRequest')
+ this.apiRequest = sinon.stub(this.RecurlyWrapper.promises, 'apiRequest')
this.response = { statusCode: 200 }
this.body = 'is_bad'
- this.apiRequest.callsArgWith(1, null, this.response, this.body)
+ this.apiRequest.resolves({
+ response: this.response,
+ body: this.body,
+ })
this._parseSubscriptionXml = sinon.stub(
- this.RecurlyWrapper,
+ this.RecurlyWrapper.promises,
'_parseSubscriptionXml'
)
- this._parseSubscriptionXml.callsArgWith(1, null, this.subscription)
- this.call = callback => {
- this.RecurlyWrapper._createCreditCardSubscription(
+ this._parseSubscriptionXml.resolves(this.subscription)
+ this.call = () => {
+ return this.RecurlyWrapper.promises._createCreditCardSubscription(
this.user,
this.subscriptionDetails,
- this.recurlyTokenIds,
- callback
+ this.recurlyTokenIds
)
}
})
@@ -630,11 +610,11 @@ describe('RecurlyWrapper', function () {
this._parseSubscriptionXml.restore()
})
- it('sends correct XML', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- const { body } = this.apiRequest.lastCall.args[0]
- expect(body).to.equal(`\
+ it('sends correct XML', async function () {
+ await this.call()
+
+ const { body } = this.apiRequest.lastCall.args[0]
+ expect(body).to.equal(`\
some_plan_code
EUR
@@ -665,40 +645,25 @@ describe('RecurlyWrapper', function () {
\
`)
- done()
- })
})
- it('should not produce an error', function (done) {
- this.call((err, sub) => {
- expect(err).to.not.be.instanceof(Error)
- expect(err).to.equal(null)
- done()
- })
+ it('should not produce an error', async function () {
+ await expect(this.call()).to.be.fulfilled
})
- it('should produce a subscription', function (done) {
- this.call((err, sub) => {
- if (err) return done(err)
- expect(sub).to.equal(this.subscription)
- done()
- })
+ it('should produce a subscription', async function () {
+ const sub = await this.call()
+ expect(sub).to.equal(this.subscription)
})
- it('should call apiRequest', function (done) {
- this.call((err, sub) => {
- if (err) return done(err)
- this.apiRequest.callCount.should.equal(1)
- done()
- })
+ it('should call apiRequest', async function () {
+ await this.call()
+ this.apiRequest.callCount.should.equal(1)
})
- it('should call _parseSubscriptionXml', function (done) {
- this.call((err, sub) => {
- if (err) return done(err)
- this._parseSubscriptionXml.callCount.should.equal(1)
- done()
- })
+ it('should call _parseSubscriptionXml', async function () {
+ await this.call()
+ this._parseSubscriptionXml.callCount.should.equal(1)
})
describe('when api request returns 422', function () {
@@ -717,11 +682,15 @@ describe('RecurlyWrapper', function () {
Your card must be authenticated with 3D Secure before continuing.
`
- this.apiRequest.yields(null, { statusCode: 422 }, body)
+ // this.apiRequest.yields(null, { statusCode: 422 }, body)
+ this.apiRequest.resolves({
+ response: { statusCode: 422 },
+ body,
+ })
})
it('should produce an error', function (done) {
- this.call((err, sub) => {
+ this.call().catch(err => {
expect(err).to.be.instanceof(
SubscriptionErrors.RecurlyTransactionError
)
@@ -738,41 +707,31 @@ describe('RecurlyWrapper', function () {
describe('when api request produces an error', function () {
beforeEach(function () {
- this.apiRequest.callsArgWith(1, new Error('woops'))
+ this.apiRequest.rejects(new Error('woops'))
})
- it('should produce an error', function (done) {
- this.call((err, sub) => {
- expect(err).to.be.instanceof(Error)
- done()
- })
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejectedWith('woops')
})
- it('should call apiRequest', function (done) {
- this.call(() => {
- this.apiRequest.callCount.should.equal(1)
- done()
- })
+ it('should call apiRequest', async function () {
+ await expect(this.call()).to.be.rejected
+ this.apiRequest.callCount.should.equal(1)
})
- it('should not _parseSubscriptionXml', function (done) {
- this.call(() => {
- this._parseSubscriptionXml.callCount.should.equal(0)
- done()
- })
+ it('should not _parseSubscriptionXml', async function () {
+ await expect(this.call()).to.be.rejected
+ this._parseSubscriptionXml.callCount.should.equal(0)
})
})
describe('when parse xml produces an error', function () {
beforeEach(function () {
- this._parseSubscriptionXml.callsArgWith(1, new Error('woops'))
+ this._parseSubscriptionXml.rejects(new Error('woops xml'))
})
- it('should produce an error', function (done) {
- this.call((err, sub) => {
- expect(err).to.be.instanceof(Error)
- done()
- })
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejectedWith('woops xml')
})
})
})
@@ -780,23 +739,23 @@ describe('RecurlyWrapper', function () {
describe('_createPaypalSubscription', function () {
beforeEach(function () {
this.checkAccountExists = sinon.stub(
- this.RecurlyWrapper._paypal,
+ this.RecurlyWrapper.promises._paypal,
'checkAccountExists'
)
this.createAccount = sinon.stub(
- this.RecurlyWrapper._paypal,
+ this.RecurlyWrapper.promises._paypal,
'createAccount'
)
this.createBillingInfo = sinon.stub(
- this.RecurlyWrapper._paypal,
+ this.RecurlyWrapper.promises._paypal,
'createBillingInfo'
)
this.setAddressAndCompanyBillingInfo = sinon.stub(
- this.RecurlyWrapper._paypal,
+ this.RecurlyWrapper.promises._paypal,
'setAddressAndCompanyBillingInfo'
)
this.createSubscription = sinon.stub(
- this.RecurlyWrapper._paypal,
+ this.RecurlyWrapper.promises._paypal,
'createSubscription'
)
this.user = {
@@ -827,21 +786,21 @@ describe('RecurlyWrapper', function () {
const { subscriptionDetails } = this
const { recurlyTokenIds } = this
- this.checkAccountExists.callsArgWith(1, null, {
+ this.checkAccountExists.resolves({
user,
subscriptionDetails,
recurlyTokenIds,
userExists: false,
account: { accountCode: 'xx' },
})
- this.createAccount.callsArgWith(1, null, {
+ this.createAccount.resolves({
user,
subscriptionDetails,
recurlyTokenIds,
userExists: false,
account: { accountCode: 'xx' },
})
- this.createBillingInfo.callsArgWith(1, null, {
+ this.createBillingInfo.resolves({
user,
subscriptionDetails,
recurlyTokenIds,
@@ -849,7 +808,7 @@ describe('RecurlyWrapper', function () {
account: { accountCode: 'xx' },
billingInfo: { token_id: 'abc' },
})
- this.setAddressAndCompanyBillingInfo.callsArgWith(1, null, {
+ this.setAddressAndCompanyBillingInfo.resolves({
user,
subscriptionDetails,
recurlyTokenIds,
@@ -857,7 +816,7 @@ describe('RecurlyWrapper', function () {
account: { accountCode: 'xx' },
billingInfo: { token_id: 'abc' },
})
- this.createSubscription.callsArgWith(1, null, {
+ this.createSubscription.resolves({
user,
subscriptionDetails,
recurlyTokenIds,
@@ -867,12 +826,11 @@ describe('RecurlyWrapper', function () {
subscription: this.subscription,
})
- this.call = callback => {
- this.RecurlyWrapper._createPaypalSubscription(
+ this.call = () => {
+ return this.RecurlyWrapper.promises._createPaypalSubscription(
this.user,
this.subscriptionDetails,
- this.recurlyTokenIds,
- callback
+ this.recurlyTokenIds
)
}
})
@@ -885,69 +843,58 @@ describe('RecurlyWrapper', function () {
this.createSubscription.restore()
})
- it('should not produce an error', function (done) {
- this.call((err, sub) => {
- expect(err).to.not.be.instanceof(Error)
- done()
- })
+ it('should not produce an error', async function () {
+ await expect(this.call()).to.be.fulfilled
})
- it('should produce a subscription object', function (done) {
- this.call((err, sub) => {
- if (err) return done(err)
- expect(sub).to.not.equal(null)
- expect(sub).to.equal(this.subscription)
- done()
- })
+ it('should produce a subscription object', async function () {
+ const sub = await this.call()
+ expect(sub).to.not.equal(null)
+ expect(sub).to.equal(this.subscription)
})
- it('should call each of the paypal stages', function (done) {
- this.call((err, sub) => {
- if (err) return done(err)
- this.checkAccountExists.callCount.should.equal(1)
- this.createAccount.callCount.should.equal(1)
- this.createBillingInfo.callCount.should.equal(1)
- this.setAddressAndCompanyBillingInfo.callCount.should.equal(1)
- this.createSubscription.callCount.should.equal(1)
- done()
- })
+ it('should call each of the paypal stages', async function () {
+ await this.call()
+ this.checkAccountExists.callCount.should.equal(1)
+ this.createAccount.callCount.should.equal(1)
+ this.createBillingInfo.callCount.should.equal(1)
+ this.setAddressAndCompanyBillingInfo.callCount.should.equal(1)
+ this.createSubscription.callCount.should.equal(1)
})
describe('when one of the paypal stages produces an error', function () {
beforeEach(function () {
- this.createAccount.callsArgWith(1, new Error('woops'))
+ this.createAccount.rejects(new Error('woops'))
})
- it('should produce an error', function (done) {
- this.call((err, sub) => {
- expect(err).to.be.instanceof(Error)
- done()
- })
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejectedWith('woops')
})
- it('should stop calling the paypal stages after the error', function (done) {
- this.call(() => {
- this.checkAccountExists.callCount.should.equal(1)
- this.createAccount.callCount.should.equal(1)
- this.createBillingInfo.callCount.should.equal(0)
- this.setAddressAndCompanyBillingInfo.callCount.should.equal(0)
- this.createSubscription.callCount.should.equal(0)
- done()
- })
+ it('should stop calling the paypal stages after the error', async function () {
+ await expect(this.call()).to.be.rejected
+ this.checkAccountExists.callCount.should.equal(1)
+ this.createAccount.callCount.should.equal(1)
+ this.createBillingInfo.callCount.should.equal(0)
+ this.setAddressAndCompanyBillingInfo.callCount.should.equal(0)
+ this.createSubscription.callCount.should.equal(0)
})
})
})
describe('paypal actions', function () {
beforeEach(function () {
- this.apiRequest = sinon.stub(this.RecurlyWrapper, 'apiRequest')
- this._parseAccountXml = sinon.spy(this.RecurlyWrapper, '_parseAccountXml')
+ this.apiRequest = sinon.stub(this.RecurlyWrapper.promises, 'apiRequest')
+ this._parseAccountXml = sinon.spy(
+ this.RecurlyWrapper.promises,
+ '_parseAccountXml'
+ )
this._parseBillingInfoXml = sinon.spy(
- this.RecurlyWrapper,
+ this.RecurlyWrapper.promises,
'_parseBillingInfoXml'
)
this._parseSubscriptionXml = sinon.spy(
- this.RecurlyWrapper,
+ this.RecurlyWrapper.promises,
'_parseSubscriptionXml'
)
this.cache = {
@@ -989,8 +936,10 @@ describe('RecurlyWrapper', function () {
describe('_paypal.checkAccountExists', function () {
beforeEach(function () {
- this.call = callback => {
- this.RecurlyWrapper._paypal.checkAccountExists(this.cache, callback)
+ this.call = () => {
+ return this.RecurlyWrapper.promises._paypal.checkAccountExists(
+ this.cache
+ )
}
})
@@ -998,119 +947,94 @@ describe('RecurlyWrapper', function () {
beforeEach(function () {
const resultXml =
'abc'
- this.apiRequest.callsArgWith(1, null, { statusCode: 200 }, resultXml)
- })
-
- it('should not produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.not.be.instanceof(Error)
- done()
+ this.apiRequest.resolves({
+ response: { statusCode: 200 },
+ body: resultXml,
})
})
- it('should call apiRequest', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- this.apiRequest.callCount.should.equal(1)
- done()
+ it('should not produce an error', async function () {
+ await expect(this.call()).to.be.fulfilled
+ })
+
+ it('should call apiRequest', async function () {
+ await this.call()
+ this.apiRequest.callCount.should.equal(1)
+ })
+
+ it('should call _parseAccountXml', async function () {
+ await this.call()
+ this.RecurlyWrapper.promises._parseAccountXml.callCount.should.equal(
+ 1
+ )
+ })
+
+ it('should add the account to the cumulative result', async function () {
+ const result = await this.call()
+ expect(result.account).to.not.equal(null)
+ expect(result.account).to.not.equal(undefined)
+ expect(result.account).to.deep.equal({
+ account_code: 'abc',
})
})
- it('should call _parseAccountXml', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- this.RecurlyWrapper._parseAccountXml.callCount.should.equal(1)
- done()
- })
- })
-
- it('should add the account to the cumulative result', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- expect(result.account).to.not.equal(null)
- expect(result.account).to.not.equal(undefined)
- expect(result.account).to.deep.equal({
- account_code: 'abc',
- })
- done()
- })
- })
-
- it('should set userExists to true', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- expect(result.userExists).to.equal(true)
- done()
- })
+ it('should set userExists to true', async function () {
+ const result = await this.call()
+ expect(result.userExists).to.equal(true)
})
})
describe('when the account does not exist', function () {
beforeEach(function () {
- this.apiRequest.callsArgWith(1, null, { statusCode: 404 }, '')
- })
-
- it('should not produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.not.be.instanceof(Error)
- done()
+ this.apiRequest.resolves({
+ response: { statusCode: 404 },
+ body: '',
})
})
- it('should call apiRequest', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- this.apiRequest.callCount.should.equal(1)
- this.apiRequest.firstCall.args[0].method.should.equal('GET')
- done()
- })
+ it('should not produce an error', async function () {
+ await expect(this.call()).to.be.fulfilled
})
- it('should not call _parseAccountXml', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- this.RecurlyWrapper._parseAccountXml.callCount.should.equal(0)
- done()
- })
+ it('should call apiRequest', async function () {
+ await this.call()
+ this.apiRequest.callCount.should.equal(1)
+ this.apiRequest.firstCall.args[0].method.should.equal('GET')
})
- it('should not add the account to result', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- expect(result.account).to.equal(undefined)
- done()
- })
+ it('should not call _parseAccountXml', async function () {
+ await this.call()
+ this.RecurlyWrapper.promises._parseAccountXml.callCount.should.equal(
+ 0
+ )
})
- it('should set userExists to false', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- expect(result.userExists).to.equal(false)
- done()
- })
+ it('should not add the account to result', async function () {
+ const result = await this.call()
+ expect(result.account).to.equal(undefined)
+ })
+
+ it('should set userExists to false', async function () {
+ const result = await this.call()
+ expect(result.userExists).to.equal(false)
})
})
describe('when apiRequest produces an error', function () {
beforeEach(function () {
- this.apiRequest.callsArgWith(1, new Error('woops'), {
- statusCode: 500,
- })
+ this.apiRequest.rejects(new Error('woops'))
})
- it('should produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- done()
- })
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejected
})
})
})
describe('_paypal.createAccount', function () {
beforeEach(function () {
- this.call = callback => {
- this.RecurlyWrapper._paypal.createAccount(this.cache, callback)
+ this.call = () => {
+ return this.RecurlyWrapper.promises._paypal.createAccount(this.cache)
}
})
@@ -1119,11 +1043,8 @@ describe('RecurlyWrapper', function () {
this.cache.subscriptionDetails.address = null
})
- it('should produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- done()
- })
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejected
})
})
@@ -1132,11 +1053,8 @@ describe('RecurlyWrapper', function () {
this.cache.subscriptionDetails.address = {}
})
- it('should produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.be.instanceof(Errors.InvalidError)
- done()
- })
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejectedWith(Errors.InvalidError)
})
})
@@ -1146,38 +1064,28 @@ describe('RecurlyWrapper', function () {
this.cache.account = { account_code: 'abc' }
})
- it('should not produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.not.be.instanceof(Error)
- done()
+ it('should not produce an error', async function () {
+ await expect(this.call()).to.be.fulfilled
+ })
+
+ it('should produce cache object', async function () {
+ const result = await this.call()
+ expect(result).to.deep.equal(this.cache)
+ expect(result.account).to.deep.equal({
+ account_code: 'abc',
})
})
- it('should produce cache object', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- expect(result).to.deep.equal(this.cache)
- expect(result.account).to.deep.equal({
- account_code: 'abc',
- })
- done()
- })
+ it('should not call apiRequest', async function () {
+ await expect(this.call()).to.be.fulfilled
+ this.apiRequest.callCount.should.equal(0)
})
- it('should not call apiRequest', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- this.apiRequest.callCount.should.equal(0)
- done()
- })
- })
-
- it('should not call _parseAccountXml', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- this.RecurlyWrapper._parseAccountXml.callCount.should.equal(0)
- done()
- })
+ it('should not call _parseAccountXml', async function () {
+ await this.call()
+ this.RecurlyWrapper.promises._parseAccountXml.callCount.should.equal(
+ 0
+ )
})
})
@@ -1186,14 +1094,16 @@ describe('RecurlyWrapper', function () {
this.cache.userExists = false
const resultXml =
'abc'
- this.apiRequest.callsArgWith(1, null, { statusCode: 200 }, resultXml)
+ this.apiRequest.resolves({
+ response: { statusCode: 200 },
+ body: resultXml,
+ })
})
- it('sends correct XML', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- const { body } = this.apiRequest.lastCall.args[0]
- expect(body).to.equal(`\
+ it('sends correct XML', async function () {
+ await this.call()
+ const { body } = this.apiRequest.lastCall.args[0]
+ expect(body).to.equal(`\
some_id
foo@bar.com
@@ -1209,46 +1119,32 @@ describe('RecurlyWrapper', function () {
\
`)
- done()
- })
})
- it('should not produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.not.be.instanceof(Error)
- done()
- })
+ it('should not produce an error', async function () {
+ await expect(this.call()).to.be.fulfilled
})
- it('should call apiRequest', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- this.apiRequest.callCount.should.equal(1)
- this.apiRequest.firstCall.args[0].method.should.equal('POST')
- done()
- })
+ it('should call apiRequest', async function () {
+ await this.call()
+ this.apiRequest.callCount.should.equal(1)
+ this.apiRequest.firstCall.args[0].method.should.equal('POST')
})
- it('should call _parseAccountXml', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- this.RecurlyWrapper._parseAccountXml.callCount.should.equal(1)
- done()
- })
+ it('should call _parseAccountXml', async function () {
+ await this.call()
+ this.RecurlyWrapper.promises._parseAccountXml.callCount.should.equal(
+ 1
+ )
})
describe('when apiRequest produces an error', function () {
beforeEach(function () {
- this.apiRequest.callsArgWith(1, new Error('woops'), {
- statusCode: 500,
- })
+ this.apiRequest.rejects(new Error('woops'))
})
- it('should produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- done()
- })
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejectedWith('woops')
})
})
})
@@ -1257,102 +1153,9 @@ describe('RecurlyWrapper', function () {
describe('_paypal.createBillingInfo', function () {
beforeEach(function () {
this.cache.account = { account_code: 'abc' }
- this.call = callback => {
- this.RecurlyWrapper._paypal.createBillingInfo(this.cache, callback)
- }
- })
-
- describe('when account_code is missing from cache', function () {
- beforeEach(function () {
- this.cache.account.account_code = null
- })
-
- it('should produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- done()
- })
- })
- })
-
- describe('when all goes well', function () {
- beforeEach(function () {
- const resultXml = '1'
- this.apiRequest.callsArgWith(1, null, { statusCode: 200 }, resultXml)
- })
-
- it('sends correct XML', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- const { body } = this.apiRequest.lastCall.args[0]
- expect(body).to.equal(`\
-
- a-token-id
-\
-`)
- done()
- })
- })
-
- it('should not produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.not.be.instanceof(Error)
- done()
- })
- })
-
- it('should call apiRequest', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- this.apiRequest.callCount.should.equal(1)
- this.apiRequest.firstCall.args[0].method.should.equal('POST')
- done()
- })
- })
-
- it('should call _parseBillingInfoXml', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- this.RecurlyWrapper._parseBillingInfoXml.callCount.should.equal(1)
- done()
- })
- })
-
- it('should set billingInfo on cache', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- expect(result.billingInfo).to.deep.equal({
- a: '1',
- })
- done()
- })
- })
- })
-
- describe('when apiRequest produces an error', function () {
- beforeEach(function () {
- this.apiRequest.callsArgWith(1, new Error('woops'), {
- statusCode: 500,
- })
- })
-
- it('should produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- done()
- })
- })
- })
- })
-
- describe('_paypal.setAddressAndCompanyBillingInfo', function () {
- beforeEach(function () {
- this.cache.account = { account_code: 'abc' }
- this.cache.billingInfo = {}
- this.call = callback => {
- this.RecurlyWrapper._paypal.setAddressAndCompanyBillingInfo(
- this.cache,
- callback
+ this.call = () => {
+ return this.RecurlyWrapper.promises._paypal.createBillingInfo(
+ this.cache
)
}
})
@@ -1362,12 +1165,85 @@ describe('RecurlyWrapper', function () {
this.cache.account.account_code = null
})
- it('should produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- done()
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejected
+ })
+ })
+
+ describe('when all goes well', function () {
+ beforeEach(function () {
+ const resultXml = '1'
+ this.apiRequest.resolves({
+ response: { statusCode: 200 },
+ body: resultXml,
})
})
+
+ it('sends correct XML', async function () {
+ await this.call()
+ const { body } = this.apiRequest.lastCall.args[0]
+ expect(body).to.equal(`\
+
+ a-token-id
+\
+`)
+ })
+
+ it('should not produce an error', async function () {
+ await expect(this.call()).to.be.fulfilled
+ })
+
+ it('should call apiRequest', async function () {
+ await this.call()
+ this.apiRequest.callCount.should.equal(1)
+ this.apiRequest.firstCall.args[0].method.should.equal('POST')
+ })
+
+ it('should call _parseBillingInfoXml', async function () {
+ await this.call()
+ this.RecurlyWrapper.promises._parseBillingInfoXml.callCount.should.equal(
+ 1
+ )
+ })
+
+ it('should set billingInfo on cache', async function () {
+ const result = await this.call()
+ expect(result.billingInfo).to.deep.equal({
+ a: '1',
+ })
+ })
+ })
+
+ describe('when apiRequest produces an error', function () {
+ beforeEach(function () {
+ this.apiRequest.resolves(new Error('woops'))
+ })
+
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejected
+ })
+ })
+ })
+
+ describe('_paypal.setAddressAndCompanyBillingInfo', function () {
+ beforeEach(function () {
+ this.cache.account = { account_code: 'abc' }
+ this.cache.billingInfo = {}
+ this.call = () => {
+ return this.RecurlyWrapper.promises._paypal.setAddressAndCompanyBillingInfo(
+ this.cache
+ )
+ }
+ })
+
+ describe('when account_code is missing from cache', function () {
+ beforeEach(function () {
+ this.cache.account.account_code = null
+ })
+
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejected
+ })
})
describe('when country is missing', function () {
@@ -1375,25 +1251,24 @@ describe('RecurlyWrapper', function () {
this.cache.subscriptionDetails.address = { country: '' }
})
- it('should produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.be.instanceof(Errors.InvalidError)
- done()
- })
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejectedWith(Errors.InvalidError)
})
})
describe('when all goes well', function () {
beforeEach(function () {
const resultXml = 'London'
- this.apiRequest.callsArgWith(1, null, { statusCode: 200 }, resultXml)
+ this.apiRequest.resolves({
+ response: { statusCode: 200 },
+ body: resultXml,
+ })
})
- it('sends correct XML', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- const { body } = this.apiRequest.lastCall.args[0]
- expect(body).to.equal(`\
+ it('sends correct XML', async function () {
+ await this.call()
+ const { body } = this.apiRequest.lastCall.args[0]
+ expect(body).to.equal(`\
addr_one
addr_two
@@ -1403,57 +1278,40 @@ describe('RecurlyWrapper', function () {
some_country
\
`)
- done()
- })
})
- it('should not produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.not.be.instanceof(Error)
- done()
- })
+ it('should not produce an error', async function () {
+ await expect(this.call()).to.be.fulfilled
})
- it('should call apiRequest', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- this.apiRequest.callCount.should.equal(1)
- this.apiRequest.firstCall.args[0].method.should.equal('PUT')
- done()
- })
+ it('should call apiRequest', async function () {
+ await this.call()
+ this.apiRequest.callCount.should.equal(1)
+ this.apiRequest.firstCall.args[0].method.should.equal('PUT')
})
- it('should call _parseBillingInfoXml', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- this.RecurlyWrapper._parseBillingInfoXml.callCount.should.equal(1)
- done()
- })
+ it('should call _parseBillingInfoXml', async function () {
+ await this.call()
+ this.RecurlyWrapper.promises._parseBillingInfoXml.callCount.should.equal(
+ 1
+ )
})
- it('should set billingInfo on cache', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- expect(result.billingInfo).to.deep.equal({
- city: 'London',
- })
- done()
+ it('should set billingInfo on cache', async function () {
+ const result = await this.call()
+ expect(result.billingInfo).to.deep.equal({
+ city: 'London',
})
})
})
describe('when apiRequest produces an error', function () {
beforeEach(function () {
- this.apiRequest.callsArgWith(1, new Error('woops'), {
- statusCode: 500,
- })
+ this.apiRequest.rejects(new Error('woops'))
})
- it('should produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- done()
- })
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejected
})
})
})
@@ -1462,22 +1320,23 @@ describe('RecurlyWrapper', function () {
beforeEach(function () {
this.cache.account = { account_code: 'abc' }
this.cache.billingInfo = {}
- this.call = callback => {
- this.RecurlyWrapper._paypal.createSubscription(this.cache, callback)
- }
+ this.call = () =>
+ this.RecurlyWrapper.promises._paypal.createSubscription(this.cache)
})
describe('when all goes well', function () {
beforeEach(function () {
const resultXml = '1'
- this.apiRequest.callsArgWith(1, null, { statusCode: 200 }, resultXml)
+ this.apiRequest.resolves({
+ response: { statusCode: 200 },
+ body: resultXml,
+ })
})
- it('sends correct XML', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- const { body } = this.apiRequest.lastCall.args[0]
- expect(body).to.equal(`\
+ it('sends correct XML', async function () {
+ await this.call()
+ const { body } = this.apiRequest.lastCall.args[0]
+ expect(body).to.equal(`\
some_plan_code
EUR
@@ -1497,57 +1356,40 @@ describe('RecurlyWrapper', function () {
\
`)
- done()
- })
})
- it('should not produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.not.be.instanceof(Error)
- done()
- })
+ it('should not produce an error', async function () {
+ await expect(this.call()).to.be.fulfilled
})
- it('should call apiRequest', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- this.apiRequest.callCount.should.equal(1)
- this.apiRequest.firstCall.args[0].method.should.equal('POST')
- done()
- })
+ it('should call apiRequest', async function () {
+ await this.call()
+ this.apiRequest.callCount.should.equal(1)
+ this.apiRequest.firstCall.args[0].method.should.equal('POST')
})
- it('should call _parseSubscriptionXml', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- this.RecurlyWrapper._parseSubscriptionXml.callCount.should.equal(1)
- done()
- })
+ it('should call _parseSubscriptionXml', async function () {
+ await this.call()
+ this.RecurlyWrapper.promises._parseSubscriptionXml.callCount.should.equal(
+ 1
+ )
})
- it('should set subscription on cache', function (done) {
- this.call((err, result) => {
- if (err) return done(err)
- expect(result.subscription).to.deep.equal({
- a: '1',
- })
- done()
+ it('should set subscription on cache', async function () {
+ const result = await this.call()
+ expect(result.subscription).to.deep.equal({
+ a: '1',
})
})
})
describe('when apiRequest produces an error', function () {
beforeEach(function () {
- this.apiRequest.callsArgWith(1, new Error('woops'), {
- statusCode: 500,
- })
+ this.apiRequest.rejects(new Error('woops'))
})
- it('should produce an error', function (done) {
- this.call((err, result) => {
- expect(err).to.be.instanceof(Error)
- done()
- })
+ it('should produce an error', async function () {
+ await expect(this.call()).to.be.rejected
})
})
})
@@ -1556,29 +1398,28 @@ describe('RecurlyWrapper', function () {
describe('listAccountActiveSubscriptions', function () {
beforeEach(function () {
this.user_id = 'mock-user-id'
- this.callback = sinon.stub()
- this.RecurlyWrapper.apiRequest = sinon
+ this.response = { mock: 'response' }
+ this.body = ''
+ this.RecurlyWrapper.promises.apiRequest = sinon.stub().resolves({
+ response: this.response,
+ body: this.body,
+ })
+ this.subscriptions = ['mock', 'subscriptions']
+ this.RecurlyWrapper.promises._parseSubscriptionsXml = sinon
.stub()
- .yields(
- null,
- (this.response = { mock: 'response' }),
- (this.body = '')
- )
- this.RecurlyWrapper._parseSubscriptionsXml = sinon
- .stub()
- .yields(null, (this.subscriptions = ['mock', 'subscriptions']))
+ .resolves(this.subscriptions)
})
describe('with an account', function () {
- beforeEach(function () {
- this.RecurlyWrapper.listAccountActiveSubscriptions(
- this.user_id,
- this.callback
- )
+ beforeEach(async function () {
+ this.result =
+ await this.RecurlyWrapper.promises.listAccountActiveSubscriptions(
+ this.user_id
+ )
})
- it('should send a request to Recurly', function () {
- this.RecurlyWrapper.apiRequest
+ it('should send a request to Recurly', async function () {
+ this.RecurlyWrapper.promises.apiRequest
.calledWith({
url: `accounts/${this.user_id}/subscriptions`,
qs: {
@@ -1589,22 +1430,22 @@ describe('RecurlyWrapper', function () {
.should.equal(true)
})
- it('should return the subscriptions', function () {
- this.callback.calledWith(null, this.subscriptions).should.equal(true)
+ it('should return the subscriptions', async function () {
+ expect(this.result).to.deep.equal(this.subscriptions)
})
})
describe('without an account', function () {
- beforeEach(function () {
+ beforeEach(async function () {
this.response.statusCode = 404
- this.RecurlyWrapper.listAccountActiveSubscriptions(
- this.user_id,
- this.callback
- )
+ this.accountActiveSubscriptions =
+ await this.RecurlyWrapper.promises.listAccountActiveSubscriptions(
+ this.user_id
+ )
})
it('should return an empty array of subscriptions', function () {
- this.callback.calledWith(null, []).should.equal(true)
+ expect(this.accountActiveSubscriptions).to.deep.equal([])
})
})
})
diff --git a/services/web/test/unit/src/Subscription/SubscriptionHandlerTests.js b/services/web/test/unit/src/Subscription/SubscriptionHandlerTests.js
index bc84d2815f..11878a60b6 100644
--- a/services/web/test/unit/src/Subscription/SubscriptionHandlerTests.js
+++ b/services/web/test/unit/src/Subscription/SubscriptionHandlerTests.js
@@ -582,7 +582,7 @@ describe('SubscriptionHandler', function () {
)
})
- it('should call RecurlyWrapper.listAccountActiveSubscriptions with the user id', function () {
+ it('should call RecurlyWrapper.promises.listAccountActiveSubscriptions with the user id', function () {
this.RecurlyWrapper.promises.listAccountActiveSubscriptions
.calledWith(this.user_id)
.should.equal(true)