diff --git a/services/web/app/src/Features/Subscription/SubscriptionController.js b/services/web/app/src/Features/Subscription/SubscriptionController.js index 10b38e537d..c825d92ca8 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionController.js +++ b/services/web/app/src/Features/Subscription/SubscriptionController.js @@ -232,27 +232,13 @@ async function _paymentAngularPage(req, res) { currency = recommendedCurrency } - const refreshedPaymentPageAssignment = - await SplitTestHandler.promises.getAssignment( - req, - res, - 'payment-page-refresh' - ) - const useRefreshedPaymentPage = - refreshedPaymentPageAssignment && - refreshedPaymentPageAssignment.variant === 'refreshed-payment-page' - await SplitTestHandler.promises.getAssignment( req, res, 'student-check-modal' ) - const template = useRefreshedPaymentPage - ? 'subscriptions/new-refreshed' - : 'subscriptions/new-updated' - - res.render(template, { + res.render('subscriptions/new-refreshed', { title: 'subscribe', currency, countryCode, diff --git a/services/web/app/views/subscriptions/new-updated.pug b/services/web/app/views/subscriptions/new-updated.pug deleted file mode 100644 index 710ab8e146..0000000000 --- a/services/web/app/views/subscriptions/new-updated.pug +++ /dev/null @@ -1,361 +0,0 @@ -extends ../layout - -include ./_new_mixins - -block append meta - meta(name="ol-countryCode" content=countryCode) - meta(name="ol-recurlyApiKey" content=settings.apis.recurly.publicKey) - meta(name="ol-recommendedCurrency" content=String(currency).slice(0,3)) - -block head-scripts - script(type="text/javascript", nonce=scriptNonce, src="https://js.recurly.com/v4/recurly.js") - -block content - main.content.content-alt#main-content - .container(ng-controller="NewSubscriptionController" ng-cloak) - .row.card-group - .col-md-3.col-md-push-1 - .card.card-first - .price-feature-description - h4(ng-if="planName") {{planName}} - h4(ng-if="!planName") #{plan.name} - if plan.features - ul.small - if plan.features.collaborators === 1 - li #{translate("collabs_per_proj_single", {collabcount: 1})} - if plan.features.collaborators === -1 - li #{translate("unlimited_collabs")} - if plan.features.collaborators > 1 - li #{translate("collabs_per_proj", {collabcount: plan.features.collaborators})} - if plan.features.compileTimeout > 1 - li #{translate("increased_compile_timeout")} - if plan.features.dropbox && plan.features.github - li #{translate("sync_dropbox_github")} - if plan.features.versioning - li #{translate("full_doc_history")} - if plan.features.trackChanges - li #{translate("track_changes")} - if plan.features.references - li #{translate("reference_search")} - if plan.features.mendeley || plan.features.zotero - li #{translate("reference_sync")} - if plan.features.symbolPalette - li #{translate("symbol_palette")} - - - div.price-summary(ng-if="recurlyPrice") - - var priceVars = { price: "{{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.total }}"}; - hr - h4 #{translate("payment_summary")} - div.small - .price-summary-line - span - | {{planName}} - span(ng-if="coupon") - | {{ availableCurrencies[currencyCode]['symbol'] }}{{ coupon.normalPriceWithoutTax | number:2 }} - span(ng-if="!coupon") - | {{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.subtotal }} - .price-summary-line(ng-if="coupon") - span - | {{ coupon.name }} - span - | –{{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.discount}} - - .price-summary-line(ng-if="taxes && taxes[0] && taxes[0].rate > 0") - span - | #{translate("vat")} {{taxes[0].rate * 100}}% - span - | {{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.tax }} - .price-summary-line.price-summary-total-line - span - b {{ monthlyBilling ? '#{translate("total_per_month")}' : '#{translate("total_per_year")}'}} - span - b {{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.total }} - - div.small.price-details-spacing(ng-if="trialLength || coupon") - div.small(ng-if="trialLength") !{translate("first_few_days_free", {trialLen:'{{trialLength}}'})} - - div.small(ng-if="recurlyPrice") - - var priceVars = { price: "{{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.total }}", discountMonths: "{{ coupon.discountMonths }}" }; - span(ng-if="!coupon.singleUse && coupon.discountMonths > 0 && monthlyBilling") - | !{translate("x_price_for_y_months", priceVars, ['strong'] )} - span(ng-if="coupon.singleUse && monthlyBilling") - | !{translate("x_price_for_first_month", priceVars, ['strong'] )} - span(ng-if="coupon.singleUse && !monthlyBilling") - | !{translate("x_price_for_first_year", priceVars, ['strong'] )} - - div.small(ng-if="coupon && coupon.normalPrice") - - var noDiscountPriceAngularExp = "{{ availableCurrencies[currencyCode]['symbol']}}{{coupon.normalPrice | number:2 }}"; - span(ng-if="!coupon.singleUse && coupon.discountMonths > 0 && monthlyBilling") - | !{translate("then_x_price_per_month", { price: noDiscountPriceAngularExp } )} - span(ng-if="!coupon.singleUse && !coupon.discountMonths && monthlyBilling") - | !{translate("normally_x_price_per_month", { price: noDiscountPriceAngularExp } )} - span(ng-if="!coupon.singleUse && !monthlyBilling") - | !{translate("normally_x_price_per_year", { price: noDiscountPriceAngularExp } )} - span(ng-if="coupon.singleUse && monthlyBilling") - | !{translate("then_x_price_per_month", { price: noDiscountPriceAngularExp } )} - span(ng-if="coupon.singleUse && !monthlyBilling") - | !{translate("then_x_price_per_year", { price: noDiscountPriceAngularExp } )} - hr - - p.price-cancel-anytime.text-center(ng-non-bindable) !{translate("cancel_anytime", { appName:'{{settings.appName}}' })} - - .col-md-5.col-md-push-1 - .card.card-highlighted.card-border(ng-hide="threeDSecureFlow") - .alert.alert-danger(ng-show="recurlyLoadError") - strong #{translate('payment_provider_unreachable_error')} - .price-switch-header(ng-hide="recurlyLoadError") - .row - .col-xs-9 - h2 {{planName}} - .col-xs-3 - div.dropdown.changePlanButton.pull-right(ng-cloak, dropdown) - a.btn.btn-default.dropdown-toggle( - href="#", - data-toggle="dropdown", - dropdown-toggle - ) - | {{currencyCode}} ({{allCurrencies[currencyCode]['symbol']}}) - span.caret - ul.dropdown-menu(role="menu") - li(ng-repeat="(currency, value) in availableCurrencies") - a( - ng-click="changeCurrency(currency)", - ) {{currency}} ({{value['symbol']}}) - .row(ng-if="planCode == 'student-annual' || planCode == 'student-monthly' || planCode == 'student_free_trial_7_days'") - .col-xs-12 - p.student-disclaimer #{translate('student_disclaimer')} - - .row(ng-hide="recurlyLoadError") - div() - .col-md-12() - form( - name="simpleCCForm" - novalidate - ) - - div.payment-method-toggle - a.payment-method-toggle-switch( - href - ng-click="setPaymentMethod('credit_card');" - ng-class="paymentMethod.value === 'credit_card' ? 'payment-method-toggle-switch-selected' : ''" - ) - i.fa.fa-cc-mastercard.fa-2x(aria-hidden="true") - span   - i.fa.fa-cc-visa.fa-2x(aria-hidden="true") - span   - i.fa.fa-cc-amex.fa-2x(aria-hidden="true") - span.sr-only Pay with Mastercard, Visa, or Amex - a.payment-method-toggle-switch( - href - ng-click="setPaymentMethod('paypal');" - ng-class="paymentMethod.value === 'paypal' ? 'payment-method-toggle-switch-selected' : ''" - ) - i.fa.fa-cc-paypal.fa-2x(aria-hidden="true") - span.sr-only Pay with PayPal - - .alert.alert-warning.small(ng-show="genericError") - strong {{genericError}} - - .alert.alert-warning.small(ng-show="couponError") - strong {{couponError}} - - div(ng-show="paymentMethod.value === 'credit_card'") - .row - .col-xs-6 - .form-group(ng-class="validation.errorFields.first_name || inputHasError(simpleCCForm.firstName) ? 'has-error' : ''") - label(for="first-name") #{translate('first_name')} - input#first-name.form-control( - type="text" - maxlength='255' - data-recurly="first_name" - name="firstName" - ng-model="data.first_name" - required - ) - span.input-feedback-message(ng-if="simpleCCForm.firstName.$error.required") #{translate('this_field_is_required')} - .col-xs-6 - .form-group(ng-class="validation.errorFields.last_name || inputHasError(simpleCCForm.lastName)? 'has-error' : ''") - label(for="last-name") #{translate('last_name')} - input#last-name.form-control( - type="text" - maxlength='255' - data-recurly="last_name" - name="lastName" - ng-model="data.last_name" - required - ) - span.input-feedback-message(ng-if="simpleCCForm.lastName.$error.required") #{translate('this_field_is_required')} - - .form-group(ng-class="validation.errorFields.number ? 'has-error' : ''") - label(for="card-no") #{translate("credit_card_number")} - div#card-no( - type="text" - name="ccNumber" - data-recurly='number' - ) - - .row - .col-xs-3 - .form-group.has-feedback(ng-class="validation.errorFields.month ? 'has-error' : ''") - label(for="month").capitalised #{translate("month")} - div( - type="number" - name="month" - data-recurly="month" - ) - .col-xs-3 - .form-group.has-feedback(ng-class="validation.errorFields.year ? 'has-error' : ''") - label(for="year").capitalised #{translate("year")} - div( - type="number" - name="year" - data-recurly="year" - ) - - .col-xs-6 - .form-group.has-feedback(ng-class="validation.errorFields.cvv ? 'has-error' : ''") - label #{translate("security_code")} - div( - type="number" - ng-model="data.cvv" - data-recurly="cvv" - name="cvv" - cc-format-sec-code - ) - .form-control-feedback - a.form-helper( - href - tabindex="-1" - tooltip-template="'cvv-tooltip-tpl.html'" - tooltip-trigger="mouseenter" - tooltip-append-to-body="true" - ) ? - - div - .row - .col-xs-12 - .form-group(ng-class="validation.errorFields.address1 || inputHasError(simpleCCForm.address1) ? 'has-error' : ''") - label(for="address-line-1") #{translate('address_line_1')} - input#address-line-1.form-control( - type="text" - maxlength="255" - data-recurly="address1" - name="address1" - ng-model="data.address1" - required - ) - span.input-feedback-message(ng-if="simpleCCForm.address1.$error.required") #{translate('this_field_is_required')} - - .row - .col-xs-12 - .form-group.has-feedback(ng-class="validation.errorFields.address2 ? 'has-error' : ''") - label(for="address-line-2") #{translate('address_line_2')} - input#address-line-2.form-control( - type="text" - maxlength="255" - data-recurly="address2" - name="address2" - ng-model="data.address2" - ) - - .row - .col-xs-4 - .form-group(ng-class="validation.errorFields.postal_code || inputHasError(simpleCCForm.postalCode) ? 'has-error' : ''") - label(for="postal-code") #{translate('postal_code')} - input#postal-code.form-control( - type="text" - maxlength="255" - data-recurly="postal_code" - name="postalCode" - ng-model="data.postal_code" - required - ) - span.input-feedback-message(ng-if="simpleCCForm.postalCode.$error.required") #{translate('this_field_is_required')} - - .col-xs-8 - .form-group(ng-class="validation.errorFields.country || inputHasError(simpleCCForm.country) ? 'has-error' : ''") - label(for="country") #{translate('country')} - select#country.form-control( - data-recurly="country" - ng-model="data.country" - name="country" - ng-change="updateCountry()" - 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(ng-if="simpleCCForm.country.$error.required") #{translate('this_field_is_required')} - - .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 && 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')} - input#coupon-code.form-control( - type="text" - ng-blur="applyCoupon()" - ng-model="data.coupon" - ) - - p(ng-if="paymentMethod.value === 'paypal'") #{translate("paypal_upgrade")} - - div.payment-submit - button.btn.btn-primary.btn-block( - ng-click="submit()" - ng-disabled="processing || !isFormValid(simpleCCForm);" - ) - span(ng-show="processing") - i.fa.fa-spinner.fa-spin(aria-hidden="true") - span.sr-only #{translate('processing')} - |   - span(ng-if="paymentMethod.value === 'credit_card'") - | {{ trialLength ? '#{translate("upgrade_cc_btn")}' : '#{translate("upgrade_now")}'}} - span(ng-if="paymentMethod.value !== 'credit_card'") #{translate("upgrade_paypal_btn")} - - p.tos-agreement-notice !{translate("by_subscribing_you_agree_to_our_terms_of_service", {}, [{name: 'a', attrs: {href: '/legal#Terms', target:'_blank', rel:'noopener noreferrer'}}])} - - div.three-d-secure-container.card.card-highlighted.card-border(ng-show="threeDSecureFlow") - .alert.alert-info.small(aria-live="assertive") - strong #{translate('card_must_be_authenticated_by_3dsecure')} - div.three-d-secure-recurly-container - - script(type="text/javascript", nonce=scriptNonce). - ga('send', 'event', 'pageview', 'payment_form', "#{plan_code}") - - script( - type="text/ng-template" - id="cvv-tooltip-tpl.html" - ) - p !{translate("for_visa_mastercard_and_discover", {}, ['strong', 'strong', 'strong'])} - p !{translate("for_american_express", {}, ['strong', 'strong', 'strong'])} - - +studentCheckModal diff --git a/services/web/frontend/js/main/new-subscription.js b/services/web/frontend/js/main/new-subscription.js index 409444c21e..9da954e0c2 100644 --- a/services/web/frontend/js/main/new-subscription.js +++ b/services/web/frontend/js/main/new-subscription.js @@ -37,7 +37,7 @@ export default App.controller( $scope.recurlyLoadError = false $scope.currencyCode = MultiCurrencyPricing.currencyCode - $scope.initiallySelectedCurrencyCode = MultiCurrencyPricing.currencyCode // track for payment-page-refreshed + $scope.initiallySelectedCurrencyCode = MultiCurrencyPricing.currencyCode $scope.allCurrencies = MultiCurrencyPricing.plans $scope.availableCurrencies = {} $scope.planCode = window.plan_code @@ -188,7 +188,6 @@ export default App.controller( } } - // abridged list for payment-page-refreshed split test $scope.limitedCurrencies = {} const limitedCurrencyCodes = ['USD', 'EUR', 'GBP'] if ( @@ -263,30 +262,26 @@ export default App.controller( $scope.ui.showCurrencyDropdown = true } - // This check is just so we don't load this on the default checkout variant - const newCardInputElement = document.querySelector('#recurly-card-input') const elements = recurly.Elements() - if (newCardInputElement) { - const card = elements.CardElement({ - displayIcon: true, - style: { - inputType: 'mobileSelect', - fontColor: '#5d6879', - placeholder: {}, - invalid: { - fontColor: '#a93529', - }, + const card = elements.CardElement({ + displayIcon: true, + style: { + inputType: 'mobileSelect', + fontColor: '#5d6879', + placeholder: {}, + invalid: { + fontColor: '#a93529', }, + }, + }) + card.attach('#recurly-card-input') + card.on('change', state => { + $scope.$applyAsync(() => { + $scope.showCardElementInvalid = + !state.focus && !state.empty && !state.valid + $scope.cardIsValid = state.valid }) - card.attach('#recurly-card-input') - card.on('change', state => { - $scope.$applyAsync(() => { - $scope.showCardElementInvalid = - !state.focus && !state.empty && !state.valid - $scope.cardIsValid = state.valid - }) - }) - } + }) $scope.applyVatNumber = () => pricing @@ -322,11 +317,7 @@ export default App.controller( if ($scope.paymentMethod.value === 'paypal') { return $scope.data.country !== '' } else { - if (newCardInputElement) { - return form.$valid && $scope.cardIsValid - } else { - return form.$valid - } + return form.$valid && $scope.cardIsValid } } @@ -452,11 +443,7 @@ export default App.controller( delete tokenData.company delete tokenData.vat_number } - if (newCardInputElement) { - recurly.token(elements, tokenData, completeSubscription) - } else { - recurly.token(tokenData, completeSubscription) - } + recurly.token(elements, tokenData, completeSubscription) } } diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 0fcdb27972..475bef39f3 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -63,7 +63,6 @@ "additional_licenses": "Your subscription includes <0>__additionalLicenses__ additional license(s) for a total of <1>__totalLicenses__ licenses.", "address": "Address", "address_line_1": "Address", - "address_line_2": "Address (line 2, optional)", "address_second_line_optional": "Address second line (optional)", "admin": "admin", "admin_user_created_message": "Created admin user, Log in here to continue", @@ -293,7 +292,6 @@ "created_at": "Created at", "creating": "Creating", "credit_card": "Credit Card", - "credit_card_number": "Credit Card Number", "cs": "Czech", "currency": "Currency", "current_experiments": "Current Experiments", @@ -506,7 +504,6 @@ "find_out_more_about_the_file_outline": "Find out more about the file outline", "find_out_more_nt": "Find out more.", "find_the_symbols_you_need_with_premium": "Find the symbols you need faster with Overleaf Premium", - "first_few_days_free": "First __trialLen__ days free", "first_name": "First Name", "first_x_days_free_after_that_y_per_month": "First <0>__trialLen__ days free, after that <0>__price__ per month", "fold_line": "Fold line", @@ -1061,7 +1058,6 @@ "password_was_detected_on_a_public_list_of_known_compromised_passwords": "This password was detected on a <0>public list of known compromised passwords", "payment_provider_unreachable_error": "Sorry, there was an error talking to our payment provider. Please try again in a few moments.\nIf you are using any ad or script blocking extensions in your browser, you may need to temporarily disable them.", "payment_summary": "Payment summary", - "paypal_upgrade": "To upgrade, click on the button below and log on to PayPal using your email and password.", "pdf_compile_in_progress_error": "A previous compile is still running. Please wait a minute and try compiling again.", "pdf_compile_rate_limit_hit": "Compile rate limit hit", "pdf_compile_try_again": "Please wait for your other compile to finish before trying again.", @@ -1301,7 +1297,6 @@ "search_whole_word": "Whole word", "secondary_email_password_reset": "That email is registered as a secondary email. Please enter the primary email for your account.", "security": "Security", - "security_code": "Security code", "see_changes_in_your_documents_live": "See changes in your documents, live", "see_what_has_been": "See what has been ", "select_a_file": "Select a File", @@ -1616,7 +1611,6 @@ "upgrade_cc_btn": "Upgrade now, pay after 7 days", "upgrade_for_longer_compiles": "Upgrade to increase your timeout limit.", "upgrade_now": "Upgrade Now", - "upgrade_paypal_btn": "Continue", "upgrade_to_get_feature": "Upgrade to get __feature__, plus:", "upgrade_to_track_changes": "Upgrade to Track Changes", "upload": "Upload", diff --git a/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js b/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js index 9c58890f45..89f0aa7e15 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js +++ b/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js @@ -275,7 +275,7 @@ describe('SubscriptionController', function () { describe('with a valid plan code', function () { it('should render the new subscription page', function (done) { this.res.render = (page, opts) => { - page.should.equal('subscriptions/new-updated') + page.should.equal('subscriptions/new-refreshed') done() } this.SubscriptionController.paymentPage(this.req, this.res)