Merge pull request #2015 from overleaf/ta-recurly-upgrade

Upgrade Recurly

GitOrigin-RevId: 9a7c4804d2fafa31ea634498359dbfd95416b2ae
This commit is contained in:
Timothée Alby 2019-07-31 10:23:13 +02:00 committed by sharelatex
parent 018b2cc474
commit b9ab0edf69
12 changed files with 102 additions and 70 deletions

View file

@ -415,7 +415,8 @@ module.exports = RecurlyWrapper = {
'base64' 'base64'
)}`, )}`,
Accept: 'application/xml', Accept: 'application/xml',
'Content-Type': 'application/xml; charset=utf-8' 'Content-Type': 'application/xml; charset=utf-8',
'X-Api-Version': Settings.apis.recurly.apiVersion
} }
const { expect404 } = options const { expect404 } = options
delete options.expect404 delete options.expect404

View file

@ -117,29 +117,33 @@ html(
include scribtex-modal include scribtex-modal
block requirejs block requirejs
script(type='text/javascript'). block requirejs-config
// minimal requirejs configuration (can be extended/overridden) script(type='text/javascript').
window.requirejs = { // minimal requirejs configuration (can be extended/overridden)
"paths" : { window.requirejs = {
"moment": "libs/#{lib('moment')}", "paths" : {
"fineuploader": "libs/#{lib('fineuploader')}", "moment": "libs/#{lib('moment')}",
"main": "#{buildJsPath('main.js', {hashedPath:settings.useMinifiedJs, removeExtension:true})}", "fineuploader": "libs/#{lib('fineuploader')}",
"libraries": "#{buildJsPath('libraries.js', {hashedPath:settings.useMinifiedJs, removeExtension:true})}", "main": "#{buildJsPath('main.js', {hashedPath:settings.useMinifiedJs, removeExtension:true})}",
}, "libraries": "#{buildJsPath('libraries.js', {hashedPath:settings.useMinifiedJs, removeExtension:true})}",
"config":{ "recurly": "https://js.recurly.com/v4/recurly.js"
"moment":{ },
"noGlobal": true "config":{
"moment":{
"noGlobal": true
}
} }
} };
};
script( block requirejs-init
data-main=buildJsPath('main.js', {hashedPath:false}), script(
baseurl=fullJsPath, data-main=buildJsPath('main.js', {hashedPath:false}),
src=buildJsPath('libs/require.js', {hashedPath:true}) baseurl=fullJsPath,
) src=buildJsPath('libs/require.js', {hashedPath:true})
)
!= moduleIncludes("contactModal", locals) != moduleIncludes("contactModal", locals)
include v1-tooltip include v1-tooltip
include sentry include sentry

View file

@ -2,6 +2,13 @@ extends ../layout
include ./dashboard/_team_name_mixin include ./dashboard/_team_name_mixin
block requirejs-init
script(
data-main=buildJsPath("main-recurly.js", {hashedPath:false}),
baseurl=fullJsPath,
src=buildJsPath('libs/require.js', {hashedPath:true})
)
block content block content
.content.content-alt(ng-cloak) .content.content-alt(ng-cloak)
.container .container
@ -12,7 +19,7 @@ block content
p You already have a subscription p You already have a subscription
.card .card
.page-header .page-header
h1 #{translate("your_subscription")} h1 #{translate("your_subscription")}
-var hasDisplayedSubscription = false -var hasDisplayedSubscription = false
-if (personalSubscription) -if (personalSubscription)

View file

@ -1,4 +1,3 @@
script(src="https://js.recurly.com/v3/recurly.js")
script(type='text/javascript'). script(type='text/javascript').
window.recurlyApiKey = "!{settings.apis.recurly.publicKey}" window.recurlyApiKey = "!{settings.apis.recurly.publicKey}"
window.subscription = !{StringHelper.stringifyJsonForScript(personalSubscription)} window.subscription = !{StringHelper.stringifyJsonForScript(personalSubscription)}
@ -8,7 +7,7 @@ div(ng-controller="RecurlySubscriptionController")
div(ng-show="!showCancellation") div(ng-show="!showCancellation")
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>
@ -18,7 +17,7 @@ div(ng-controller="RecurlySubscriptionController")
p p
a(href=personalSubscription.recurly.billingDetailsLink, target="_blank").btn.btn-info #{translate("update_your_billing_details")} a(href=personalSubscription.recurly.billingDetailsLink, target="_blank").btn.btn-info #{translate("update_your_billing_details")}
| &nbsp; | &nbsp;
a(href, ng-click="switchToCancellationView()").btn.btn-primary !{translate("cancel_your_subscription")} a(href, ng-click="switchToCancellationView()", ng-hide="recurlyLoadError").btn.btn-primary !{translate("cancel_your_subscription")}
when "canceled" when "canceled"
p !{translate("currently_subscribed_to_plan", {planName:"<strong>" + personalSubscription.plan.name + "</strong>"})} p !{translate("currently_subscribed_to_plan", {planName:"<strong>" + personalSubscription.plan.name + "</strong>"})}
p !{translate("subscription_canceled_and_terminate_on_x", {terminateDate:"<strong>" + personalSubscription.recurly.nextPaymentDueAt + "</strong>"})} p !{translate("subscription_canceled_and_terminate_on_x", {terminateDate:"<strong>" + personalSubscription.recurly.nextPaymentDueAt + "</strong>"})}
@ -26,11 +25,14 @@ div(ng-controller="RecurlySubscriptionController")
input(type="hidden", name="_csrf", value=csrfToken) input(type="hidden", name="_csrf", value=csrfToken)
input(type="submit",value="Reactivate your subscription").btn.btn-success input(type="submit",value="Reactivate your subscription").btn.btn-success
when "expired" when "expired"
p !{translate("your_subscription_has_expired")} p !{translate("your_subscription_has_expired")}
a(href="/user/subscription/plans") !{translate("create_new_subscription")} a(href="/user/subscription/plans") !{translate("create_new_subscription")}
default default
p !{translate("problem_with_subscription_contact_us")} p !{translate("problem_with_subscription_contact_us")}
.alert.alert-warning(ng-show="recurlyLoadError")
strong #{translate('payment_provider_unreachable_error')}
include ./_change_plans_mixins include ./_change_plans_mixins
div(ng-show="showChangePlan", ng-cloak) div(ng-show="showChangePlan", ng-cloak)
h2 !{translate("change_plan")} h2 !{translate("change_plan")}

View file

@ -1,5 +1,12 @@
extends ../layout extends ../layout
block requirejs-init
script(
data-main=buildJsPath("main-recurly.js", {hashedPath:false}),
baseurl=fullJsPath,
src=buildJsPath('libs/require.js', {hashedPath:true})
)
block scripts block scripts
script(type='text/javascript'). script(type='text/javascript').
window.countryCode = '#{countryCode}' window.countryCode = '#{countryCode}'
@ -15,7 +22,9 @@ block content
.row.card-group .row.card-group
.col-md-5.col-md-push-4 .col-md-5.col-md-push-4
.card.card-highlighted.card-border .card.card-highlighted.card-border
.page-header .alert.alert-danger(ng-show="recurlyLoadError")
strong #{translate('payment_provider_unreachable_error')}
.page-header(ng-hide="recurlyLoadError")
.row .row
.col-xs-9 .col-xs-9
h2 {{planName}} h2 {{planName}}
@ -26,13 +35,13 @@ block content
data-toggle="dropdown", data-toggle="dropdown",
dropdown-toggle dropdown-toggle
) )
| {{currencyCode}} ({{allCurrencies[currencyCode]['symbol']}}) | {{currencyCode}} ({{allCurrencies[currencyCode]['symbol']}})
span.caret span.caret
ul.dropdown-menu(role="menu") ul.dropdown-menu(role="menu")
li(ng-repeat="(currency, value) in availableCurrencies") li(ng-repeat="(currency, value) in availableCurrencies")
a( a(
ng-click="changeCurrency(currency)", ng-click="changeCurrency(currency)",
) {{currency}} ({{value['symbol']}}) ) {{currency}} ({{value['symbol']}})
.row(ng-if="planCode == 'student-annual' || planCode == 'student-monthly' || planCode == 'student_free_trial_7_days'") .row(ng-if="planCode == 'student-annual' || planCode == 'student-monthly' || planCode == 'student_free_trial_7_days'")
.col-xs-12 .col-xs-12
p.student-disclaimer #{translate('student_disclaimer')} p.student-disclaimer #{translate('student_disclaimer')}
@ -40,16 +49,16 @@ block content
hr.thin hr.thin
.row .row
.col-md-12.text-center .col-md-12.text-center
div(ng-if="trialLength") div(ng-if="trialLength")
span !{translate("first_few_days_free", {trialLen:'{{trialLength}}'})} span !{translate("first_few_days_free", {trialLen:'{{trialLength}}'})}
span(ng-if="discountMonths && discountRate") &nbsp; - {{discountMonths}} #{translate("month")}s {{discountRate}}% Off span(ng-if="discountMonths && discountRate") &nbsp; - {{discountMonths}} #{translate("month")}s {{discountRate}}% Off
div(ng-if="price") div(ng-if="price")
strong {{availableCurrencies[currencyCode]['symbol']}}{{price.next.total}} strong {{availableCurrencies[currencyCode]['symbol']}}{{price.next.total}}
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")}
div(ng-if="normalPrice") div(ng-if="normalPrice")
span.small Normally {{availableCurrencies[currencyCode]['symbol']}}{{normalPrice}} span.small Normally {{availableCurrencies[currencyCode]['symbol']}}{{normalPrice}}
.row .row(ng-hide="recurlyLoadError")
div() div()
.col-md-12() .col-md-12()
form( form(
@ -57,7 +66,7 @@ block content
novalidate novalidate
) )
div.payment-method-toggle div.payment-method-toggle
a.payment-method-toggle-switch( a.payment-method-toggle-switch(
href href
@ -77,7 +86,7 @@ block content
) )
i.fa.fa-cc-paypal.fa-2x(aria-hidden="true") i.fa.fa-cc-paypal.fa-2x(aria-hidden="true")
span.sr-only Pay with PayPal span.sr-only Pay with PayPal
.alert.alert-warning.small(ng-show="genericError") .alert.alert-warning.small(ng-show="genericError")
strong {{genericError}} strong {{genericError}}
@ -132,7 +141,7 @@ block content
name="year" name="year"
data-recurly="year" data-recurly="year"
) )
.col-xs-6 .col-xs-6
.form-group.has-feedback(ng-class="validation.errorFields.cvv ? 'has-external-error' : ''") .form-group.has-feedback(ng-class="validation.errorFields.cvv ? 'has-external-error' : ''")
label #{translate("security_code")} label #{translate("security_code")}
@ -184,14 +193,14 @@ block content
ng-blur="applyCoupon()" ng-blur="applyCoupon()"
ng-model="data.coupon" ng-model="data.coupon"
) )
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-if="price.next.tax !== '0.00'")
hr.thin hr.thin
span Total: span Total:
strong {{availableCurrencies[currencyCode]['symbol']}}{{price.next.total}} strong {{availableCurrencies[currencyCode]['symbol']}}{{price.next.total}}
span ({{availableCurrencies[currencyCode]['symbol']}}{{price.next.subtotal}} + {{availableCurrencies[currencyCode]['symbol']}}{{price.next.tax}} tax) 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
@ -199,9 +208,9 @@ block content
div.payment-submit div.payment-submit
button.btn.btn-success.btn-block( button.btn.btn-success.btn-block(
ng-click="submit()" ng-click="submit()"
ng-disabled="processing || !isFormValid(simpleCCForm);" ng-disabled="processing || !isFormValid(simpleCCForm);"
) )
span(ng-show="processing") span(ng-show="processing")
i.fa.fa-spinner.fa-spin(aria-hidden="true") i.fa.fa-spinner.fa-spin(aria-hidden="true")
span.sr-only #{translate('processing')} span.sr-only #{translate('processing')}
| &nbsp; | &nbsp;
@ -215,13 +224,13 @@ block content
a.btn-primary.btn.plansPageStudentLink( a.btn-primary.btn.plansPageStudentLink(
href, href,
ng-click="switchToStudent()" ng-click="switchToStudent()"
) #{translate("half_price_student")} ) #{translate("half_price_student")}
.card.card-first .card.card-first
.paymentPageFeatures .paymentPageFeatures
h3 #{translate("unlimited_projects")} h3 #{translate("unlimited_projects")}
p #{translate("create_unlimited_projects")} p #{translate("create_unlimited_projects")}
h3 h3
if plan.features.collaborators == -1 if plan.features.collaborators == -1
- var collaboratorCount = 'Unlimited' - var collaboratorCount = 'Unlimited'
@ -229,21 +238,21 @@ block content
- var collaboratorCount = plan.features.collaborators - var collaboratorCount = plan.features.collaborators
| #{translate("collabs_per_proj", {collabcount:collaboratorCount})} | #{translate("collabs_per_proj", {collabcount:collaboratorCount})}
p #{translate("work_on_single_version")}. #{translate("view_collab_edits")} in real time. p #{translate("work_on_single_version")}. #{translate("view_collab_edits")} in real time.
h3 #{translate("full_doc_history")} h3 #{translate("full_doc_history")}
p #{translate("see_what_has_been")} p #{translate("see_what_has_been")}
span.added #{translate("added")} span.added #{translate("added")}
| #{translate("and")} | #{translate("and")}
span.removed #{translate("removed")}. span.removed #{translate("removed")}.
| #{translate("restore_to_any_older_version")}. | #{translate("restore_to_any_older_version")}.
h3 #{translate("sync_to_dropbox")} h3 #{translate("sync_to_dropbox")}
p p
| #{translate("acces_work_from_anywhere")}. | #{translate("acces_work_from_anywhere")}.
| #{translate("work_offline_and_sync_with_dropbox")}. | #{translate("work_offline_and_sync_with_dropbox")}.
hr hr
p.small.text-center(ng-non-bindable) We're confident that you'll love #{settings.appName}, but if not you can cancel anytime. We'll give you your money back, no questions asked, if you let us know within 30 days. p.small.text-center(ng-non-bindable) We're confident that you'll love #{settings.appName}, but if not you can cancel anytime. We'll give you your money back, no questions asked, if you let us know within 30 days.

View file

@ -136,6 +136,7 @@ module.exports = settings =
url: "http://#{process.env['GITHUB_SYNC_HOST'] or 'localhost'}:3022" url: "http://#{process.env['GITHUB_SYNC_HOST'] or 'localhost'}:3022"
recurly: recurly:
apiKey: process.env['RECURLY_API_KEY'] or '' apiKey: process.env['RECURLY_API_KEY'] or ''
apiVersion: process.env['RECURLY_API_VERSION']
subdomain: process.env['RECURLY_SUBDOMAIN'] or '' subdomain: process.env['RECURLY_SUBDOMAIN'] or ''
publicKey: process.env['RECURLY_PUBLIC_KEY'] or '' publicKey: process.env['RECURLY_PUBLIC_KEY'] or ''
geoIpLookup: geoIpLookup:

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,8 @@
// main bundle plus recurly (loaded externaly from CDN) and all files requiring
// Recurly JS
define([
'main',
'recurly',
'main/subscription-dashboard',
'main/new-subscription'
], function() {})

View file

@ -22,8 +22,6 @@ define([
'main/bonus', 'main/bonus',
'main/system-messages', 'main/system-messages',
'main/translations', 'main/translations',
'main/subscription-dashboard',
'main/new-subscription',
'main/annual-upgrade', 'main/annual-upgrade',
'main/announcements', 'main/announcements',
'main/register-users', 'main/register-users',

View file

@ -4,7 +4,7 @@
no-return-assign no-return-assign
*/ */
/* global recurly,_,define */ /* global recurly,_,define */
define(['base', 'directives/creditCards', 'libs/recurly-4.8.5'], App => define(['base', 'directives/creditCards'], App =>
App.controller('NewSubscriptionController', function( App.controller('NewSubscriptionController', function(
$scope, $scope,
MultiCurrencyPricing, MultiCurrencyPricing,
@ -12,10 +12,12 @@ define(['base', 'directives/creditCards', 'libs/recurly-4.8.5'], App =>
event_tracking, event_tracking,
ccUtils ccUtils
) { ) {
if (typeof recurly === 'undefined') { if (typeof recurly === 'undefined' || !recurly) {
throw new Error('Recurly API Library Missing.') $scope.recurlyLoadError = true
return
} }
$scope.recurlyLoadError = false
$scope.currencyCode = MultiCurrencyPricing.currencyCode $scope.currencyCode = MultiCurrencyPricing.currencyCode
$scope.allCurrencies = MultiCurrencyPricing.plans $scope.allCurrencies = MultiCurrencyPricing.plans
$scope.availableCurrencies = {} $scope.availableCurrencies = {}

View file

@ -3,7 +3,7 @@
max-len max-len
*/ */
/* global define,history */ /* global define,history */
define(['base', 'libs/recurly-4.8.5'], function(App, recurly) { define(['base'], function(App) {
App.factory('MultiCurrencyPricing', function() { App.factory('MultiCurrencyPricing', function() {
const currencyCode = window.recomendedCurrency const currencyCode = window.recomendedCurrency

View file

@ -19,8 +19,11 @@ define(['base'], function(App) {
const SUBSCRIPTION_URL = '/user/subscription/update' const SUBSCRIPTION_URL = '/user/subscription/update'
const ensureRecurlyIsSetup = _.once(() => { const ensureRecurlyIsSetup = _.once(() => {
if (!recurly) return if (typeof recurly === 'undefined' || !recurly) {
return false
}
recurly.configure(window.recurlyApiKey) recurly.configure(window.recurlyApiKey)
return true
}) })
App.controller('MetricsEmailController', function($scope, $http) { App.controller('MetricsEmailController', function($scope, $http) {
@ -60,7 +63,7 @@ define(['base'], function(App) {
App.factory('RecurlyPricing', function($q, MultiCurrencyPricing) { App.factory('RecurlyPricing', function($q, MultiCurrencyPricing) {
return { return {
loadDisplayPriceWithTax: function(planCode, currency, taxRate) { loadDisplayPriceWithTax: function(planCode, currency, taxRate) {
ensureRecurlyIsSetup() if (!ensureRecurlyIsSetup()) return
const currencySymbol = MultiCurrencyPricing.plans[currency].symbol const currencySymbol = MultiCurrencyPricing.plans[currency].symbol
const pricing = recurly.Pricing() const pricing = recurly.Pricing()
return $q(function(resolve, reject) { return $q(function(resolve, reject) {
@ -89,7 +92,7 @@ define(['base'], function(App) {
$modal, $modal,
RecurlyPricing RecurlyPricing
) { ) {
ensureRecurlyIsSetup() if (!ensureRecurlyIsSetup()) return
$scope.changePlan = () => $scope.changePlan = () =>
$modal.open({ $modal.open({
@ -164,7 +167,9 @@ define(['base'], function(App) {
}) })
App.controller('RecurlySubscriptionController', function($scope) { App.controller('RecurlySubscriptionController', function($scope) {
$scope.showChangePlanButton = !subscription.groupPlan const recurlyIsSetup = ensureRecurlyIsSetup()
$scope.showChangePlanButton = recurlyIsSetup && !subscription.groupPlan
$scope.recurlyLoadError = !recurlyIsSetup
$scope.switchToDefaultView = () => { $scope.switchToDefaultView = () => {
$scope.showCancellation = false $scope.showCancellation = false
@ -188,6 +193,7 @@ define(['base'], function(App) {
RecurlyPricing, RecurlyPricing,
$http $http
) { ) {
if (!ensureRecurlyIsSetup()) return
const subscription = window.subscription const subscription = window.subscription
const sevenDaysTime = new Date() const sevenDaysTime = new Date()
sevenDaysTime.setDate(sevenDaysTime.getDate() + 7) sevenDaysTime.setDate(sevenDaysTime.getDate() + 7)