diff --git a/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee b/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee
index 680905ca6c..edf4dc8841 100644
--- a/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee
+++ b/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee
@@ -4,11 +4,140 @@ request = require 'request'
Settings = require "settings-sharelatex"
xml2js = require "xml2js"
logger = require("logger-sharelatex")
+Async = require('async')
module.exports = RecurlyWrapper =
apiUrl : "https://api.recurly.com/v2"
- createSubscription: (user, subscriptionDetails, recurly_token_id, callback)->
+ _createPaypalSubscription: (user, subscriptionDetails, recurly_token_id, callback) ->
+ logger.log {user_id: user._id, recurly_token_id}, "starting process of creating paypal subscription"
+ Async.waterfall([
+ (next) -> # create account
+ 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) =>
+ return next(error) if 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}
+ 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) =>
+ return next(error) if 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 = """
+
+ #{address.address1}
+ #{address.address2}
+ #{address.city}
+ #{address.state}
+ #{address.zip}
+ #{address.country}
+
+ """
+ RecurlyWrapper.apiRequest({
+ url: "accounts/#{accountCode}/billing_info"
+ method: "PUT"
+ body: requestBody
+ }, (error, response, responseBody) =>
+ return next(error) if 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) =>
+ return next(error) if 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)
+ )
+
+ _createCreditCardSubscription: (user, subscriptionDetails, recurly_token_id, callback) ->
requestBody = """
#{subscriptionDetails.plan_code}
@@ -32,7 +161,13 @@ module.exports = RecurlyWrapper =
}, (error, response, responseBody) =>
return callback(error) if error?
@_parseSubscriptionXml responseBody, callback
- )
+ )
+
+ createSubscription: (user, subscriptionDetails, recurly_token_id, callback)->
+ isPaypal = subscriptionDetails.isPaypal
+ logger.log {user_id: user._id, isPaypal, recurly_token_id}, "setting up subscription in recurly"
+ fn = if isPaypal then RecurlyWrapper._createPaypalSubscription else RecurlyWrapper._createCreditCardSubscription
+ fn user, subscriptionDetails, recurly_token_id, callback
apiRequest : (options, callback) ->
options.url = @apiUrl + "/" + options.url
@@ -60,7 +195,7 @@ module.exports = RecurlyWrapper =
newAttributes[key] = value
else
newAttributes[newKey] = value
-
+
return newAttributes
crypto.randomBytes 32, (error, buffer) ->
@@ -74,7 +209,7 @@ module.exports = RecurlyWrapper =
signature = "#{signed}|#{unsignedQuery}"
callback null, signature
-
+
getSubscriptions: (accountId, callback)->
@apiRequest({
@@ -227,7 +362,7 @@ module.exports = RecurlyWrapper =
}, (error, response, body) ->
callback(error)
)
-
+
redeemCoupon: (account_code, coupon_code, callback)->
requestBody = """
@@ -278,6 +413,15 @@ module.exports = RecurlyWrapper =
return callback "I don't understand the response from Recurly"
callback null, account
+ _parseBillingInfoXml: (xml, callback) ->
+ @_parseXml xml, (error, data) ->
+ return callback(error) if error?
+ if data? and data.account?
+ billingInfo = data.billing_info
+ else
+ return callback "I don't understand the response from Recurly"
+ callback null, billingInfo
+
_parseXml: (xml, callback) ->
convertDataTypes = (data) ->
if data? and data["$"]?
@@ -299,7 +443,7 @@ module.exports = RecurlyWrapper =
else
array.push(convertDataTypes(value))
data = array
-
+
if data instanceof Array
data = (convertDataTypes(entry) for entry in data)
else if typeof data == "object"
@@ -315,6 +459,3 @@ module.exports = RecurlyWrapper =
return callback(error) if error?
result = convertDataTypes(data)
callback null, result
-
-
-
diff --git a/services/web/public/coffee/main/new-subscription.coffee b/services/web/public/coffee/main/new-subscription.coffee
index 404b512e7b..9547508337 100644
--- a/services/web/public/coffee/main/new-subscription.coffee
+++ b/services/web/public/coffee/main/new-subscription.coffee
@@ -115,6 +115,13 @@ define [
currencyCode:pricing.items.currency
plan_code:pricing.items.plan.code
coupon_code:pricing.items?.coupon?.code || ""
+ isPaypal: $scope.paymentMethod == 'paypal'
+ address:
+ address1: $scope.data.address1
+ address2: $scope.data.address2
+ country: $scope.data.country
+ state: $scope.data.state
+ zip: $scope.data.zip
$http.post("/user/subscription/create", postData)
.success (data, status, headers)->
sixpack.convert "in-editor-free-trial-plan", pricing.items.plan.code, (err)->