Merge pull request #2697 from overleaf/pr-add-vat-number

Add VAT number and company name to the payment form

GitOrigin-RevId: 9ba2d7345f6f3cef1f7372e17c04243ab285e0bd
This commit is contained in:
Paulo Jorge Reis 2020-04-29 13:43:45 +01:00 committed by Copybot
parent ff38ea9533
commit 7f13e258bb
5 changed files with 125 additions and 35 deletions

View file

@ -86,7 +86,7 @@ module.exports = RecurlyWrapper = {
let address let address
try { try {
address = getAddressFromSubscriptionDetails(subscriptionDetails) address = getAddressFromSubscriptionDetails(subscriptionDetails, false)
} catch (error) { } catch (error) {
return next(error) return next(error)
} }
@ -173,25 +173,36 @@ module.exports = RecurlyWrapper = {
) )
}, },
setAddress(cache, next) { setAddressAndCompanyBillingInfo(cache, next) {
const { user } = cache const { user } = cache
const { subscriptionDetails } = 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__( const accountCode = __guard__(
cache != null ? cache.account : undefined, cache != null ? cache.account : undefined,
x1 => x1.account_code x1 => x1.account_code
) )
if (!accountCode) { 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 { try {
address = getAddressFromSubscriptionDetails(subscriptionDetails) addressAndCompanyBillingInfo = getAddressFromSubscriptionDetails(
subscriptionDetails,
true
)
} catch (error) { } catch (error) {
return next(error) return next(error)
} }
const requestBody = RecurlyWrapper._buildXml('billing_info', address) const requestBody = RecurlyWrapper._buildXml(
'billing_info',
addressAndCompanyBillingInfo
)
return RecurlyWrapper.apiRequest( return RecurlyWrapper.apiRequest(
{ {
@ -296,7 +307,7 @@ module.exports = RecurlyWrapper = {
Async.apply(RecurlyWrapper._paypal.checkAccountExists, cache), Async.apply(RecurlyWrapper._paypal.checkAccountExists, cache),
RecurlyWrapper._paypal.createAccount, RecurlyWrapper._paypal.createAccount,
RecurlyWrapper._paypal.createBillingInfo, RecurlyWrapper._paypal.createBillingInfo,
RecurlyWrapper._paypal.setAddress, RecurlyWrapper._paypal.setAddressAndCompanyBillingInfo,
RecurlyWrapper._paypal.createSubscription RecurlyWrapper._paypal.createSubscription
], ],
function(err, result) { function(err, result) {
@ -1048,8 +1059,12 @@ function getCustomFieldsFromSubscriptionDetails(subscriptionDetails) {
return { custom_field: customFields } return { custom_field: customFields }
} }
function getAddressFromSubscriptionDetails(subscriptionDetails) { function getAddressFromSubscriptionDetails(
subscriptionDetails,
includeCompanyInfo
) {
const { address } = subscriptionDetails const { address } = subscriptionDetails
if (!address || !address.country) { if (!address || !address.country) {
throw new Errors.InvalidError({ throw new Errors.InvalidError({
message: 'Invalid country', message: 'Invalid country',
@ -1070,6 +1085,21 @@ function getAddressFromSubscriptionDetails(subscriptionDetails) {
country: address.country 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 return addressObject
} }

View file

@ -8,6 +8,7 @@ div(ng-controller="RecurlySubscriptionController")
case personalSubscription.recurly.state case personalSubscription.recurly.state
when "active" when "active"
p !{translate("currently_subscribed_to_plan", {planName:"<strong>" + personalSubscription.plan.name + "</strong>"})} p !{translate("currently_subscribed_to_plan", {planName:"<strong>" + personalSubscription.plan.name + "</strong>"})}
|
a(href, ng-click="switchToChangePlanView()", ng-if="showChangePlanButton") !{translate("change_plan")}. a(href, ng-click="switchToChangePlanView()", ng-if="showChangePlanButton") !{translate("change_plan")}.
-if (personalSubscription.recurly.trialEndsAtFormatted && personalSubscription.recurly.trial_ends_at > Date.now()) -if (personalSubscription.recurly.trialEndsAtFormatted && personalSubscription.recurly.trial_ends_at > Date.now())
p You're on a free trial which ends on <strong ng-non-bindable>#{personalSubscription.recurly.trialEndsAtFormatted}</strong> p You're on a free trial which ends on <strong ng-non-bindable>#{personalSubscription.recurly.trialEndsAtFormatted}</strong>

View file

@ -62,7 +62,6 @@ block content
novalidate novalidate
) )
div.payment-method-toggle div.payment-method-toggle
a.payment-method-toggle-switch( a.payment-method-toggle-switch(
href href
@ -165,22 +164,42 @@ block content
ng-model="data.country" ng-model="data.country"
name="country" name="country"
ng-change="updateCountry()" ng-change="updateCountry()"
required,
ng-selected="{{country.code == data.country}}" ng-selected="{{country.code == data.country}}"
ng-model-options="{ debounce: 200 }"
required
) )
option(value='', disabled) #{translate("country")} option(value='', disabled) #{translate("country")}
option(value='-', disabled) -------------- option(value='-', disabled) --------------
option(ng-repeat="country in countries" ng-bind-html="country.name" value="{{country.code}}") 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' : '' }} span.input-feedback-message {{ simpleCCForm.country.$error.required ? 'This field is required' : '' }}
if (showVatField) .form-group
.form-group .checkbox
label(for="vat-no") #{translate('vat_number')} label
input#vat-no.form-control( input(
type="text" type="checkbox"
ng-blur="applyVatNumber()" ng-model="ui.addCompanyDetails"
ng-model="data.vat_number" )
) |
| #{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) if (showCouponField)
.form-group .form-group
label(for="coupon-code") #{translate('coupon_code')} label(for="coupon-code") #{translate('coupon_code')}
@ -192,11 +211,19 @@ block content
p(ng-if="paymentMethod.value === 'paypal'") #{translate("paypal_upgrade")} 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 hr.thin
span Total: span
strong {{availableCurrencies[currencyCode]['symbol']}}{{price.next.total}} | Total:
span ({{availableCurrencies[currencyCode]['symbol']}}{{price.next.subtotal}} + {{availableCurrencies[currencyCode]['symbol']}}{{price.next.tax}} tax) |
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("month")}
span(ng-if="!monthlyBilling") #{translate("every")} #{translate("year")} span(ng-if="!monthlyBilling") #{translate("every")} #{translate("year")}
hr.thin hr.thin

View file

@ -17,6 +17,10 @@ define(['../base', '../directives/creditCards'], App =>
return return
} }
$scope.ui = {
addCompanyDetails: false
}
$scope.recurlyLoadError = false $scope.recurlyLoadError = false
$scope.currencyCode = MultiCurrencyPricing.currencyCode $scope.currencyCode = MultiCurrencyPricing.currencyCode
$scope.allCurrencies = MultiCurrencyPricing.plans $scope.allCurrencies = MultiCurrencyPricing.plans
@ -58,6 +62,8 @@ define(['../base', '../directives/creditCards'], App =>
address2: '', address2: '',
state: '', state: '',
city: '', city: '',
company: '',
vat_number: '',
country: window.countryCode, country: window.countryCode,
coupon: window.couponCode coupon: window.couponCode
} }
@ -99,7 +105,9 @@ define(['../base', '../directives/creditCards'], App =>
pricing pricing
.plan(window.plan_code, { quantity: 1 }) .plan(window.plan_code, { quantity: 1 })
.address({ country: $scope.data.country }) .address({
country: $scope.data.country
})
.tax({ tax_code: 'digital', vat_number: '' }) .tax({ tax_code: 'digital', vat_number: '' })
.currency($scope.currencyCode) .currency($scope.currencyCode)
.coupon($scope.data.coupon) .coupon($scope.data.coupon)
@ -219,7 +227,6 @@ define(['../base', '../directives/creditCards'], App =>
coupon_code: pricing.items.coupon ? pricing.items.coupon.code : '', coupon_code: pricing.items.coupon ? pricing.items.coupon.code : '',
first_name: $scope.data.first_name, first_name: $scope.data.first_name,
last_name: $scope.data.last_name, last_name: $scope.data.last_name,
isPaypal: $scope.paymentMethod.value === 'paypal', isPaypal: $scope.paymentMethod.value === 'paypal',
address: { address: {
address1: $scope.data.address1, 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', { eventTracking.sendMB('subscription-form-submitted', {
currencyCode: postData.subscriptionDetails.currencyCode, currencyCode: postData.subscriptionDetails.currencyCode,
plan_code: postData.subscriptionDetails.plan_code, plan_code: postData.subscriptionDetails.plan_code,
@ -244,7 +266,6 @@ define(['../base', '../directives/creditCards'], App =>
'subscription-form-submitted', 'subscription-form-submitted',
postData.subscriptionDetails.plan_code postData.subscriptionDetails.plan_code
) )
return $http return $http
.post('/user/subscription/create', postData) .post('/user/subscription/create', postData)
.then(function() { .then(function() {
@ -274,9 +295,14 @@ define(['../base', '../directives/creditCards'], App =>
$scope.processing = true $scope.processing = true
if ($scope.paymentMethod.value === 'paypal') { if ($scope.paymentMethod.value === 'paypal') {
const opts = { description: $scope.planName } const opts = { description: $scope.planName }
return recurly.paypal(opts, completeSubscription) recurly.paypal(opts, completeSubscription)
} else { } 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)
} }
} }

View file

@ -862,7 +862,10 @@ describe('RecurlyWrapper', function() {
this.RecurlyWrapper._paypal, this.RecurlyWrapper._paypal,
'createBillingInfo' 'createBillingInfo'
) )
this.setAddress = sinon.stub(this.RecurlyWrapper._paypal, 'setAddress') this.setAddressAndCompanyBillingInfo = sinon.stub(
this.RecurlyWrapper._paypal,
'setAddressAndCompanyBillingInfo'
)
this.createSubscription = sinon.stub( this.createSubscription = sinon.stub(
this.RecurlyWrapper._paypal, this.RecurlyWrapper._paypal,
'createSubscription' 'createSubscription'
@ -917,7 +920,7 @@ describe('RecurlyWrapper', function() {
account: { accountCode: 'xx' }, account: { accountCode: 'xx' },
billingInfo: { token_id: 'abc' } billingInfo: { token_id: 'abc' }
}) })
this.setAddress.callsArgWith(1, null, { this.setAddressAndCompanyBillingInfo.callsArgWith(1, null, {
user, user,
subscriptionDetails, subscriptionDetails,
recurlyTokenIds, recurlyTokenIds,
@ -949,7 +952,7 @@ describe('RecurlyWrapper', function() {
this.checkAccountExists.restore() this.checkAccountExists.restore()
this.createAccount.restore() this.createAccount.restore()
this.createBillingInfo.restore() this.createBillingInfo.restore()
this.setAddress.restore() this.setAddressAndCompanyBillingInfo.restore()
return this.createSubscription.restore() return this.createSubscription.restore()
}) })
@ -973,7 +976,7 @@ describe('RecurlyWrapper', function() {
this.checkAccountExists.callCount.should.equal(1) this.checkAccountExists.callCount.should.equal(1)
this.createAccount.callCount.should.equal(1) this.createAccount.callCount.should.equal(1)
this.createBillingInfo.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) this.createSubscription.callCount.should.equal(1)
return done() return done()
}) })
@ -996,7 +999,7 @@ describe('RecurlyWrapper', function() {
this.checkAccountExists.callCount.should.equal(1) this.checkAccountExists.callCount.should.equal(1)
this.createAccount.callCount.should.equal(1) this.createAccount.callCount.should.equal(1)
this.createBillingInfo.callCount.should.equal(0) this.createBillingInfo.callCount.should.equal(0)
this.setAddress.callCount.should.equal(0) this.setAddressAndCompanyBillingInfo.callCount.should.equal(0)
this.createSubscription.callCount.should.equal(0) this.createSubscription.callCount.should.equal(0)
return done() return done()
}) })
@ -1414,12 +1417,15 @@ describe('RecurlyWrapper', function() {
}) })
}) })
describe('_paypal.setAddress', function() { describe('_paypal.setAddressAndCompanyBillingInfo', function() {
beforeEach(function() { beforeEach(function() {
this.cache.account = { account_code: 'abc' } this.cache.account = { account_code: 'abc' }
this.cache.billingInfo = {} this.cache.billingInfo = {}
return (this.call = callback => { return (this.call = callback => {
return this.RecurlyWrapper._paypal.setAddress(this.cache, callback) return this.RecurlyWrapper._paypal.setAddressAndCompanyBillingInfo(
this.cache,
callback
)
}) })
}) })