Enable additional currencies when purchasing (or upgrading to) a group plan (#4884)

* Add script to fetch group data pricing from Recurly

* Update groups pricing data using script to fetch prices from Recurly

* Add additional currencies to saas settings

* Refactor group plans upgrade modal to use shared options from settings

GitOrigin-RevId: 6d13d5b152d01e0399f9d2b8f6f8bf99784589e8
This commit is contained in:
Thomas 2022-01-11 15:57:03 +01:00 committed by Copybot
parent 7288c05075
commit 5e61fce3b4
6 changed files with 441 additions and 138 deletions

View file

@ -8,6 +8,9 @@ const Path = require('path')
// It is also used by scripts/recurly/sync_recurly.rb, which will make sure
// Recurly has a plan configured for all the groups, and that the prices are
// up to date with the data in groups.json.
// Alternatively, scripts/recurly/get_recurly_group_prices.rb can be used to
// fetch pricing data and generate a groups.json using the current Recurly
// prices
const data = fs.readFileSync(
Path.join(__dirname, '/../../../templates/plans/groups.json')
)

View file

@ -160,6 +160,7 @@ async function userSubscriptionPage(req, res) {
managedPublishers,
v1SubscriptionStatus,
currentInstitutionsWithLicence,
groupPlanModalOptions,
}
res.render('subscriptions/dashboard', data)
}

View file

