Merge pull request #2454 from overleaf/ta-recurly-address-check

Check Country Presence when Creating New Subscription

GitOrigin-RevId: a92266cf2a304e3649ff3b02f9b47e76ae0f8523
This commit is contained in:
Timothée Alby 2019-12-16 16:22:21 +05:30 committed by Copybot
parent 3f669510e5
commit 390c8641da
4 changed files with 81 additions and 32 deletions

View file

@ -24,6 +24,7 @@ const Settings = require('settings-sharelatex')
const xml2js = require('xml2js') const xml2js = require('xml2js')
const logger = require('logger-sharelatex') const logger = require('logger-sharelatex')
const Async = require('async') const Async = require('async')
const Errors = require('../Errors/Errors')
const SubscriptionErrors = require('./Errors') const SubscriptionErrors = require('./Errors')
module.exports = RecurlyWrapper = { module.exports = RecurlyWrapper = {
@ -79,28 +80,22 @@ module.exports = RecurlyWrapper = {
createAccount(cache, next) { createAccount(cache, next) {
const { user } = cache const { user } = cache
const { subscriptionDetails } = cache const { subscriptionDetails } = cache
const { address } = subscriptionDetails
if (!address) {
return next(
new Error('no address in subscriptionDetails at createAccount stage')
)
}
if (cache.userExists) { if (cache.userExists) {
return next(null, cache) return next(null, cache)
} }
let address
try {
address = getAddressFromSubscriptionDetails(subscriptionDetails)
} catch (error) {
return next(error)
}
const data = { const data = {
account_code: user._id, account_code: user._id,
email: user.email, email: user.email,
first_name: user.first_name, first_name: user.first_name,
last_name: user.last_name, last_name: user.last_name,
address: { address
address1: address.address1,
address2: address.address2 || '',
city: address.city || '',
state: address.state || '',
zip: address.zip || '',
country: address.country
}
} }
const requestBody = RecurlyWrapper._buildXml('account', data) const requestBody = RecurlyWrapper._buildXml('account', data)
@ -189,21 +184,14 @@ module.exports = RecurlyWrapper = {
if (!accountCode) { if (!accountCode) {
return next(new Error('no account code at setAddress stage')) return next(new Error('no account code at setAddress stage'))
} }
const { address } = subscriptionDetails
if (!address) { let address
return next( try {
new Error('no address in subscriptionDetails at setAddress stage') address = getAddressFromSubscriptionDetails(subscriptionDetails)
) } catch (error) {
return next(error)
} }
const data = { const requestBody = RecurlyWrapper._buildXml('billing_info', address)
address1: address.address1,
address2: address.address2 || '',
city: address.city || '',
state: address.state || '',
zip: address.zip || '',
country: address.country
}
const requestBody = RecurlyWrapper._buildXml('billing_info', data)
return RecurlyWrapper.apiRequest( return RecurlyWrapper.apiRequest(
{ {
@ -1051,6 +1039,31 @@ function getCustomFieldsFromSubscriptionDetails(subscriptionDetails) {
return { custom_field: customFields } return { custom_field: customFields }
} }
function getAddressFromSubscriptionDetails(subscriptionDetails) {
const { address } = subscriptionDetails
if (!address || !address.country) {
throw new Errors.InvalidError({
message: 'Invalid country',
info: {
public: {
message: 'Invalid country'
}
}
})
}
const addressObject = {
address1: address.address1,
address2: address.address2 || '',
city: address.city || '',
state: address.state || '',
zip: address.zip || '',
country: address.country
}
return addressObject
}
function __guard__(value, transform) { function __guard__(value, transform) {
return typeof value !== 'undefined' && value !== null return typeof value !== 'undefined' && value !== null
? transform(value) ? transform(value)

View file

@ -29,6 +29,7 @@ const FeaturesUpdater = require('./FeaturesUpdater')
const planFeatures = require('./planFeatures') const planFeatures = require('./planFeatures')
const GroupPlansData = require('./GroupPlansData') const GroupPlansData = require('./GroupPlansData')
const V1SubscriptionManager = require('./V1SubscriptionManager') const V1SubscriptionManager = require('./V1SubscriptionManager')
const Errors = require('../Errors/Errors')
const SubscriptionErrors = require('./Errors') const SubscriptionErrors = require('./Errors')
const HttpErrors = require('@overleaf/o-error/http') const HttpErrors = require('@overleaf/o-error/http')
@ -219,6 +220,10 @@ module.exports = SubscriptionController = {
return next( return next(
new HttpErrors.UnprocessableEntityError({}).withCause(err) new HttpErrors.UnprocessableEntityError({}).withCause(err)
) )
} else if (err instanceof Errors.InvalidError) {
return next(
new HttpErrors.UnprocessableEntityError({}).withCause(err)
)
} }
logger.warn( logger.warn(

View file

@ -21,6 +21,7 @@ const querystring = require('querystring')
const modulePath = '../../../../app/src/Features/Subscription/RecurlyWrapper' const modulePath = '../../../../app/src/Features/Subscription/RecurlyWrapper'
const SandboxedModule = require('sandboxed-module') const SandboxedModule = require('sandboxed-module')
const tk = require('timekeeper') const tk = require('timekeeper')
const Errors = require('../../../../app/src/Features/Errors/Errors')
const SubscriptionErrors = require('../../../../app/src/Features/Subscription/Errors') const SubscriptionErrors = require('../../../../app/src/Features/Subscription/Errors')
const fixtures = { const fixtures = {
@ -158,7 +159,8 @@ describe('RecurlyWrapper', function() {
}, },
request: sinon.stub(), request: sinon.stub(),
xml2js: require('xml2js'), xml2js: require('xml2js'),
'./Errors': SubscriptionErrors './Errors': SubscriptionErrors,
'../Errors/Errors': Errors
} }
} }
)) ))
@ -1148,6 +1150,19 @@ describe('RecurlyWrapper', function() {
}) })
}) })
describe('when country is missing from address', function() {
beforeEach(function() {
return (this.cache.subscriptionDetails.address = {})
})
it('should produce an error', function(done) {
return this.call((err, result) => {
expect(err).to.be.instanceof(Errors.InvalidError)
return done()
})
})
})
describe('when account already exists', function() { describe('when account already exists', function() {
beforeEach(function() { beforeEach(function() {
this.cache.userExists = true this.cache.userExists = true
@ -1378,14 +1393,14 @@ describe('RecurlyWrapper', function() {
}) })
}) })
describe('when address is missing from subscriptionDetails', function() { describe('when country is missing', function() {
beforeEach(function() { beforeEach(function() {
return (this.cache.subscriptionDetails.address = null) return (this.cache.subscriptionDetails.address = { country: '' })
}) })
it('should produce an error', function(done) { it('should produce an error', function(done) {
return this.call((err, result) => { return this.call((err, result) => {
expect(err).to.be.instanceof(Error) expect(err).to.be.instanceof(Errors.InvalidError)
return done() return done()
}) })
}) })

View file

@ -19,6 +19,7 @@ const MockRequest = require('../helpers/MockRequest')
const MockResponse = require('../helpers/MockResponse') const MockResponse = require('../helpers/MockResponse')
const modulePath = const modulePath =
'../../../../app/src/Features/Subscription/SubscriptionController' '../../../../app/src/Features/Subscription/SubscriptionController'
const Errors = require('../../../../app/src/Features/Errors/Errors')
const SubscriptionErrors = require('../../../../app/src/Features/Subscription/Errors') const SubscriptionErrors = require('../../../../app/src/Features/Subscription/Errors')
const OError = require('@overleaf/o-error') const OError = require('@overleaf/o-error')
const HttpErrors = require('@overleaf/o-error/http') const HttpErrors = require('@overleaf/o-error/http')
@ -118,6 +119,7 @@ describe('SubscriptionController', function() {
'./FeaturesUpdater': (this.FeaturesUpdater = {}), './FeaturesUpdater': (this.FeaturesUpdater = {}),
'./GroupPlansData': (this.GroupPlansData = {}), './GroupPlansData': (this.GroupPlansData = {}),
'./V1SubscriptionManager': (this.V1SubscriptionManager = {}), './V1SubscriptionManager': (this.V1SubscriptionManager = {}),
'../Errors/Errors': Errors,
'./Errors': SubscriptionErrors, './Errors': SubscriptionErrors,
'@overleaf/o-error/http': HttpErrors '@overleaf/o-error/http': HttpErrors
} }
@ -433,6 +435,20 @@ describe('SubscriptionController', function() {
}) })
return done() return done()
}) })
it('should handle validation errors', function(done) {
this.next = sinon.stub()
this.LimitationsManager.userHasV1OrV2Subscription.yields(null, false)
this.SubscriptionHandler.createSubscription.yields(
new Errors.InvalidError({})
)
this.SubscriptionController.createSubscription(this.req, null, error => {
expect(error).to.exist
expect(error).to.be.instanceof(HttpErrors.UnprocessableEntityError)
expect(OError.hasCauseInstanceOf(error, Errors.InvalidError)).to.be.true
})
return done()
})
}) })
describe('updateSubscription via post', function() { describe('updateSubscription via post', function() {