From 0421c86039e58e1cb06d407bd51bc5a2a2fbf40f Mon Sep 17 00:00:00 2001 From: Hampus Date: Fri, 11 Sep 2026 00:28:14 +0200 Subject: [PATCH] fix(api): price gifts in the base currency everywhere (#2648) --- .../StripeCheckoutCountryEnforcement.test.ts | 23 +++++++++++++++ fluxer_api/src/api/utils/CurrencyUtils.ts | 5 +++- .../src/api/utils/tests/CurrencyUtils.test.ts | 28 ++++++++++++++++++- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/fluxer_api/src/api/stripe/tests/StripeCheckoutCountryEnforcement.test.ts b/fluxer_api/src/api/stripe/tests/StripeCheckoutCountryEnforcement.test.ts index ba89bed80..38207bfc8 100644 --- a/fluxer_api/src/api/stripe/tests/StripeCheckoutCountryEnforcement.test.ts +++ b/fluxer_api/src/api/stripe/tests/StripeCheckoutCountryEnforcement.test.ts @@ -112,6 +112,29 @@ describe('StripeCheckoutCountryEnforcement', () => { expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(0); }); + test('rejects a localized gift price even from inside that market', async () => { + lookupGeoipMock.mockResolvedValue(geoipCountry('BR')); + const token = await createPurchaser(); + await createBuilder(harness, token) + .post('/stripe/checkout/gift') + .body({price_id: MOCK_PRICES.gift1MonthBrl, country_code: 'BR'}) + .expect(HTTP_STATUS.BAD_REQUEST, APIErrorCodes.STRIPE_INVALID_PRODUCT_CONFIGURATION) + .execute(); + expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(0); + }); + + test('accepts the base gift price from inside a localized market', async () => { + lookupGeoipMock.mockResolvedValue(geoipCountry('BR')); + const token = await createPurchaser(); + const response = await createBuilder<{url: string}>(harness, token) + .post('/stripe/checkout/gift') + .body({price_id: MOCK_PRICES.gift1MonthUsd, country_code: 'BR'}) + .expect(HTTP_STATUS.OK) + .execute(); + expect(response.url).toContain('checkout.stripe.com'); + expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(1); + }); + test('accepts the localized price for a request that geolocates to that market', async () => { lookupGeoipMock.mockResolvedValue(geoipCountry('BR')); const token = await createPurchaser(); diff --git a/fluxer_api/src/api/utils/CurrencyUtils.ts b/fluxer_api/src/api/utils/CurrencyUtils.ts index 27f4e8b06..3e10748b7 100644 --- a/fluxer_api/src/api/utils/CurrencyUtils.ts +++ b/fluxer_api/src/api/utils/CurrencyUtils.ts @@ -32,5 +32,8 @@ export function getCurrencyPreferences(countryCode: string | null | undefined): } export function getGiftCurrencyPreferences(countryCode: string | null | undefined): Array { - return getCurrencyPreferences(countryCode); + if (countryCode && isEuEeaCountryCode(countryCode.toUpperCase())) { + return ['EUR', 'USD']; + } + return ['USD', 'EUR']; } diff --git a/fluxer_api/src/api/utils/tests/CurrencyUtils.test.ts b/fluxer_api/src/api/utils/tests/CurrencyUtils.test.ts index eef6df46d..682a0edf4 100644 --- a/fluxer_api/src/api/utils/tests/CurrencyUtils.test.ts +++ b/fluxer_api/src/api/utils/tests/CurrencyUtils.test.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-or-later import {describe, expect, it} from 'vitest'; -import {getCurrency} from '../CurrencyUtils'; +import {getCurrency, getCurrencyPreferences, getGiftCurrencyPreferences} from '../CurrencyUtils'; describe('getCurrency', () => { describe('returns USD for non-EEA countries', () => { @@ -162,3 +162,29 @@ describe('getCurrency', () => { }); }); }); + +describe('getGiftCurrencyPreferences', () => { + it('never offers a localized currency for a localized market', () => { + for (const country of ['BR', 'IN', 'PL', 'TR']) { + expect(getGiftCurrencyPreferences(country)).not.toContain(getCurrencyPreferences(country)[0]); + } + }); + it('uses EUR for EEA countries', () => { + expect(getGiftCurrencyPreferences('DE')).toEqual(['EUR', 'USD']); + expect(getGiftCurrencyPreferences('PL')).toEqual(['EUR', 'USD']); + }); + it('uses USD everywhere else', () => { + expect(getGiftCurrencyPreferences('BR')).toEqual(['USD', 'EUR']); + expect(getGiftCurrencyPreferences('IN')).toEqual(['USD', 'EUR']); + expect(getGiftCurrencyPreferences('TR')).toEqual(['USD', 'EUR']); + expect(getGiftCurrencyPreferences('US')).toEqual(['USD', 'EUR']); + }); + it('uses USD when the country is unknown', () => { + expect(getGiftCurrencyPreferences(null)).toEqual(['USD', 'EUR']); + expect(getGiftCurrencyPreferences(undefined)).toEqual(['USD', 'EUR']); + }); + it('is case insensitive', () => { + expect(getGiftCurrencyPreferences('de')).toEqual(['EUR', 'USD']); + expect(getGiftCurrencyPreferences('br')).toEqual(['USD', 'EUR']); + }); +});