@ -1,104 +1,42 @@
{
"enterprise": {
"collaborator": {
"USD": {
"2": 252,
"3": 376,
"4": 495,
"5": 615,
"10": 1170,
"20": 2160,
"50": 4950
},
"EUR": {
"2": 235,
"3": 352,
"4": 468,
"5": 584,
"10": 1090,
"20": 2015,
"50": 4620
},
"GBP": {
"2": 198,
"3": 296,
"4": 394,
"5": 492,
"10": 935,
"20": 1730,
"50": 3960
}
},
"professional": {
"USD": {
"2": 504,
"3": 752,
"4": 990,
"5": 1230,
"10": 2340,
"20": 4320,
"50": 9900
},
"EUR": {
"2": 470,
"3": 704,
"4": 936,
"5": 1168,
"10": 2185,
"20": 4030,
"50": 9240
},
"GBP": {
"2": 396,
"3": 592,
"4": 788,
"5": 984,
"10": 1870,
"20": 3455,
"50": 7920
}
}
},
"educational": {
"collaborator": {
"USD": {
"2": 252,
"3": 376,
"4": 495,
"5": 615,
"10": 695,
"20": 1295,
"50": 2970
},
"EUR": {
"2": 235,
"3": 352,
"4": 468,
"5": 584,
"10": 655,
"20": 1210,
"50": 2770
},
"GBP": {
"2": 198,
"3": 296,
"4": 394,
"5": 492,
"10": 560,
"20": 1035,
"50": 2375
}
},
"professional": {
"USD": {
"AUD": {
"2": 604,
"3": 906,
"4": 1208,
"5": 1510,
"10": 1685,
"20": 3110,
"50": 7130
},
"CAD": {
"2": 570,
"3": 854,
"4": 1138,
"5": 1420,
"10": 1590,
"20": 2940,
"50": 6730
},
"CHF": {
"2": 504,
"3": 752,
"4": 990,
"5": 1230,
"10": 1390,
"3": 756,
"4": 1008,
"5": 1260,
"10": 1405,
"20": 2590,
"50": 5940
},
"DKK": {
"2": 3024,
"3": 4536,
"4": 6048,
"5": 7560,
"10": 8425,
"20": 15550,
"50": 35640
},
"EUR": {
"2": 470,
"3": 704,
@ -116,7 +54,357 @@
"10": 1125,
"20": 2075,
"50": 4750
},
"NOK": {
"2": 3696,
"3": 5544,
"4": 7392,
"5": 9240,
"10": 10295,
"20": 19010,
"50": 43560
},
"NZD": {
"2": 604,
"3": 906,
"4": 1208,
"5": 1510,
"10": 1685,
"20": 3110,
"50": 7130
},
"SEK": {
"2": 3696,
"3": 5544,
"4": 7392,
"5": 9240,
"10": 10295,
"20": 19010,
"50": 43560
},
"SGD": {
"2": 672,
"3": 1008,
"4": 1344,
"5": 1680,
"10": 1870,
"20": 3455,
"50": 7920
},
"USD": {
"2": 504,
"3": 752,
"4": 990,
"5": 1230,
"10": 1390,
"20": 2590,
"50": 5940
}
},
"collaborator": {
"AUD": {
"2": 302,
"3": 453,
"4": 604,
"5": 755,
"10": 840,
"20": 1555,
"50": 3565
},
"CAD": {
"2": 285,
"3": 427,
"4": 569,
"5": 710,
"10": 795,
"20": 1470,
"50": 3365
},
"CHF": {
"2": 252,
"3": 378,
"4": 504,
"5": 630,
"10": 700,
"20": 1295,
"50": 2970
},
"DKK": {
"2": 1512,
"3": 2268,
"4": 3024,
"5": 3780,
"10": 4210,
"20": 7775,
"50": 17820
},
"EUR": {
"2": 235,
"3": 352,
"4": 468,
"5": 584,
"10": 655,
"20": 1210,
"50": 2770
},
"GBP": {
"2": 198,
"3": 296,
"4": 394,
"5": 492,
"10": 560,
"20": 1035,
"50": 2375
},
"NOK": {
"2": 1848,
"3": 2772,
"4": 3696,
"5": 4620,
"10": 5150,
"20": 9505,
"50": 21780
},
"NZD": {
"2": 302,
"3": 453,
"4": 604,
"5": 755,
"10": 840,
"20": 1555,
"50": 3565
},
"SEK": {
"2": 1848,
"3": 2772,
"4": 3696,
"5": 4620,
"10": 5150,
"20": 9505,
"50": 21780
},
"SGD": {
"2": 336,
"3": 504,
"4": 672,
"5": 840,
"10": 935,
"20": 1730,
"50": 3960
},
"USD": {
"2": 252,
"3": 376,
"4": 495,
"5": 615,
"10": 695,
"20": 1295,
"50": 2970
}
}
},
"enterprise": {
"professional": {
"AUD": {
"2": 604,
"3": 906,
"4": 1208,
"5": 1510,
"10": 2810,
"20": 5185,
"50": 11880
},
"CAD": {
"2": 570,
"3": 854,
"4": 1138,
"5": 1420,
"10": 2650,
"20": 4895,
"50": 11220
},
"CHF": {
"2": 504,
"3": 756,
"4": 1008,
"5": 1260,
"10": 2340,
"20": 4320,
"50": 9900
},
"DKK": {
"2": 3024,
"3": 4536,
"4": 6048,
"5": 7560,
"10": 14040,
"20": 25920,
"50": 59400
},
"EUR": {
"2": 470,
"3": 704,
"4": 936,
"5": 1168,
"10": 2185,
"20": 4030,
"50": 9240
},
"GBP": {
"2": 396,
"3": 592,
"4": 788,
"5": 984,
"10": 1870,
"20": 3455,
"50": 7920
},
"NOK": {
"2": 3696,
"3": 5544,
"4": 7392,
"5": 9240,
"10": 17160,
"20": 31680,
"50": 72600
},
"NZD": {
"2": 604,
"3": 906,
"4": 1208,
"5": 1510,
"10": 2810,
"20": 5185,
"50": 11880
},
"SEK": {
"2": 3696,
"3": 5544,
"4": 7392,
"5": 9240,
"10": 17160,
"20": 31680,
"50": 72600
},
"SGD": {
"2": 672,
"3": 1008,
"4": 1344,
"5": 1680,
"10": 3120,
"20": 5760,
"50": 13200
},
"USD": {
"2": 504,
"3": 752,
"4": 990,
"5": 1230,
"10": 2340,
"20": 4320,
"50": 9900
}
},
"collaborator": {
"AUD": {
"2": 302,
"3": 453,
"4": 604,
"5": 755,
"10": 1405,
"20": 2590,
"50": 5940
},
"CAD": {
"2": 285,
"3": 427,
"4": 569,
"5": 710,
"10": 1325,
"20": 2450,
"50": 5610
},
"CHF": {
"2": 252,
"3": 378,
"4": 504,
"5": 630,
"10": 1170,
"20": 2160,
"50": 4950
},
"DKK": {
"2": 1512,
"3": 2268,
"4": 3024,
"5": 3780,
"10": 7020,
"20": 12960,
"50": 29700
},
"EUR": {
"2": 235,
"3": 352,
"4": 468,
"5": 584,
"10": 1090,
"20": 2015,
"50": 4620
},
"GBP": {
"2": 198,
"3": 296,
"4": 394,
"5": 492,
"10": 935,
"20": 1730,
"50": 3960
},
"NOK": {
"2": 1848,
"3": 2772,
"4": 3696,
"5": 4620,
"10": 8580,
"20": 15840,
"50": 36300
},
"NZD": {
"2": 302,
"3": 453,
"4": 604,
"5": 755,
"10": 1405,
"20": 2590,
"50": 5940
},
"SEK": {
"2": 1848,
"3": 2772,
"4": 3696,
"5": 4620,
"10": 8580,
"20": 15840,
"50": 36300
},
"SGD": {
"2": 336,
"3": 504,
"4": 672,
"5": 840,
"10": 1560,
"20": 2880,
"50": 6600
},
"USD": {
"2": 252,
"3": 376,
"4": 495,
"5": 615,
"10": 1170,
"20": 2160,
"50": 4950
}
}
}
}
}

