diff --git a/services/web/app/src/Features/Subscription/RecurlyWrapper.js b/services/web/app/src/Features/Subscription/RecurlyWrapper.js index b29d7f65ab..6ec1451d4e 100644 --- a/services/web/app/src/Features/Subscription/RecurlyWrapper.js +++ b/services/web/app/src/Features/Subscription/RecurlyWrapper.js @@ -86,7 +86,7 @@ module.exports = RecurlyWrapper = { let address try { - address = getAddressFromSubscriptionDetails(subscriptionDetails) + address = getAddressFromSubscriptionDetails(subscriptionDetails, false) } catch (error) { return next(error) } @@ -173,25 +173,36 @@ module.exports = RecurlyWrapper = { ) }, - setAddress(cache, next) { + setAddressAndCompanyBillingInfo(cache, next) { const { user } = cache const { subscriptionDetails } = cache - logger.log({ user_id: user._id }, 'setting billing address in recurly') + logger.log( + { user_id: user._id }, + 'setting billing address and company info in recurly' + ) const accountCode = __guard__( cache != null ? cache.account : undefined, x1 => x1.account_code ) if (!accountCode) { - return next(new Error('no account code at setAddress stage')) + return next( + new Error('no account code at setAddressAndCompanyBillingInfo stage') + ) } - let address + let addressAndCompanyBillingInfo try { - address = getAddressFromSubscriptionDetails(subscriptionDetails) + addressAndCompanyBillingInfo = getAddressFromSubscriptionDetails( + subscriptionDetails, + true + ) } catch (error) { return next(error) } - const requestBody = RecurlyWrapper._buildXml('billing_info', address) + const requestBody = RecurlyWrapper._buildXml( + 'billing_info', + addressAndCompanyBillingInfo + ) return RecurlyWrapper.apiRequest( { @@ -296,7 +307,7 @@ module.exports = RecurlyWrapper = { Async.apply(RecurlyWrapper._paypal.checkAccountExists, cache), RecurlyWrapper._paypal.createAccount, RecurlyWrapper._paypal.createBillingInfo, - RecurlyWrapper._paypal.setAddress, + RecurlyWrapper._paypal.setAddressAndCompanyBillingInfo, RecurlyWrapper._paypal.createSubscription ], function(err, result) { @@ -1048,8 +1059,12 @@ function getCustomFieldsFromSubscriptionDetails(subscriptionDetails) { return { custom_field: customFields } } -function getAddressFromSubscriptionDetails(subscriptionDetails) { +function getAddressFromSubscriptionDetails( + subscriptionDetails, + includeCompanyInfo +) { const { address } = subscriptionDetails + if (!address || !address.country) { throw new Errors.InvalidError({ message: 'Invalid country', @@ -1070,6 +1085,21 @@ function getAddressFromSubscriptionDetails(subscriptionDetails) { country: address.country } + if ( + includeCompanyInfo && + subscriptionDetails.billing_info && + subscriptionDetails.billing_info.company && + subscriptionDetails.billing_info.company !== '' + ) { + addressObject.company = subscriptionDetails.billing_info.company + if ( + subscriptionDetails.billing_info.vat_number && + subscriptionDetails.billing_info.vat_number !== '' + ) { + addressObject.vat_number = subscriptionDetails.billing_info.vat_number + } + } + return addressObject } diff --git a/services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly.pug b/services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly.pug index 579281ab89..78a046d8e7 100644 --- a/services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly.pug +++ b/services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly.pug @@ -8,6 +8,7 @@ div(ng-controller="RecurlySubscriptionController") case personalSubscription.recurly.state when "active" p !{translate("currently_subscribed_to_plan", {planName:"" + personalSubscription.plan.name + ""})} + | a(href, ng-click="switchToChangePlanView()", ng-if="showChangePlanButton") !{translate("change_plan")}. -if (personalSubscription.recurly.trialEndsAtFormatted && personalSubscription.recurly.trial_ends_at > Date.now()) p You're on a free trial which ends on #{personalSubscription.recurly.trialEndsAtFormatted} diff --git a/services/web/app/views/subscriptions/new.pug b/services/web/app/views/subscriptions/new.pug index 42f8fd54b5..974237bc4b 100644 --- a/services/web/app/views/subscriptions/new.pug +++ b/services/web/app/views/subscriptions/new.pug @@ -62,7 +62,6 @@ block content novalidate ) - div.payment-method-toggle a.payment-method-toggle-switch( href @@ -165,22 +164,42 @@ block content ng-model="data.country" name="country" ng-change="updateCountry()" - required, ng-selected="{{country.code == data.country}}" + ng-model-options="{ debounce: 200 }" + required ) option(value='', disabled) #{translate("country")} option(value='-', disabled) -------------- option(ng-repeat="country in countries" ng-bind-html="country.name" value="{{country.code}}") span.input-feedback-message {{ simpleCCForm.country.$error.required ? 'This field is required' : '' }} - if (showVatField) - .form-group - label(for="vat-no") #{translate('vat_number')} - input#vat-no.form-control( - type="text" - ng-blur="applyVatNumber()" - ng-model="data.vat_number" - ) + .form-group + .checkbox + label + input( + type="checkbox" + ng-model="ui.addCompanyDetails" + ) + | + | #{translate("add_company_details")} + + .form-group(ng-show="ui.addCompanyDetails") + label(for="company-name") #{translate("company_name")} + input#company-name.form-control( + type="text" + name="companyName" + ng-model="data.company" + ) + + .form-group(ng-show="ui.addCompanyDetails && price.taxes.length") + label(for="vat-number") #{translate("vat_number")} + input#vat-number.form-control( + type="text" + name="vatNumber" + ng-model="data.vat_number" + ng-blur="applyVatNumber()" + ) + if (showCouponField) .form-group label(for="coupon-code") #{translate('coupon_code')} @@ -192,11 +211,19 @@ block content p(ng-if="paymentMethod.value === 'paypal'") #{translate("paypal_upgrade")} - div.price-breakdown(ng-if="price.next.tax !== '0.00'") + div.price-breakdown( + ng-show="price.taxes.length" + ) hr.thin - span Total: - strong {{availableCurrencies[currencyCode]['symbol']}}{{price.next.total}} - span ({{availableCurrencies[currencyCode]['symbol']}}{{price.next.subtotal}} + {{availableCurrencies[currencyCode]['symbol']}}{{price.next.tax}} tax) + span + | Total: + | + strong + | {{availableCurrencies[currencyCode]['symbol']}}{{price.next.total}} + | + span + | ({{availableCurrencies[currencyCode]['symbol']}}{{price.next.subtotal}} + {{availableCurrencies[currencyCode]['symbol']}}{{price.next.tax}} tax) + | span(ng-if="monthlyBilling") #{translate("every")} #{translate("month")} span(ng-if="!monthlyBilling") #{translate("every")} #{translate("year")} hr.thin diff --git a/services/web/frontend/js/main/new-subscription.js b/services/web/frontend/js/main/new-subscription.js index 8997d25501..1fa5157161 100644 --- a/services/web/frontend/js/main/new-subscription.js +++ b/services/web/frontend/js/main/new-subscription.js @@ -17,6 +17,10 @@ define(['../base', '../directives/creditCards'], App => return } + $scope.ui = { + addCompanyDetails: false + } + $scope.recurlyLoadError = false $scope.currencyCode = MultiCurrencyPricing.currencyCode $scope.allCurrencies = MultiCurrencyPricing.plans @@ -58,6 +62,8 @@ define(['../base', '../directives/creditCards'], App => address2: '', state: '', city: '', + company: '', + vat_number: '', country: window.countryCode, coupon: window.couponCode } @@ -99,7 +105,9 @@ define(['../base', '../directives/creditCards'], App => pricing .plan(window.plan_code, { quantity: 1 }) - .address({ country: $scope.data.country }) + .address({ + country: $scope.data.country + }) .tax({ tax_code: 'digital', vat_number: '' }) .currency($scope.currencyCode) .coupon($scope.data.coupon) @@ -219,7 +227,6 @@ define(['../base', '../directives/creditCards'], App => coupon_code: pricing.items.coupon ? pricing.items.coupon.code : '', first_name: $scope.data.first_name, last_name: $scope.data.last_name, - isPaypal: $scope.paymentMethod.value === 'paypal', address: { address1: $scope.data.address1, @@ -233,6 +240,21 @@ define(['../base', '../directives/creditCards'], App => } } + if ( + postData.subscriptionDetails.isPaypal && + $scope.ui.addCompanyDetails + ) { + postData.subscriptionDetails.billing_info = {} + if ($scope.data.company && $scope.data.company !== '') { + postData.subscriptionDetails.billing_info.company = + $scope.data.company + } + if ($scope.data.vat_number && $scope.data.vat_number !== '') { + postData.subscriptionDetails.billing_info.vat_number = + $scope.data.vat_number + } + } + eventTracking.sendMB('subscription-form-submitted', { currencyCode: postData.subscriptionDetails.currencyCode, plan_code: postData.subscriptionDetails.plan_code, @@ -244,7 +266,6 @@ define(['../base', '../directives/creditCards'], App => 'subscription-form-submitted', postData.subscriptionDetails.plan_code ) - return $http .post('/user/subscription/create', postData) .then(function() { @@ -274,9 +295,14 @@ define(['../base', '../directives/creditCards'], App => $scope.processing = true if ($scope.paymentMethod.value === 'paypal') { const opts = { description: $scope.planName } - return recurly.paypal(opts, completeSubscription) + recurly.paypal(opts, completeSubscription) } else { - return recurly.token($scope.data, completeSubscription) + const tokenData = _.cloneDeep($scope.data) + if (!$scope.ui.addCompanyDetails) { + delete tokenData.company + delete tokenData.vat_number + } + recurly.token(tokenData, completeSubscription) } } diff --git a/services/web/test/unit/src/Subscription/RecurlyWrapperTests.js b/services/web/test/unit/src/Subscription/RecurlyWrapperTests.js index 25651dd654..bcd939358a 100644 --- a/services/web/test/unit/src/Subscription/RecurlyWrapperTests.js +++ b/services/web/test/unit/src/Subscription/RecurlyWrapperTests.js @@ -862,7 +862,10 @@ describe('RecurlyWrapper', function() { this.RecurlyWrapper._paypal, 'createBillingInfo' ) - this.setAddress = sinon.stub(this.RecurlyWrapper._paypal, 'setAddress') + this.setAddressAndCompanyBillingInfo = sinon.stub( + this.RecurlyWrapper._paypal, + 'setAddressAndCompanyBillingInfo' + ) this.createSubscription = sinon.stub( this.RecurlyWrapper._paypal, 'createSubscription' @@ -917,7 +920,7 @@ describe('RecurlyWrapper', function() { account: { accountCode: 'xx' }, billingInfo: { token_id: 'abc' } }) - this.setAddress.callsArgWith(1, null, { + this.setAddressAndCompanyBillingInfo.callsArgWith(1, null, { user, subscriptionDetails, recurlyTokenIds, @@ -949,7 +952,7 @@ describe('RecurlyWrapper', function() { this.checkAccountExists.restore() this.createAccount.restore() this.createBillingInfo.restore() - this.setAddress.restore() + this.setAddressAndCompanyBillingInfo.restore() return this.createSubscription.restore() }) @@ -973,7 +976,7 @@ describe('RecurlyWrapper', function() { this.checkAccountExists.callCount.should.equal(1) this.createAccount.callCount.should.equal(1) this.createBillingInfo.callCount.should.equal(1) - this.setAddress.callCount.should.equal(1) + this.setAddressAndCompanyBillingInfo.callCount.should.equal(1) this.createSubscription.callCount.should.equal(1) return done() }) @@ -996,7 +999,7 @@ describe('RecurlyWrapper', function() { this.checkAccountExists.callCount.should.equal(1) this.createAccount.callCount.should.equal(1) this.createBillingInfo.callCount.should.equal(0) - this.setAddress.callCount.should.equal(0) + this.setAddressAndCompanyBillingInfo.callCount.should.equal(0) this.createSubscription.callCount.should.equal(0) return done() }) @@ -1414,12 +1417,15 @@ describe('RecurlyWrapper', function() { }) }) - describe('_paypal.setAddress', function() { + describe('_paypal.setAddressAndCompanyBillingInfo', function() { beforeEach(function() { this.cache.account = { account_code: 'abc' } this.cache.billingInfo = {} return (this.call = callback => { - return this.RecurlyWrapper._paypal.setAddress(this.cache, callback) + return this.RecurlyWrapper._paypal.setAddressAndCompanyBillingInfo( + this.cache, + callback + ) }) })