diff --git a/services/web/app/src/Features/Project/ProjectListController.mjs b/services/web/app/src/Features/Project/ProjectListController.mjs index a3a677e710..ef6c668244 100644 --- a/services/web/app/src/Features/Project/ProjectListController.mjs +++ b/services/web/app/src/Features/Project/ProjectListController.mjs @@ -496,7 +496,8 @@ async function projectListPage(req, res, next) { showInrGeoBanner = true } - showLATAMBanner = ['MX', 'CO', 'CL', 'PE'].includes(countryCode) + showLATAMBanner = + !!countryCode && ['MX', 'CO', 'CL', 'PE'].includes(countryCode) // LATAM Banner needs to know which currency to display if (showLATAMBanner) { recommendedCurrency = currencyCode diff --git a/services/web/app/src/Features/Subscription/SubscriptionController.mjs b/services/web/app/src/Features/Subscription/SubscriptionController.mjs index 820023d8a8..ca0e54dd58 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionController.mjs +++ b/services/web/app/src/Features/Subscription/SubscriptionController.mjs @@ -48,6 +48,10 @@ const { const SUBSCRIPTION_PAUSED_REDIRECT_PATH = '/user/subscription?redirect-reason=subscription-paused' +/** + * @typedef {import('../../../../types/subscription/currency').CurrencyCode} CurrencyCode + */ + /** * Check if a Stripe subscription is currently paused * @param {Object} subscription - The subscription object @@ -972,6 +976,9 @@ async function refreshUserFeatures(req, res) { res.sendStatus(200) } +/** + * @returns {Promise<{currency: CurrencyCode, recommendedCurrency: CurrencyCode, countryCode: string|undefined}>} + */ async function getRecommendedCurrency(req, res) { const userId = SessionManager.getLoggedInUserId(req.session) let ip = req.ip diff --git a/services/web/app/src/infrastructure/GeoIpLookup.mjs b/services/web/app/src/infrastructure/GeoIpLookup.mjs index 4c7ff12466..21b09d7cce 100644 --- a/services/web/app/src/infrastructure/GeoIpLookup.mjs +++ b/services/web/app/src/infrastructure/GeoIpLookup.mjs @@ -1,9 +1,16 @@ +// @ts-check + import settings from '@overleaf/settings' import logger from '@overleaf/logger' import { fetchJson } from '@overleaf/fetch-utils' -const DEFAULT_CURRENCY_CODE = 'USD' +/** + * @typedef {import('../../../types/subscription/currency').CurrencyCode} CurrencyCode + */ +const DEFAULT_CURRENCY_CODE = /** @type {const} */ 'USD' + +/** @type {Record} */ const currencyMappings = { GB: 'GBP', US: 'USD', @@ -84,6 +91,9 @@ async function getDetails(ip, callback) { return await fetchJson(url, { signal: AbortSignal.timeout(1_000) }) } +/** + * @returns {Promise<{currencyCode: CurrencyCode, countryCode: string|undefined}>} + */ async function getCurrencyCode(ip) { let ipDetails try { @@ -93,7 +103,7 @@ async function getCurrencyCode(ip) { { err, ip }, `problem getting currencyCode for ip, defaulting to ${DEFAULT_CURRENCY_CODE}` ) - return { currencyCode: DEFAULT_CURRENCY_CODE } + return { currencyCode: DEFAULT_CURRENCY_CODE, countryCode: undefined } } const countryCode = ipDetails && ipDetails.country_code diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index c73ad484ac..87c39dc689 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -1812,6 +1812,7 @@ "strongly_disagree": "", "student": "", "student_disclaimer": "", + "student_discount": "", "subject": "", "subject_area": "", "subject_to_additional_vat": "", diff --git a/services/web/locales/en.json b/services/web/locales/en.json index fe187242b4..c784a13048 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -191,7 +191,7 @@ "apply_educational_discount": "Apply educational discount", "apply_educational_discount_description": "40% discount for groups using __appName__ for teaching", "apply_educational_discount_description_with_group_discount": "Get a total of 40% off for groups using __appName__ for teaching", - "apply_educational_discount_individual": "Apply 50% student discount", + "apply_student_discount": "Apply student discount", "apply_suggestion": "Apply suggestion", "april": "April", "archive": "Archive", @@ -2305,6 +2305,7 @@ "strongly_disagree": "Strongly disagree", "student": "Student", "student_disclaimer": "The educational discount applies to all students at secondary and postsecondary institutions (schools and universities). We may contact you to confirm that you’re eligible for the discount.", + "student_discount": "Student discount", "student_verification_required": "Student verification required", "students": "Students", "subject": "Subject",