View file

@ -13,6 +13,7 @@ block append meta
meta(name="ol-subscription" data-type="json" content=personalSubscription)
meta(name="ol-recomendedCurrency" content=personalSubscription.recurly.currency)
meta(name="ol-groupPlans" data-type="json" content=groupPlans)
meta(name="ol-groupPlanModalOptions" data-type="json" content=groupPlanModalOptions)
block content
main.content.content-alt#main-content(ng-cloak)

View file

@ -21,6 +21,8 @@ import App from '../base'
import getMeta from '../utils/meta'
const SUBSCRIPTION_URL = '/user/subscription/update'
const GROUP_PLAN_MODAL_OPTIONS = getMeta('ol-groupPlanModalOptions')
const ensureRecurlyIsSetup = _.once(() => {
if (typeof recurly === 'undefined' || !recurly) {
return false
@ -102,7 +104,11 @@ App.controller('ChangePlanToGroupFormController', function ($scope, $modal) {
const subscription = getMeta('ol-subscription')
const currency = subscription.recurly.currency
if (['USD', 'GBP', 'EUR'].includes(currency)) {
const validCurrencies = GROUP_PLAN_MODAL_OPTIONS.currencies.map(
item => item.code
)
if (validCurrencies.includes(currency)) {
$scope.isValidCurrencyForUpgrade = true
}
@ -123,48 +129,7 @@ App.controller('ChangePlanToGroupFormController', function ($scope, $modal) {
App.controller(
'GroupPlansModalUpgradeController',
function ($scope, $modal, $location, $http, RecurlyPricing) {
$scope.options = {
plan_codes: [
{
display: 'Collaborator',
code: 'collaborator',
},
{
display: 'Professional',
code: 'professional',
},
],
currencies: [
{
display: 'USD ($)',
code: 'USD',
},
{
display: 'GBP (£)',
code: 'GBP',
},
{
display: 'EUR (€)',
code: 'EUR',
},
],
currencySymbols: {
USD: '$',
EUR: '€',
GBP: '£',
},
sizes: [2, 3, 4, 5, 10, 20, 50],
usages: [
{
display: 'Enterprise',
code: 'enterprise',
},
{
display: 'Educational',
code: 'educational',
},
],
}
$scope.options = GROUP_PLAN_MODAL_OPTIONS
$scope.prices = getMeta('ol-groupPlans')

View file

@ -0,0 +1,45 @@
// Get prices from Recurly in GroupPlansData format, ie to update:
// app/templates/plans/groups.json
//
// Usage example:
// node scripts/recurly/get_recurly_group_prices.js
const recurly = require('recurly')
const Settings = require('@overleaf/settings')
const recurlySettings = Settings.apis.recurly
const recurlyApiKey = recurlySettings ? recurlySettings.apiKey : undefined
const client = new recurly.Client(recurlyApiKey)
async function getRecurlyGroupPrices() {
const prices = {}
const plans = client.listPlans({ params: { limit: 200 } })
for await (const plan of plans.each()) {
if (plan.code.substr(0, 6) === 'group_') {
const [, type, size, usage] = plan.code.split('_')
plan.currencies.forEach(planPricing => {
const { currency, unitAmount } = planPricing
prices[usage] = prices[usage] || {}
prices[usage][type] = prices[usage][type] || {}
prices[usage][type][currency] = prices[usage][type][currency] || {}
prices[usage][type][currency][size] = unitAmount
})
}
}
return prices
}
async function main() {
const prices = await getRecurlyGroupPrices()
console.log(JSON.stringify(prices, undefined, 2))
}
main()
.then(() => {
process.exit(0)
})
.catch(error => {
console.error({ error })
process.exit(1)
})