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)->