mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 13:35:42 +09:00
feat(api): add SEK, DKK and NOK as localized currencies (#2684)
This commit is contained in:
1 parent
0e470f532e
commit
3af43b3366
50 files changed
+668
-390
No files matched your search
@@ -372,17 +372,29 @@ export function buildAPIConfigFromMaster(master: MasterConfig): APIConfig {
|
||||
monthlyUsd: master.integrations.stripe.prices.monthly_usd,
|
||||
monthlyEur: master.integrations.stripe.prices.monthly_eur,
|
||||
monthlyBrl: master.integrations.stripe.prices.monthly_brl,
|
||||
monthlyDkk: master.integrations.stripe.prices.monthly_dkk,
|
||||
monthlyInr: master.integrations.stripe.prices.monthly_inr,
|
||||
monthlyNok: master.integrations.stripe.prices.monthly_nok,
|
||||
monthlyPln: master.integrations.stripe.prices.monthly_pln,
|
||||
monthlySek: master.integrations.stripe.prices.monthly_sek,
|
||||
monthlyTry: master.integrations.stripe.prices.monthly_try,
|
||||
yearlyUsd: master.integrations.stripe.prices.yearly_usd,
|
||||
yearlyEur: master.integrations.stripe.prices.yearly_eur,
|
||||
yearlyBrl: master.integrations.stripe.prices.yearly_brl,
|
||||
yearlyDkk: master.integrations.stripe.prices.yearly_dkk,
|
||||
yearlyInr: master.integrations.stripe.prices.yearly_inr,
|
||||
yearlyNok: master.integrations.stripe.prices.yearly_nok,
|
||||
yearlyPln: master.integrations.stripe.prices.yearly_pln,
|
||||
yearlySek: master.integrations.stripe.prices.yearly_sek,
|
||||
yearlyTry: master.integrations.stripe.prices.yearly_try,
|
||||
gift1MonthUsd: master.integrations.stripe.prices.gift_1_month_usd,
|
||||
gift1MonthEur: master.integrations.stripe.prices.gift_1_month_eur,
|
||||
gift1MonthSek: master.integrations.stripe.prices.gift_1_month_sek,
|
||||
gift1YearSek: master.integrations.stripe.prices.gift_1_year_sek,
|
||||
gift1MonthDkk: master.integrations.stripe.prices.gift_1_month_dkk,
|
||||
gift1YearDkk: master.integrations.stripe.prices.gift_1_year_dkk,
|
||||
gift1MonthNok: master.integrations.stripe.prices.gift_1_month_nok,
|
||||
gift1YearNok: master.integrations.stripe.prices.gift_1_year_nok,
|
||||
gift1MonthBrl: master.integrations.stripe.prices.gift_1_month_brl,
|
||||
gift1MonthInr: master.integrations.stripe.prices.gift_1_month_inr,
|
||||
gift1MonthPln: master.integrations.stripe.prices.gift_1_month_pln,
|
||||
|
||||
@@ -226,17 +226,29 @@ export interface APIConfig {
|
||||
monthlyUsd?: string;
|
||||
monthlyEur?: string;
|
||||
monthlyBrl?: string;
|
||||
monthlyDkk?: string;
|
||||
monthlyInr?: string;
|
||||
monthlyNok?: string;
|
||||
monthlyPln?: string;
|
||||
monthlySek?: string;
|
||||
monthlyTry?: string;
|
||||
yearlyUsd?: string;
|
||||
yearlyEur?: string;
|
||||
yearlyBrl?: string;
|
||||
yearlyDkk?: string;
|
||||
yearlyInr?: string;
|
||||
yearlyNok?: string;
|
||||
yearlyPln?: string;
|
||||
yearlySek?: string;
|
||||
yearlyTry?: string;
|
||||
gift1MonthUsd?: string;
|
||||
gift1MonthEur?: string;
|
||||
gift1MonthSek?: string;
|
||||
gift1YearSek?: string;
|
||||
gift1MonthDkk?: string;
|
||||
gift1YearDkk?: string;
|
||||
gift1MonthNok?: string;
|
||||
gift1YearNok?: string;
|
||||
gift1MonthBrl?: string;
|
||||
gift1MonthInr?: string;
|
||||
gift1MonthPln?: string;
|
||||
|
||||
@@ -32064,9 +32064,9 @@
|
||||
"description": "The amount the user is actually charged, in the currency minor unit"
|
||||
},
|
||||
"currency": {
|
||||
"enum": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"],
|
||||
"enum": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"],
|
||||
"type": "string",
|
||||
"x-enumNames": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"],
|
||||
"x-enumNames": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"],
|
||||
"description": "Currency of the charged amount"
|
||||
},
|
||||
"billing_cycle": {
|
||||
@@ -32345,9 +32345,9 @@
|
||||
"currency": {
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"],
|
||||
"enum": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"],
|
||||
"type": "string",
|
||||
"x-enumNames": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"]
|
||||
"x-enumNames": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"]
|
||||
},
|
||||
{"type": "null"}
|
||||
],
|
||||
@@ -32418,9 +32418,9 @@
|
||||
"currency": {
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"],
|
||||
"enum": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"],
|
||||
"type": "string",
|
||||
"x-enumNames": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"]
|
||||
"x-enumNames": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"]
|
||||
},
|
||||
{"type": "null"}
|
||||
],
|
||||
@@ -32662,15 +32662,15 @@
|
||||
"description": "Gift 1 year price amount in the currency minor unit"
|
||||
},
|
||||
"currency": {
|
||||
"enum": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"],
|
||||
"enum": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"],
|
||||
"type": "string",
|
||||
"x-enumNames": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"],
|
||||
"x-enumNames": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"],
|
||||
"description": "Currency for the prices"
|
||||
},
|
||||
"gift_currency": {
|
||||
"enum": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"],
|
||||
"enum": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"],
|
||||
"type": "string",
|
||||
"x-enumNames": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"],
|
||||
"x-enumNames": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"],
|
||||
"description": "Currency for gift prices"
|
||||
}
|
||||
},
|
||||
@@ -32749,9 +32749,9 @@
|
||||
"description": "Amount billed before the switch, in the currency minor unit"
|
||||
},
|
||||
"currency": {
|
||||
"enum": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"],
|
||||
"enum": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"],
|
||||
"type": "string",
|
||||
"x-enumNames": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"],
|
||||
"x-enumNames": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"],
|
||||
"description": "Currency of both amounts"
|
||||
}
|
||||
},
|
||||
@@ -32788,9 +32788,9 @@
|
||||
"description": "Amount billed before the switch, in the currency minor unit"
|
||||
},
|
||||
"currency": {
|
||||
"enum": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"],
|
||||
"enum": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"],
|
||||
"type": "string",
|
||||
"x-enumNames": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"],
|
||||
"x-enumNames": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"],
|
||||
"description": "Currency of both amounts"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -56,8 +56,11 @@ const LEGACY_SLOT_CURRENCIES: Record<string, Currency | undefined> = {
|
||||
usd: 'USD',
|
||||
eur: 'EUR',
|
||||
brl: 'BRL',
|
||||
dkk: 'DKK',
|
||||
inr: 'INR',
|
||||
nok: 'NOK',
|
||||
pln: 'PLN',
|
||||
sek: 'SEK',
|
||||
try: 'TRY',
|
||||
};
|
||||
|
||||
@@ -109,6 +112,14 @@ export class ProductRegistry {
|
||||
currency: 'BRL',
|
||||
billingCycle: 'monthly',
|
||||
});
|
||||
this.registerProduct(prices.monthlyDkk, {
|
||||
type: ProductType.MONTHLY_SUBSCRIPTION,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
durationMonths: 1,
|
||||
isGift: false,
|
||||
currency: 'DKK',
|
||||
billingCycle: 'monthly',
|
||||
});
|
||||
this.registerProduct(prices.monthlyInr, {
|
||||
type: ProductType.MONTHLY_SUBSCRIPTION,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
@@ -117,6 +128,14 @@ export class ProductRegistry {
|
||||
currency: 'INR',
|
||||
billingCycle: 'monthly',
|
||||
});
|
||||
this.registerProduct(prices.monthlyNok, {
|
||||
type: ProductType.MONTHLY_SUBSCRIPTION,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
durationMonths: 1,
|
||||
isGift: false,
|
||||
currency: 'NOK',
|
||||
billingCycle: 'monthly',
|
||||
});
|
||||
this.registerProduct(prices.monthlyPln, {
|
||||
type: ProductType.MONTHLY_SUBSCRIPTION,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
@@ -125,6 +144,14 @@ export class ProductRegistry {
|
||||
currency: 'PLN',
|
||||
billingCycle: 'monthly',
|
||||
});
|
||||
this.registerProduct(prices.monthlySek, {
|
||||
type: ProductType.MONTHLY_SUBSCRIPTION,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
durationMonths: 1,
|
||||
isGift: false,
|
||||
currency: 'SEK',
|
||||
billingCycle: 'monthly',
|
||||
});
|
||||
this.registerProduct(prices.monthlyTry, {
|
||||
type: ProductType.MONTHLY_SUBSCRIPTION,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
@@ -157,6 +184,14 @@ export class ProductRegistry {
|
||||
currency: 'BRL',
|
||||
billingCycle: 'yearly',
|
||||
});
|
||||
this.registerProduct(prices.yearlyDkk, {
|
||||
type: ProductType.YEARLY_SUBSCRIPTION,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
durationMonths: 12,
|
||||
isGift: false,
|
||||
currency: 'DKK',
|
||||
billingCycle: 'yearly',
|
||||
});
|
||||
this.registerProduct(prices.yearlyInr, {
|
||||
type: ProductType.YEARLY_SUBSCRIPTION,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
@@ -165,6 +200,14 @@ export class ProductRegistry {
|
||||
currency: 'INR',
|
||||
billingCycle: 'yearly',
|
||||
});
|
||||
this.registerProduct(prices.yearlyNok, {
|
||||
type: ProductType.YEARLY_SUBSCRIPTION,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
durationMonths: 12,
|
||||
isGift: false,
|
||||
currency: 'NOK',
|
||||
billingCycle: 'yearly',
|
||||
});
|
||||
this.registerProduct(prices.yearlyPln, {
|
||||
type: ProductType.YEARLY_SUBSCRIPTION,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
@@ -173,6 +216,14 @@ export class ProductRegistry {
|
||||
currency: 'PLN',
|
||||
billingCycle: 'yearly',
|
||||
});
|
||||
this.registerProduct(prices.yearlySek, {
|
||||
type: ProductType.YEARLY_SUBSCRIPTION,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
durationMonths: 12,
|
||||
isGift: false,
|
||||
currency: 'SEK',
|
||||
billingCycle: 'yearly',
|
||||
});
|
||||
this.registerProduct(prices.yearlyTry, {
|
||||
type: ProductType.YEARLY_SUBSCRIPTION,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
@@ -209,6 +260,48 @@ export class ProductRegistry {
|
||||
isGift: true,
|
||||
currency: 'INR',
|
||||
});
|
||||
this.registerProduct(prices.gift1MonthDkk, {
|
||||
type: ProductType.GIFT_1_MONTH,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
durationMonths: 1,
|
||||
isGift: true,
|
||||
currency: 'DKK',
|
||||
});
|
||||
this.registerProduct(prices.gift1YearDkk, {
|
||||
type: ProductType.GIFT_1_YEAR,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
durationMonths: 12,
|
||||
isGift: true,
|
||||
currency: 'DKK',
|
||||
});
|
||||
this.registerProduct(prices.gift1MonthNok, {
|
||||
type: ProductType.GIFT_1_MONTH,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
durationMonths: 1,
|
||||
isGift: true,
|
||||
currency: 'NOK',
|
||||
});
|
||||
this.registerProduct(prices.gift1YearNok, {
|
||||
type: ProductType.GIFT_1_YEAR,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
durationMonths: 12,
|
||||
isGift: true,
|
||||
currency: 'NOK',
|
||||
});
|
||||
this.registerProduct(prices.gift1MonthSek, {
|
||||
type: ProductType.GIFT_1_MONTH,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
durationMonths: 1,
|
||||
isGift: true,
|
||||
currency: 'SEK',
|
||||
});
|
||||
this.registerProduct(prices.gift1YearSek, {
|
||||
type: ProductType.GIFT_1_YEAR,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
durationMonths: 12,
|
||||
isGift: true,
|
||||
currency: 'SEK',
|
||||
});
|
||||
this.registerProduct(prices.gift1MonthPln, {
|
||||
type: ProductType.GIFT_1_MONTH,
|
||||
premiumType: UserPremiumTypes.SUBSCRIPTION,
|
||||
@@ -319,12 +412,21 @@ export class ProductRegistry {
|
||||
if (normalizedCurrency === 'brl') {
|
||||
return billingCycle === 'monthly' ? (prices.monthlyBrl ?? null) : (prices.yearlyBrl ?? null);
|
||||
}
|
||||
if (normalizedCurrency === 'dkk') {
|
||||
return billingCycle === 'monthly' ? (prices.monthlyDkk ?? null) : (prices.yearlyDkk ?? null);
|
||||
}
|
||||
if (normalizedCurrency === 'inr') {
|
||||
return billingCycle === 'monthly' ? (prices.monthlyInr ?? null) : (prices.yearlyInr ?? null);
|
||||
}
|
||||
if (normalizedCurrency === 'nok') {
|
||||
return billingCycle === 'monthly' ? (prices.monthlyNok ?? null) : (prices.yearlyNok ?? null);
|
||||
}
|
||||
if (normalizedCurrency === 'pln') {
|
||||
return billingCycle === 'monthly' ? (prices.monthlyPln ?? null) : (prices.yearlyPln ?? null);
|
||||
}
|
||||
if (normalizedCurrency === 'sek') {
|
||||
return billingCycle === 'monthly' ? (prices.monthlySek ?? null) : (prices.yearlySek ?? null);
|
||||
}
|
||||
if (normalizedCurrency === 'try') {
|
||||
return billingCycle === 'monthly' ? (prices.monthlyTry ?? null) : (prices.yearlyTry ?? null);
|
||||
}
|
||||
@@ -349,6 +451,15 @@ export class ProductRegistry {
|
||||
if (normalizedCurrency === 'inr') {
|
||||
return duration === 'gift_1_month' ? (prices.gift1MonthInr ?? null) : (prices.gift1YearInr ?? null);
|
||||
}
|
||||
if (normalizedCurrency === 'dkk') {
|
||||
return duration === 'gift_1_month' ? (prices.gift1MonthDkk ?? null) : (prices.gift1YearDkk ?? null);
|
||||
}
|
||||
if (normalizedCurrency === 'nok') {
|
||||
return duration === 'gift_1_month' ? (prices.gift1MonthNok ?? null) : (prices.gift1YearNok ?? null);
|
||||
}
|
||||
if (normalizedCurrency === 'sek') {
|
||||
return duration === 'gift_1_month' ? (prices.gift1MonthSek ?? null) : (prices.gift1YearSek ?? null);
|
||||
}
|
||||
if (normalizedCurrency === 'pln') {
|
||||
return duration === 'gift_1_month' ? (prices.gift1MonthPln ?? null) : (prices.gift1YearPln ?? null);
|
||||
}
|
||||
|
||||
@@ -104,8 +104,11 @@ function normalizeCurrency(value: string | null | undefined): Currency | null {
|
||||
currency === 'USD' ||
|
||||
currency === 'EUR' ||
|
||||
currency === 'BRL' ||
|
||||
currency === 'DKK' ||
|
||||
currency === 'INR' ||
|
||||
currency === 'NOK' ||
|
||||
currency === 'PLN' ||
|
||||
currency === 'SEK' ||
|
||||
currency === 'TRY'
|
||||
) {
|
||||
return currency;
|
||||
|
||||
@@ -1108,6 +1108,15 @@ export class StripeCheckoutService {
|
||||
yearly: prices.yearlyBrl,
|
||||
currency,
|
||||
};
|
||||
case 'DKK':
|
||||
if (!prices.monthlyDkk || !prices.yearlyDkk) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
monthly: prices.monthlyDkk,
|
||||
yearly: prices.yearlyDkk,
|
||||
currency,
|
||||
};
|
||||
case 'INR':
|
||||
if (!prices.monthlyInr || !prices.yearlyInr) {
|
||||
return null;
|
||||
@@ -1117,6 +1126,15 @@ export class StripeCheckoutService {
|
||||
yearly: prices.yearlyInr,
|
||||
currency,
|
||||
};
|
||||
case 'NOK':
|
||||
if (!prices.monthlyNok || !prices.yearlyNok) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
monthly: prices.monthlyNok,
|
||||
yearly: prices.yearlyNok,
|
||||
currency,
|
||||
};
|
||||
case 'PLN':
|
||||
if (!prices.monthlyPln || !prices.yearlyPln) {
|
||||
return null;
|
||||
@@ -1126,6 +1144,15 @@ export class StripeCheckoutService {
|
||||
yearly: prices.yearlyPln,
|
||||
currency,
|
||||
};
|
||||
case 'SEK':
|
||||
if (!prices.monthlySek || !prices.yearlySek) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
monthly: prices.monthlySek,
|
||||
yearly: prices.yearlySek,
|
||||
currency,
|
||||
};
|
||||
case 'TRY':
|
||||
if (!prices.monthlyTry || !prices.yearlyTry) {
|
||||
return null;
|
||||
@@ -1175,6 +1202,33 @@ export class StripeCheckoutService {
|
||||
gift_1_year: prices.gift1YearInr,
|
||||
gift_currency: 'INR',
|
||||
};
|
||||
case 'DKK':
|
||||
if (!prices.gift1MonthDkk || !prices.gift1YearDkk) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
gift_1_month: prices.gift1MonthDkk,
|
||||
gift_1_year: prices.gift1YearDkk,
|
||||
gift_currency: 'DKK',
|
||||
};
|
||||
case 'NOK':
|
||||
if (!prices.gift1MonthNok || !prices.gift1YearNok) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
gift_1_month: prices.gift1MonthNok,
|
||||
gift_1_year: prices.gift1YearNok,
|
||||
gift_currency: 'NOK',
|
||||
};
|
||||
case 'SEK':
|
||||
if (!prices.gift1MonthSek || !prices.gift1YearSek) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
gift_1_month: prices.gift1MonthSek,
|
||||
gift_1_year: prices.gift1YearSek,
|
||||
gift_currency: 'SEK',
|
||||
};
|
||||
case 'PLN':
|
||||
if (!prices.gift1MonthPln || !prices.gift1YearPln) {
|
||||
return null;
|
||||
|
||||
@@ -119,16 +119,22 @@ describe('ProductRegistry - legacy prices', () => {
|
||||
monthly_usd: ['legacy_usd'],
|
||||
monthly_eur: ['legacy_eur'],
|
||||
monthly_brl: ['legacy_brl'],
|
||||
monthly_dkk: ['legacy_dkk'],
|
||||
monthly_inr: ['legacy_inr'],
|
||||
monthly_nok: ['legacy_nok'],
|
||||
monthly_pln: ['legacy_pln'],
|
||||
monthly_sek: ['legacy_sek'],
|
||||
monthly_try: ['legacy_try'],
|
||||
},
|
||||
);
|
||||
expect(registry.getProduct('legacy_usd')?.currency).toBe('USD');
|
||||
expect(registry.getProduct('legacy_eur')?.currency).toBe('EUR');
|
||||
expect(registry.getProduct('legacy_brl')?.currency).toBe('BRL');
|
||||
expect(registry.getProduct('legacy_dkk')?.currency).toBe('DKK');
|
||||
expect(registry.getProduct('legacy_inr')?.currency).toBe('INR');
|
||||
expect(registry.getProduct('legacy_nok')?.currency).toBe('NOK');
|
||||
expect(registry.getProduct('legacy_pln')?.currency).toBe('PLN');
|
||||
expect(registry.getProduct('legacy_sek')?.currency).toBe('SEK');
|
||||
expect(registry.getProduct('legacy_try')?.currency).toBe('TRY');
|
||||
});
|
||||
|
||||
@@ -205,7 +211,7 @@ describe('ProductRegistry - legacy prices', () => {
|
||||
test('no legacy price id is ever returned by either price getter, for any cycle or currency', () => {
|
||||
const registry = buildRegistry(MOCK_PRICES, MOCK_LEGACY_PRICES);
|
||||
const legacyIds = new Set(Object.values(MOCK_LEGACY_PRICES).flatMap((ids) => ids ?? []));
|
||||
const currencies = ['USD', 'EUR', 'BRL', 'INR', 'PLN', 'TRY'];
|
||||
const currencies = ['USD', 'EUR', 'BRL', 'DKK', 'INR', 'NOK', 'PLN', 'SEK', 'TRY'];
|
||||
|
||||
const offered: Array<string> = [];
|
||||
for (const currency of currencies) {
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import type {
|
||||
CurrentSubscriptionPriceResponse,
|
||||
PriceIdsResponse,
|
||||
} from '@fluxer/schema/src/domains/premium/PremiumSchemas';
|
||||
import type {GeoipResult} from '@pkgs/geoip/src/GeoipLookup';
|
||||
import {afterAll, beforeAll, beforeEach, describe, expect, test, vi} from 'vitest';
|
||||
import {createTestAccount} from '../../auth/tests/AuthTestUtils';
|
||||
import {Config} from '../../Config';
|
||||
import {type ApiTestHarness, createApiTestHarness} from '../../test/ApiTestHarness';
|
||||
import {createStripeApiHandlers, type StripeApiHandlers} from '../../test/msw/handlers/StripeApiHandlers';
|
||||
import {server} from '../../test/msw/server';
|
||||
import {HTTP_STATUS} from '../../test/TestConstants';
|
||||
import {createBuilder} from '../../test/TestRequestBuilder';
|
||||
|
||||
const {lookupGeoipMock} = vi.hoisted(() => ({
|
||||
lookupGeoipMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../utils/IpUtils', async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import('../../utils/IpUtils')>()),
|
||||
lookupGeoip: lookupGeoipMock,
|
||||
}));
|
||||
|
||||
const MOCK_PRICES = {
|
||||
monthlyUsd: 'price_nordic_monthly_usd',
|
||||
yearlyUsd: 'price_nordic_yearly_usd',
|
||||
monthlyEur: 'price_nordic_monthly_eur',
|
||||
yearlyEur: 'price_nordic_yearly_eur',
|
||||
monthlyDkk: 'price_nordic_monthly_dkk',
|
||||
yearlyDkk: 'price_nordic_yearly_dkk',
|
||||
monthlyNok: 'price_nordic_monthly_nok',
|
||||
yearlyNok: 'price_nordic_yearly_nok',
|
||||
monthlyPln: 'price_nordic_monthly_pln',
|
||||
yearlyPln: 'price_nordic_yearly_pln',
|
||||
monthlySek: 'price_nordic_monthly_sek',
|
||||
yearlySek: 'price_nordic_yearly_sek',
|
||||
gift1MonthUsd: 'price_nordic_gift_1_month_usd',
|
||||
gift1YearUsd: 'price_nordic_gift_1_year_usd',
|
||||
gift1MonthEur: 'price_nordic_gift_1_month_eur',
|
||||
gift1YearEur: 'price_nordic_gift_1_year_eur',
|
||||
};
|
||||
|
||||
const MOCK_PRICE_SEEDS = {
|
||||
[MOCK_PRICES.monthlyUsd]: {unit_amount: 499, currency: 'usd', interval: 'month' as const},
|
||||
[MOCK_PRICES.yearlyUsd]: {unit_amount: 4999, currency: 'usd', interval: 'year' as const},
|
||||
[MOCK_PRICES.monthlyEur]: {unit_amount: 499, currency: 'eur', interval: 'month' as const},
|
||||
[MOCK_PRICES.yearlyEur]: {unit_amount: 4999, currency: 'eur', interval: 'year' as const},
|
||||
[MOCK_PRICES.monthlyDkk]: {unit_amount: 3700, currency: 'dkk', interval: 'month' as const},
|
||||
[MOCK_PRICES.yearlyDkk]: {unit_amount: 37000, currency: 'dkk', interval: 'year' as const},
|
||||
[MOCK_PRICES.monthlyNok]: {unit_amount: 5400, currency: 'nok', interval: 'month' as const},
|
||||
[MOCK_PRICES.yearlyNok]: {unit_amount: 54000, currency: 'nok', interval: 'year' as const},
|
||||
[MOCK_PRICES.monthlyPln]: {unit_amount: 1900, currency: 'pln', interval: 'month' as const},
|
||||
[MOCK_PRICES.yearlyPln]: {unit_amount: 19000, currency: 'pln', interval: 'year' as const},
|
||||
[MOCK_PRICES.monthlySek]: {unit_amount: 5400, currency: 'sek', interval: 'month' as const},
|
||||
[MOCK_PRICES.yearlySek]: {unit_amount: 54000, currency: 'sek', interval: 'year' as const},
|
||||
};
|
||||
|
||||
const EUR_SUBSCRIPTION_ID = 'sub_nordic_legacy_eur';
|
||||
|
||||
function geoipCountry(countryCode: string | null): GeoipResult {
|
||||
return {
|
||||
countryCode,
|
||||
normalizedIp: '203.0.113.10',
|
||||
city: null,
|
||||
region: null,
|
||||
countryName: null,
|
||||
};
|
||||
}
|
||||
|
||||
describe('Nordic localized currencies', () => {
|
||||
let harness: ApiTestHarness;
|
||||
let stripeHandlers: StripeApiHandlers;
|
||||
let originalPrices: typeof Config.stripe.prices | undefined;
|
||||
|
||||
async function createPurchaser(): Promise<string> {
|
||||
const account = await createTestAccount(harness);
|
||||
await createBuilder(harness, account.token)
|
||||
.post(`/test/users/${account.userId}/security-flags`)
|
||||
.body({email_verified: true})
|
||||
.execute();
|
||||
return account.token;
|
||||
}
|
||||
|
||||
function getPriceIds(countryCode: string | null): Promise<PriceIdsResponse> {
|
||||
lookupGeoipMock.mockResolvedValue(geoipCountry(countryCode));
|
||||
return createBuilder<PriceIdsResponse>(harness, '').get('/premium/price-ids').expect(HTTP_STATUS.OK).execute();
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
originalPrices = Config.stripe.prices;
|
||||
Config.stripe.prices = MOCK_PRICES;
|
||||
harness = await createApiTestHarness();
|
||||
});
|
||||
afterAll(async () => {
|
||||
await harness.shutdown();
|
||||
Config.stripe.prices = originalPrices;
|
||||
});
|
||||
beforeEach(() => {
|
||||
Config.stripe.prices = MOCK_PRICES;
|
||||
lookupGeoipMock.mockReset();
|
||||
lookupGeoipMock.mockResolvedValue(geoipCountry(null));
|
||||
stripeHandlers = createStripeApiHandlers({
|
||||
prices: MOCK_PRICE_SEEDS,
|
||||
subscriptionsListEmpty: true,
|
||||
subscriptions: {
|
||||
[EUR_SUBSCRIPTION_ID]: {
|
||||
customer: `cus_${EUR_SUBSCRIPTION_ID}`,
|
||||
price_id: MOCK_PRICES.monthlyEur,
|
||||
unit_amount: 499,
|
||||
currency: 'eur',
|
||||
interval: 'month',
|
||||
item_id: `si_${EUR_SUBSCRIPTION_ID}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
server.use(...stripeHandlers.handlers);
|
||||
});
|
||||
|
||||
describe('country routing', () => {
|
||||
test('Sweden resolves to the SEK subscription catalog', async () => {
|
||||
const priceIds = await getPriceIds('SE');
|
||||
expect(priceIds.currency).toBe('SEK');
|
||||
expect(priceIds.monthly).toBe(MOCK_PRICES.monthlySek);
|
||||
expect(priceIds.yearly).toBe(MOCK_PRICES.yearlySek);
|
||||
});
|
||||
|
||||
test('Denmark resolves to the DKK subscription catalog', async () => {
|
||||
const priceIds = await getPriceIds('DK');
|
||||
expect(priceIds.currency).toBe('DKK');
|
||||
expect(priceIds.monthly).toBe(MOCK_PRICES.monthlyDkk);
|
||||
expect(priceIds.yearly).toBe(MOCK_PRICES.yearlyDkk);
|
||||
});
|
||||
|
||||
test('Norway resolves to the NOK subscription catalog', async () => {
|
||||
const priceIds = await getPriceIds('NO');
|
||||
expect(priceIds.currency).toBe('NOK');
|
||||
expect(priceIds.monthly).toBe(MOCK_PRICES.monthlyNok);
|
||||
expect(priceIds.yearly).toBe(MOCK_PRICES.yearlyNok);
|
||||
});
|
||||
|
||||
test('Poland still resolves to the PLN subscription catalog', async () => {
|
||||
const priceIds = await getPriceIds('PL');
|
||||
expect(priceIds.currency).toBe('PLN');
|
||||
expect(priceIds.monthly).toBe(MOCK_PRICES.monthlyPln);
|
||||
});
|
||||
|
||||
test.each(['DE', 'NL', 'FI', 'IE', 'IS'])('%s still resolves to the EUR subscription catalog', async (country) => {
|
||||
const priceIds = await getPriceIds(country);
|
||||
expect(priceIds.currency).toBe('EUR');
|
||||
expect(priceIds.monthly).toBe(MOCK_PRICES.monthlyEur);
|
||||
expect(priceIds.yearly).toBe(MOCK_PRICES.yearlyEur);
|
||||
});
|
||||
|
||||
test.each(['SE', 'DK', 'NO'])('%s keeps gifts on the base currency', async (country) => {
|
||||
const priceIds = await getPriceIds(country);
|
||||
expect(priceIds.gift_currency).toBe('EUR');
|
||||
expect(priceIds.gift_1_month).toBe(MOCK_PRICES.gift1MonthEur);
|
||||
expect(priceIds.gift_1_year).toBe(MOCK_PRICES.gift1YearEur);
|
||||
});
|
||||
|
||||
test('a Nordic gift falls through to USD when no EUR gift price is configured', async () => {
|
||||
Config.stripe.prices = {
|
||||
...MOCK_PRICES,
|
||||
gift1MonthEur: undefined,
|
||||
gift1YearEur: undefined,
|
||||
};
|
||||
const priceIds = await getPriceIds('SE');
|
||||
expect(priceIds.currency).toBe('SEK');
|
||||
expect(priceIds.gift_currency).toBe('USD');
|
||||
expect(priceIds.gift_1_month).toBe(MOCK_PRICES.gift1MonthUsd);
|
||||
});
|
||||
|
||||
test('Sweden falls back to EUR while the SEK prices are still unconfigured', async () => {
|
||||
Config.stripe.prices = {
|
||||
...MOCK_PRICES,
|
||||
monthlySek: undefined,
|
||||
yearlySek: undefined,
|
||||
};
|
||||
const priceIds = await getPriceIds('SE');
|
||||
expect(priceIds.currency).toBe('EUR');
|
||||
expect(priceIds.monthly).toBe(MOCK_PRICES.monthlyEur);
|
||||
expect(priceIds.gift_currency).toBe('EUR');
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkout enforcement', () => {
|
||||
test('rejects the EUR subscription price for a purchase that geolocates to Sweden', async () => {
|
||||
lookupGeoipMock.mockResolvedValue(geoipCountry('SE'));
|
||||
const token = await createPurchaser();
|
||||
await createBuilder(harness, token)
|
||||
.post('/stripe/checkout/subscription')
|
||||
.body({price_id: MOCK_PRICES.monthlyEur, country_code: 'SE'})
|
||||
.expect(HTTP_STATUS.BAD_REQUEST, APIErrorCodes.STRIPE_INVALID_PRODUCT_CONFIGURATION)
|
||||
.execute();
|
||||
expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('rejects the USD subscription price for a purchase that geolocates to Sweden', async () => {
|
||||
lookupGeoipMock.mockResolvedValue(geoipCountry('SE'));
|
||||
const token = await createPurchaser();
|
||||
await createBuilder(harness, token)
|
||||
.post('/stripe/checkout/subscription')
|
||||
.body({price_id: MOCK_PRICES.monthlyUsd, country_code: 'US'})
|
||||
.expect(HTTP_STATUS.BAD_REQUEST, APIErrorCodes.STRIPE_INVALID_PRODUCT_CONFIGURATION)
|
||||
.execute();
|
||||
expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('rejects a neighbouring Nordic price for a purchase that geolocates to Sweden', async () => {
|
||||
lookupGeoipMock.mockResolvedValue(geoipCountry('SE'));
|
||||
const token = await createPurchaser();
|
||||
await createBuilder(harness, token)
|
||||
.post('/stripe/checkout/subscription')
|
||||
.body({price_id: MOCK_PRICES.monthlyDkk, country_code: 'DK'})
|
||||
.expect(HTTP_STATUS.BAD_REQUEST, APIErrorCodes.STRIPE_INVALID_PRODUCT_CONFIGURATION)
|
||||
.execute();
|
||||
expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('accepts the SEK subscription price for a purchase that geolocates to Sweden', async () => {
|
||||
lookupGeoipMock.mockResolvedValue(geoipCountry('SE'));
|
||||
const token = await createPurchaser();
|
||||
const response = await createBuilder<{url: string}>(harness, token)
|
||||
.post('/stripe/checkout/subscription')
|
||||
.body({price_id: MOCK_PRICES.monthlySek, country_code: 'SE'})
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.execute();
|
||||
expect(response.url).toContain('checkout.stripe.com');
|
||||
expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('accepts the base gift price from inside Sweden', async () => {
|
||||
lookupGeoipMock.mockResolvedValue(geoipCountry('SE'));
|
||||
const token = await createPurchaser();
|
||||
const response = await createBuilder<{url: string}>(harness, token)
|
||||
.post('/stripe/checkout/gift')
|
||||
.body({price_id: MOCK_PRICES.gift1MonthEur, country_code: 'SE'})
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.execute();
|
||||
expect(response.url).toContain('checkout.stripe.com');
|
||||
expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('rejects the SEK subscription price on the gift endpoint', async () => {
|
||||
lookupGeoipMock.mockResolvedValue(geoipCountry('SE'));
|
||||
const token = await createPurchaser();
|
||||
await createBuilder(harness, token)
|
||||
.post('/stripe/checkout/gift')
|
||||
.body({price_id: MOCK_PRICES.monthlySek, country_code: 'SE'})
|
||||
.expect(HTTP_STATUS.BAD_REQUEST, APIErrorCodes.STRIPE_INVALID_PRODUCT_CONFIGURATION)
|
||||
.execute();
|
||||
expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('existing subscribers', () => {
|
||||
test('a Swedish customer billed in EUR keeps the EUR price and is not repriced to SEK', async () => {
|
||||
lookupGeoipMock.mockResolvedValue(geoipCountry('SE'));
|
||||
const account = await createTestAccount(harness);
|
||||
await createBuilder(harness, account.token)
|
||||
.post(`/test/users/${account.userId}/premium`)
|
||||
.body({
|
||||
stripe_subscription_id: EUR_SUBSCRIPTION_ID,
|
||||
premium_type: 1,
|
||||
premium_billing_cycle: 'monthly',
|
||||
premium_until: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
premium_will_cancel: false,
|
||||
})
|
||||
.execute();
|
||||
const price = await createBuilder<CurrentSubscriptionPriceResponse>(harness, account.token)
|
||||
.get('/premium/current-subscription-price')
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.execute();
|
||||
expect(price).toMatchObject({
|
||||
price_id: MOCK_PRICES.monthlyEur,
|
||||
currency: 'EUR',
|
||||
billing_cycle: 'monthly',
|
||||
is_grandfathered: false,
|
||||
list_price_id: MOCK_PRICES.monthlyEur,
|
||||
});
|
||||
expect(stripeHandlers.spies.updatedSubscriptions).toHaveLength(0);
|
||||
expect(stripeHandlers.spies.createdSubscriptionSchedules).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -801,13 +801,19 @@ export function createStripeApiHandlers(config: StripeApiMockConfig = {}): Strip
|
||||
? 'eur'
|
||||
: normalizedPriceId.includes('brl')
|
||||
? 'brl'
|
||||
: normalizedPriceId.includes('inr')
|
||||
? 'inr'
|
||||
: normalizedPriceId.includes('pln')
|
||||
? 'pln'
|
||||
: normalizedPriceId.includes('try')
|
||||
? 'try'
|
||||
: 'usd',
|
||||
: normalizedPriceId.includes('dkk')
|
||||
? 'dkk'
|
||||
: normalizedPriceId.includes('inr')
|
||||
? 'inr'
|
||||
: normalizedPriceId.includes('nok')
|
||||
? 'nok'
|
||||
: normalizedPriceId.includes('pln')
|
||||
? 'pln'
|
||||
: normalizedPriceId.includes('sek')
|
||||
? 'sek'
|
||||
: normalizedPriceId.includes('try')
|
||||
? 'try'
|
||||
: 'usd',
|
||||
interval: normalizedPriceId.includes('year') ? 'year' : 'month',
|
||||
};
|
||||
}
|
||||
@@ -1724,13 +1730,19 @@ export function createStripeApiHandlers(config: StripeApiMockConfig = {}): Strip
|
||||
? 'eur'
|
||||
: normalizedPriceId.includes('brl')
|
||||
? 'brl'
|
||||
: normalizedPriceId.includes('inr')
|
||||
? 'inr'
|
||||
: normalizedPriceId.includes('pln')
|
||||
? 'pln'
|
||||
: normalizedPriceId.includes('try')
|
||||
? 'try'
|
||||
: 'usd'),
|
||||
: normalizedPriceId.includes('dkk')
|
||||
? 'dkk'
|
||||
: normalizedPriceId.includes('inr')
|
||||
? 'inr'
|
||||
: normalizedPriceId.includes('nok')
|
||||
? 'nok'
|
||||
: normalizedPriceId.includes('pln')
|
||||
? 'pln'
|
||||
: normalizedPriceId.includes('sek')
|
||||
? 'sek'
|
||||
: normalizedPriceId.includes('try')
|
||||
? 'try'
|
||||
: 'usd'),
|
||||
unit_amount: overrides?.unit_amount ?? (normalizedPriceId.includes('year') ? 4999 : 499),
|
||||
type: 'recurring',
|
||||
recurring: {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import {isEuEeaCountryCode} from '@fluxer/constants/src/EuropeanEconomicArea';
|
||||
|
||||
export type Currency = 'USD' | 'EUR' | 'BRL' | 'INR' | 'PLN' | 'TRY';
|
||||
export type Currency = 'USD' | 'EUR' | 'BRL' | 'DKK' | 'INR' | 'NOK' | 'PLN' | 'SEK' | 'TRY';
|
||||
|
||||
export function getCurrency(countryCode: string | null | undefined): Currency {
|
||||
return getCurrencyPreferences(countryCode)[0];
|
||||
@@ -16,12 +16,21 @@ export function getCurrencyPreferences(countryCode: string | null | undefined):
|
||||
if (upperCode === 'BR') {
|
||||
return ['BRL', 'USD', 'EUR'];
|
||||
}
|
||||
if (upperCode === 'DK') {
|
||||
return ['DKK', 'EUR', 'USD'];
|
||||
}
|
||||
if (upperCode === 'IN') {
|
||||
return ['INR', 'USD', 'EUR'];
|
||||
}
|
||||
if (upperCode === 'NO') {
|
||||
return ['NOK', 'EUR', 'USD'];
|
||||
}
|
||||
if (upperCode === 'PL') {
|
||||
return ['PLN', 'EUR', 'USD'];
|
||||
}
|
||||
if (upperCode === 'SE') {
|
||||
return ['SEK', 'EUR', 'USD'];
|
||||
}
|
||||
if (upperCode === 'TR') {
|
||||
return ['TRY', 'USD', 'EUR'];
|
||||
}
|
||||
@@ -31,9 +40,10 @@ export function getCurrencyPreferences(countryCode: string | null | undefined):
|
||||
return ['USD', 'EUR'];
|
||||
}
|
||||
|
||||
const GIFT_ELIGIBLE_LOCALIZED_CURRENCIES = new Set<Currency>(['DKK', 'NOK', 'SEK']);
|
||||
|
||||
export function getGiftCurrencyPreferences(countryCode: string | null | undefined): Array<Currency> {
|
||||
if (countryCode && isEuEeaCountryCode(countryCode.toUpperCase())) {
|
||||
return ['EUR', 'USD'];
|
||||
}
|
||||
return ['USD', 'EUR'];
|
||||
return getCurrencyPreferences(countryCode).filter(
|
||||
(currency) => currency === 'USD' || currency === 'EUR' || GIFT_ELIGIBLE_LOCALIZED_CURRENCIES.has(currency),
|
||||
);
|
||||
}
|
||||
@@ -55,11 +55,11 @@ describe('getCurrency', () => {
|
||||
it('returns EUR for Finland', () => {
|
||||
expect(getCurrency('FI')).toBe('EUR');
|
||||
});
|
||||
it('returns EUR for Sweden', () => {
|
||||
expect(getCurrency('SE')).toBe('EUR');
|
||||
it('returns SEK for Sweden', () => {
|
||||
expect(getCurrency('SE')).toBe('SEK');
|
||||
});
|
||||
it('returns EUR for Denmark', () => {
|
||||
expect(getCurrency('DK')).toBe('EUR');
|
||||
it('returns DKK for Denmark', () => {
|
||||
expect(getCurrency('DK')).toBe('DKK');
|
||||
});
|
||||
it('returns PLN for Poland', () => {
|
||||
expect(getCurrency('PL')).toBe('PLN');
|
||||
@@ -76,8 +76,8 @@ describe('getCurrency', () => {
|
||||
it('returns EUR for Romania', () => {
|
||||
expect(getCurrency('RO')).toBe('EUR');
|
||||
});
|
||||
it('returns EUR for Norway (EEA but not EU)', () => {
|
||||
expect(getCurrency('NO')).toBe('EUR');
|
||||
it('returns NOK for Norway (EEA but not EU)', () => {
|
||||
expect(getCurrency('NO')).toBe('NOK');
|
||||
});
|
||||
it('returns EUR for Iceland (EEA but not EU)', () => {
|
||||
expect(getCurrency('IS')).toBe('EUR');
|
||||
@@ -128,7 +128,6 @@ describe('getCurrency', () => {
|
||||
'HR',
|
||||
'CY',
|
||||
'CZ',
|
||||
'DK',
|
||||
'EE',
|
||||
'FI',
|
||||
'FR',
|
||||
@@ -147,10 +146,8 @@ describe('getCurrency', () => {
|
||||
'SK',
|
||||
'SI',
|
||||
'ES',
|
||||
'SE',
|
||||
'IS',
|
||||
'LI',
|
||||
'NO',
|
||||
];
|
||||
for (const country of eeaCountries) {
|
||||
it(`returns EUR for ${country}`, () => {
|
||||
@@ -160,16 +157,30 @@ describe('getCurrency', () => {
|
||||
it('uses local currency for Poland', () => {
|
||||
expect(getCurrency('PL')).toBe('PLN');
|
||||
});
|
||||
it('uses local currency for Sweden', () => {
|
||||
expect(getCurrency('SE')).toBe('SEK');
|
||||
});
|
||||
it('uses local currency for Denmark', () => {
|
||||
expect(getCurrency('DK')).toBe('DKK');
|
||||
});
|
||||
it('uses local currency for Norway', () => {
|
||||
expect(getCurrency('NO')).toBe('NOK');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getGiftCurrencyPreferences', () => {
|
||||
it('never offers a localized currency for a localized market', () => {
|
||||
it('never offers a localized currency that is cheaper than the base price', () => {
|
||||
for (const country of ['BR', 'IN', 'PL', 'TR']) {
|
||||
expect(getGiftCurrencyPreferences(country)).not.toContain(getCurrencyPreferences(country)[0]);
|
||||
}
|
||||
});
|
||||
it('uses EUR for EEA countries', () => {
|
||||
it('offers the localized currency where it is not cheaper than the base price', () => {
|
||||
expect(getGiftCurrencyPreferences('SE')).toEqual(['SEK', 'EUR', 'USD']);
|
||||
expect(getGiftCurrencyPreferences('DK')).toEqual(['DKK', 'EUR', 'USD']);
|
||||
expect(getGiftCurrencyPreferences('NO')).toEqual(['NOK', 'EUR', 'USD']);
|
||||
});
|
||||
it('uses EUR for other EEA countries', () => {
|
||||
expect(getGiftCurrencyPreferences('DE')).toEqual(['EUR', 'USD']);
|
||||
expect(getGiftCurrencyPreferences('PL')).toEqual(['EUR', 'USD']);
|
||||
});
|
||||
@@ -184,7 +195,7 @@ describe('getGiftCurrencyPreferences', () => {
|
||||
expect(getGiftCurrencyPreferences(undefined)).toEqual(['USD', 'EUR']);
|
||||
});
|
||||
it('is case insensitive', () => {
|
||||
expect(getGiftCurrencyPreferences('de')).toEqual(['EUR', 'USD']);
|
||||
expect(getGiftCurrencyPreferences('se')).toEqual(['SEK', 'EUR', 'USD']);
|
||||
expect(getGiftCurrencyPreferences('br')).toEqual(['USD', 'EUR']);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user