diff --git a/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee b/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee
index 41435eabe4..841ee0a77e 100644
--- a/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee
+++ b/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee
@@ -18,160 +18,178 @@ module.exports = RecurlyWrapper =
resultString += "\n"
return resultString
+ _paypal:
+ checkAccountExists: (cache, next) ->
+ user = cache.user
+ recurly_token_id = cache.recurly_token_id
+ subscriptionDetails = cache.subscriptionDetails
+ logger.log {user_id: user._id, recurly_token_id}, "checking if recurly account exists for user"
+ RecurlyWrapper.apiRequest({
+ url: "accounts/#{user._id}"
+ method: "GET"
+ }, (error, response, responseBody) ->
+ if error
+ if response.statusCode == 404 # actually not an error in this case, just no existing account
+ cache.userExists = false
+ return next(null, cache)
+ logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while checking account"
+ return next(error)
+ logger.log {user_id: user._id, recurly_token_id}, "user appears to exist in recurly"
+ RecurlyWrapper._parseAccountXml responseBody, (err, account) ->
+ if err
+ logger.error {err, user_id: user._id, recurly_token_id}, "error parsing account"
+ return next(err)
+ cache.account = account
+ return next(null, cache)
+ )
+ createAccount: (cache, next) ->
+ user = cache.user
+ recurly_token_id = cache.recurly_token_id
+ subscriptionDetails = cache.subscriptionDetails
+ if cache.userExists
+ logger.log {user_id: user._id, recurly_token_id}, "user already exists in recurly"
+ return next(null, cache)
+ logger.log {user_id: user._id, recurly_token_id}, "creating user in recurly"
+ address = subscriptionDetails.address
+ if !address
+ return next(new Error('no address in subscriptionDetails at createAccount stage'))
+ requestBody = """
+
+ #{user._id}
+ #{user.email}
+ #{user.first_name}
+ #{user.last_name}
+
+ #{address.address1}
+ #{address.address2}
+ #{address.city || ''}
+ #{address.state || ''}
+ #{address.zip || ''}
+ #{address.country}
+
+
+ """
+ RecurlyWrapper.apiRequest({
+ url : "accounts"
+ method : "POST"
+ body : requestBody
+ }, (error, response, responseBody) =>
+ if error
+ logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while creating account"
+ return next(error)
+ RecurlyWrapper._parseAccountXml responseBody, (err, account) ->
+ if err
+ logger.error {err, user_id: user._id, recurly_token_id}, "error creating account"
+ return next(err)
+ cache.account = account
+ return next(null, cache)
+ )
+ createBillingInfo: (cache, next) ->
+ user = cache.user
+ recurly_token_id = cache.recurly_token_id
+ subscriptionDetails = cache.subscriptionDetails
+ logger.log {user_id: user._id, recurly_token_id}, "creating billing info in recurly"
+ accountCode = cache?.account?.account_code
+ if !accountCode
+ return next(new Error('no account code at createBillingInfo stage'))
+ requestBody = """
+
+ #{recurly_token_id}
+
+ """
+ RecurlyWrapper.apiRequest({
+ url: "accounts/#{accountCode}/billing_info"
+ method: "POST"
+ body: requestBody
+ }, (error, response, responseBody) =>
+ if error
+ logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while creating billing info"
+ return next(error)
+ RecurlyWrapper._parseBillingInfoXml responseBody, (err, billingInfo) ->
+ if err
+ logger.error {err, user_id: user._id, accountCode, recurly_token_id}, "error creating billing info"
+ return next(err)
+ cache.billingInfo = billingInfo
+ return next(null, cache)
+ )
+
+ setAddress: (cache, next) ->
+ user = cache.user
+ recurly_token_id = cache.recurly_token_id
+ subscriptionDetails = cache.subscriptionDetails
+ logger.log {user_id: user._id, recurly_token_id}, "setting billing address in recurly"
+ accountCode = cache?.account?.account_code
+ if !accountCode
+ return next(new Error('no account code at setAddress stage'))
+ address = subscriptionDetails.address
+ if !address
+ return next(new Error('no address in subscriptionDetails at setAddress stage'))
+ requestBody = RecurlyWrapper._addressToXml(address)
+ RecurlyWrapper.apiRequest({
+ url: "accounts/#{accountCode}/billing_info"
+ method: "PUT"
+ body: requestBody
+ }, (error, response, responseBody) =>
+ if error
+ logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while setting address"
+ return next(error)
+ RecurlyWrapper._parseBillingInfoXml responseBody, (err, billingInfo) ->
+ if err
+ logger.error {err, user_id: user._id, recurly_token_id}, "error updating billing info"
+ return next(err)
+ cache.billingInfo = billingInfo
+ return next(null, cache)
+ )
+ createSubscription: (cache, next) ->
+ user = cache.user
+ recurly_token_id = cache.recurly_token_id
+ subscriptionDetails = cache.subscriptionDetails
+ logger.log {user_id: user._id, recurly_token_id}, "creating subscription in recurly"
+ requestBody = """
+
+ #{subscriptionDetails.plan_code}
+ #{subscriptionDetails.currencyCode}
+ #{subscriptionDetails.coupon_code}
+
+ #{user._id}
+
+
+ """ # TODO: check account details and billing
+ RecurlyWrapper.apiRequest({
+ url : "subscriptions"
+ method : "POST"
+ body : requestBody
+ }, (error, response, responseBody) =>
+ if error
+ logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while creating subscription"
+ return next(error)
+ RecurlyWrapper._parseSubscriptionXml responseBody, (err, subscription) ->
+ if err
+ logger.error {err, user_id: user._id, recurly_token_id}, "error creating subscription"
+ return next(err)
+ cache.subscription = subscription
+ return next(null, cache)
+ )
+
_createPaypalSubscription: (user, subscriptionDetails, recurly_token_id, callback) ->
logger.log {user_id: user._id, recurly_token_id}, "starting process of creating paypal subscription"
+ cache = {user, recurly_token_id, subscriptionDetails}
Async.waterfall([
- (next) -> # check if account exists
- logger.log {user_id: user._id, recurly_token_id}, "checking if recurly account exists for user"
- RecurlyWrapper.apiRequest({
- url: "accounts/#{user._id}"
- method: "GET"
- }, (error, response, responseBody) ->
- result = {userExists: true}
- if error
- if response.statusCode == 404 # actually not an error in this case, just no existing account
- result.userExists = false
- return next(null, result)
- logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while checking account"
- return next(error)
- logger.log {user_id: user._id, recurly_token_id}, "user appears to exist in recurly"
- RecurlyWrapper._parseAccountXml responseBody, (err, account) ->
- if err
- logger.error {err, user_id: user._id, recurly_token_id}, "error parsing account"
- return next(err)
- result.account = account
- return next(null, result)
- )
-
- , (result, next) -> # create account
- if result.userExists
- logger.log {user_id: user._id, recurly_token_id}, "user already exists in recurly"
- return next(null, result)
- logger.log {user_id: user._id, recurly_token_id}, "creating user in recurly"
- address = subscriptionDetails.address
- if !address
- return next(new Error('no address in subscriptionDetails at createAccount stage'))
- requestBody = """
-
- #{user._id}
- #{user.email}
- #{user.first_name}
- #{user.last_name}
-
- #{address.address1}
- #{address.address2}
- #{address.city || ''}
- #{address.state || ''}
- #{address.zip || ''}
- #{address.country}
-
-
- """
- RecurlyWrapper.apiRequest({
- url : "accounts"
- method : "POST"
- body : requestBody
- }, (error, response, responseBody) =>
- if error
- logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while creating account"
- return next(error)
- RecurlyWrapper._parseAccountXml responseBody, (err, account) ->
- if err
- logger.error {err, user_id: user._id, recurly_token_id}, "error creating account"
- return next(err)
- result.account = account
- return next(null, result)
- )
-
- , (result, next) -> # create billing info
- logger.log {user_id: user._id, recurly_token_id}, "creating billing info in recurly"
- accountCode = result?.account?.account_code
- if !accountCode
- return next(new Error('no account code at createBillingInfo stage'))
- requestBody = """
-
- #{recurly_token_id}
-
- """
- RecurlyWrapper.apiRequest({
- url: "accounts/#{accountCode}/billing_info"
- method: "POST"
- body: requestBody
- }, (error, response, responseBody) =>
- if error
- logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while creating billing info"
- return next(error)
- RecurlyWrapper._parseBillingInfoXml responseBody, (err, billingInfo) ->
- if err
- logger.error {err, user_id: user._id, accountCode, recurly_token_id}, "error creating billing info"
- return next(err)
- result.billingInfo = billingInfo
- return next(null, result)
- )
-
- , (result, next) -> # set address
- logger.log {user_id: user._id, recurly_token_id}, "setting billing address in recurly"
- accountCode = result?.account?.account_code
- if !accountCode
- return next(new Error('no account code at setAddress stage'))
- address = subscriptionDetails.address
- if !address
- return next(new Error('no address in subscriptionDetails at setAddress stage'))
- requestBody = RecurlyWrapper._addressToXml(address)
- RecurlyWrapper.apiRequest({
- url: "accounts/#{accountCode}/billing_info"
- method: "PUT"
- body: requestBody
- }, (error, response, responseBody) =>
- if error
- logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while setting address"
- return next(error)
- RecurlyWrapper._parseBillingInfoXml responseBody, (err, billingInfo) ->
- if err
- logger.error {err, user_id: user._id, recurly_token_id}, "error updating billing info"
- return next(err)
- result.billingInfo = billingInfo
- return next(null, result)
- )
-
- , (result, next) -> # create subscription
- logger.log {user_id: user._id, recurly_token_id}, "creating subscription in recurly"
- requestBody = """
-
- #{subscriptionDetails.plan_code}
- #{subscriptionDetails.currencyCode}
- #{subscriptionDetails.coupon_code}
-
- #{user._id}
-
-
- """ # TODO: check account details and billing
- RecurlyWrapper.apiRequest({
- url : "subscriptions"
- method : "POST"
- body : requestBody
- }, (error, response, responseBody) =>
- if error
- logger.error {error, user_id: user._id, recurly_token_id}, "error response from recurly while creating subscription"
- return next(error)
- RecurlyWrapper._parseSubscriptionXml responseBody, (err, subscription) ->
- if err
- logger.error {err, user_id: user._id, recurly_token_id}, "error creating subscription"
- return next(err)
- result.subscription = subscription
- return next(null, result)
- )
-
- ], (err, result) ->
- if err
- logger.error {err, user_id: user._id, recurly_token_id}, "error in paypal subscription creation process"
- return callback(err)
- if !result.subscription
- err = new Error('no subscription object in result')
- logger.error {err, user_id: user._id, recurly_token_id}, "error in paypal subscription creation process"
- return callback(err)
- logger.log {user_id: user._id, recurly_token_id}, "done creating paypal subscription for user"
- callback(null, result.subscription)
+ Async.apply(RecurlyWrapper._paypal.checkAccountExists, cache),
+ RecurlyWrapper._paypal.createAccount,
+ RecurlyWrapper._paypal.createBillingInfo,
+ RecurlyWrapper._paypal.setAddress,
+ RecurlyWrapper._paypal.createSubscription,
+ ], (err, result) ->
+ if err
+ logger.error {err, user_id: user._id, recurly_token_id}, "error in paypal subscription creation process"
+ return callback(err)
+ if !result.subscription
+ err = new Error('no subscription object in result')
+ logger.error {err, user_id: user._id, recurly_token_id}, "error in paypal subscription creation process"
+ return callback(err)
+ logger.log {user_id: user._id, recurly_token_id}, "done creating paypal subscription for user"
+ callback(null, result.subscription)
)
_createCreditCardSubscription: (user, subscriptionDetails, recurly_token_id, callback) ->
diff --git a/services/web/test/UnitTests/coffee/Subscription/RecurlyWrapperTests.coffee b/services/web/test/UnitTests/coffee/Subscription/RecurlyWrapperTests.coffee
index fd8bbe25ed..e9dae2cd66 100644
--- a/services/web/test/UnitTests/coffee/Subscription/RecurlyWrapperTests.coffee
+++ b/services/web/test/UnitTests/coffee/Subscription/RecurlyWrapperTests.coffee
@@ -516,6 +516,16 @@ describe "RecurlyWrapper", ->
expect(sub).to.equal @subscription
done()
+ it 'should call apiRequest', (done) ->
+ @call (err, sub) =>
+ @apiRequest.callCount.should.equal 1
+ done()
+
+ it 'should call _parseSubscriptionXml', (done) ->
+ @call (err, sub) =>
+ @_parseSubscriptionXml.callCount.should.equal 1
+ done()
+
describe 'when api request produces an error', ->
beforeEach ->
@@ -526,6 +536,16 @@ describe "RecurlyWrapper", ->
expect(err).to.be.instanceof Error
done()
+ it 'should call apiRequest', (done) ->
+ @call (err, sub) =>
+ @apiRequest.callCount.should.equal 1
+ done()
+
+ it 'should not _parseSubscriptionXml', (done) ->
+ @call (err, sub) =>
+ @_parseSubscriptionXml.callCount.should.equal 0
+ done()
+
describe 'when parse xml produces an error', ->
beforeEach ->
@@ -539,7 +559,69 @@ describe "RecurlyWrapper", ->
describe '_createPaypalSubscription', ->
beforeEach ->
+ @checkAccountExists = sinon.stub(@RecurlyWrapper._paypal, 'checkAccountExists')
+ @createAccount = sinon.stub(@RecurlyWrapper._paypal, 'createAccount')
+ @createBillingInfo = sinon.stub(@RecurlyWrapper._paypal, 'createBillingInfo')
+ @setAddress = sinon.stub(@RecurlyWrapper._paypal, 'setAddress')
+ @createSubscription = sinon.stub(@RecurlyWrapper._paypal, 'createSubscription')
+ @user =
+ _id: 'some_id'
+ email: 'user@example.com'
+ @subscriptionDetails =
+ currencyCode: "EUR"
+ plan_code: "some_plan_code"
+ coupon_code: ""
+ isPaypal: true
+ address:
+ address1: "addr_one"
+ address2: "addr_two"
+ country: "some_country"
+ state: "some_state"
+ zip: "some_zip"
+ @subscription = {}
+ @recurly_token_id = "a-token-id"
- describe 'when all goes well', ->
+ # set up data callbacks
+ user = @user
+ subscriptionDetails = @subscriptionDetails
+ recurly_token_id = @recurly_token_id
- beforeEach ->
+ @checkAccountExists.callsArgWith(1, null,
+ {user, subscriptionDetails, recurly_token_id,
+ userExists: false, account: {accountCode: 'xx'}}
+ )
+
+ @createAccount.callsArgWith(1, null,
+ {user, subscriptionDetails, recurly_token_id,
+ userExists: false, account: {accountCode: 'xx'}}
+ )
+
+ @createBillingInfo.callsArgWith(1, null,
+ {user, subscriptionDetails, recurly_token_id,
+ userExists: false, account: {accountCode: 'xx'}, billingInfo: {token_id: 'abc'}}
+ )
+
+ @setAddress.callsArgWith(1, null,
+ {user, subscriptionDetails, recurly_token_id,
+ userExists: false, account: {accountCode: 'xx'}, billingInfo: {token_id: 'abc'}}
+ )
+
+ @createSubscription.callsArgWith(1, null,
+ {user, subscriptionDetails, recurly_token_id,
+ userExists: false, account: {accountCode: 'xx'}, billingInfo: {token_id: 'abc'}, subscription: {}}
+ )
+
+ @call = (callback) =>
+ @RecurlyWrapper._createPaypalSubscription @user, @subscriptionDetails, @recurly_token_id, callback
+
+ afterEach ->
+ @checkAccountExists.restore()
+ @createAccount.restore()
+ @createBillingInfo.restore()
+ @setAddress.restore()
+ @createSubscription.restore()
+
+ it 'should not produce an error', (done) ->
+ @call (err, sub) =>
+ expect(err).to.not.be.instanceof Error
+ done()