diff --git a/platforms/react-native/sample/src/App.tsx b/platforms/react-native/sample/src/App.tsx index 2a309ec3..221064c9 100644 --- a/platforms/react-native/sample/src/App.tsx +++ b/platforms/react-native/sample/src/App.tsx @@ -49,6 +49,10 @@ import type {ProductVariant, ShopifyProduct} from '../@types'; import ErrorBoundary from './ErrorBoundary'; import env from 'react-native-config'; import {createDebugLogger} from './utils'; +import { + createStorefrontApiUrl, + normalizeStorefrontDomain, +} from './storefrontUrl'; import {useShopifyEventHandlers} from './hooks/useCheckoutEventHandlers'; import {useE2ECartBootstrap} from './hooks/useE2ECartBootstrap'; import {E2ETestIds} from './e2e/testIds'; @@ -59,7 +63,12 @@ function configured(value: string | undefined) { return value ? 'configured' : 'missing'; } -const storefrontApiVersion = env.API_VERSION ?? env.STOREFRONT_VERSION; +const storefrontApiVersion = env.API_VERSION ?? env.STOREFRONT_VERSION ?? ''; +const storefrontDomain = normalizeStorefrontDomain(env.STOREFRONT_DOMAIN); +const storefrontApiUrl = createStorefrontApiUrl( + env.STOREFRONT_DOMAIN, + storefrontApiVersion, +); console.groupCollapsed('ENV'); log('STOREFRONT_DOMAIN:', configured(env.STOREFRONT_DOMAIN)); @@ -101,7 +110,7 @@ const styles = StyleSheet.create({ export const cache = new InMemoryCache(); const client = new ApolloClient({ - uri: `https://${env.STOREFRONT_DOMAIN}/api/${storefrontApiVersion}/graphql.json`, + uri: storefrontApiUrl, cache, headers: { 'Content-Type': 'application/json', @@ -408,7 +417,7 @@ function AppWithCheckoutKit({children}: PropsWithChildren) { }, }, acceleratedCheckouts: { - storefrontDomain: env.STOREFRONT_DOMAIN!, + storefrontDomain, storefrontAccessToken: env.STOREFRONT_ACCESS_TOKEN!, /** * We're reading the hardcoded customer email and phone number from the diff --git a/platforms/react-native/sample/src/__tests__/storefrontUrl.test.ts b/platforms/react-native/sample/src/__tests__/storefrontUrl.test.ts new file mode 100644 index 00000000..993c183b --- /dev/null +++ b/platforms/react-native/sample/src/__tests__/storefrontUrl.test.ts @@ -0,0 +1,40 @@ +import { + createStorefrontApiUrl, + normalizeStorefrontDomain, +} from '../storefrontUrl'; + +describe('storefrontUrl', () => { + describe('normalizeStorefrontDomain', () => { + it('keeps a bare storefront domain unchanged', () => { + expect(normalizeStorefrontDomain('shop.example.myshopify.com')).toBe( + 'shop.example.myshopify.com', + ); + }); + + it('removes an HTTPS scheme from the storefront domain', () => { + expect(normalizeStorefrontDomain('https://shop.example.myshopify.com')).toBe( + 'shop.example.myshopify.com', + ); + }); + + it('removes trailing slashes from the storefront domain', () => { + expect(normalizeStorefrontDomain('https://shop.example.myshopify.com/')).toBe( + 'shop.example.myshopify.com', + ); + }); + }); + + describe('createStorefrontApiUrl', () => { + it('adds HTTPS to a bare storefront domain', () => { + expect( + createStorefrontApiUrl('shop.example.myshopify.com', '2026-04'), + ).toBe('https://shop.example.myshopify.com/api/2026-04/graphql.json'); + }); + + it('does not add HTTPS when the storefront domain already includes it', () => { + expect( + createStorefrontApiUrl('https://shop.example.myshopify.com', '2026-04'), + ).toBe('https://shop.example.myshopify.com/api/2026-04/graphql.json'); + }); + }); +}); diff --git a/platforms/react-native/sample/src/storefrontUrl.ts b/platforms/react-native/sample/src/storefrontUrl.ts new file mode 100644 index 00000000..c82011cc --- /dev/null +++ b/platforms/react-native/sample/src/storefrontUrl.ts @@ -0,0 +1,18 @@ +export function normalizeStorefrontDomain( + storefrontDomain: string | undefined, +): string { + const domainWithoutScheme = (storefrontDomain ?? '') + .trim() + .replace(/^https?:\/\//i, ''); + + return domainWithoutScheme.split('/')[0] ?? ''; +} + +export function createStorefrontApiUrl( + storefrontDomain: string | undefined, + apiVersion: string, +): string { + return `https://${normalizeStorefrontDomain( + storefrontDomain, + )}/api/${apiVersion}/graphql.json`; +} diff --git a/scripts/setup_storefront_env b/scripts/setup_storefront_env index 79729d7f..eab0f360 100755 --- a/scripts/setup_storefront_env +++ b/scripts/setup_storefront_env @@ -148,6 +148,13 @@ is_placeholder_value() { [[ "$value" == "your-public-storefront-access-token" ]] } +normalize_storefront_domain() { + local value + value="$(strip_outer_quotes "$1")" + value="$(printf '%s' "$value" | sed -E 's#^[Hh][Tt][Tt][Pp][Ss]?://##; s#/.*$##')" + printf '%s' "$value" +} + env_fallback() { local key="$1" printf '%s' "${!key:-}" @@ -389,6 +396,7 @@ load_values() { "$(read_env_value STOREFRONT_DOMAIN "$REACT_NATIVE_ENV")" \ "$(read_env_value STOREFRONT_DOMAIN "$SWIFT_DEMO_XCCONFIG")" \ "$(read_env_value STOREFRONT_DOMAIN "$SWIFT_ACCELERATED_XCCONFIG")")" + STOREFRONT_DOMAIN_VALUE="$(normalize_storefront_domain "$STOREFRONT_DOMAIN_VALUE")" STOREFRONT_ACCESS_TOKEN_VALUE="$(first_config_value \ "$(required_config_value STOREFRONT_ACCESS_TOKEN)" \ diff --git a/scripts/test_setup_storefront_env b/scripts/test_setup_storefront_env index 0dc6b7aa..b1c95014 100755 --- a/scripts/test_setup_storefront_env +++ b/scripts/test_setup_storefront_env @@ -135,12 +135,16 @@ test_required_values_only() { fixture="$(make_fixture)" output="$fixture/output.log" - STOREFRONT_DOMAIN=synthetic-store.example.myshopify.com \ + STOREFRONT_DOMAIN=https://synthetic-store.example.myshopify.com/ \ STOREFRONT_ACCESS_TOKEN=synthetic-token \ "$fixture/scripts/setup_storefront_env" --skip-optional-prompts >"$output" 2>&1 assert_output_is_sanitized "$output" assert_file_exists "$fixture/.env" + assert_contains "$fixture/.env" "STOREFRONT_DOMAIN=synthetic-store.example.myshopify.com" + assert_contains "$fixture/platforms/android/samples/CheckoutKitAndroidDemo/.env" "STOREFRONT_DOMAIN=synthetic-store.example.myshopify.com" + assert_contains "$fixture/platforms/react-native/sample/.env" "STOREFRONT_DOMAIN=\"synthetic-store.example.myshopify.com\"" + assert_contains "$fixture/platforms/swift/Samples/CheckoutKitSwiftDemo/Storefront.xcconfig" "STOREFRONT_DOMAIN = synthetic-store.example.myshopify.com" assert_contains "$fixture/.env" "STOREFRONT_MERCHANT_IDENTIFIER=" assert_contains "$fixture/.env" "CUSTOMER_ACCOUNT_API_CLIENT_ID=" assert_contains "$fixture/.env" "CUSTOMER_ACCOUNT_API_SHOP_ID="