diff --git a/packages/core/src/cart/cart-selector.ts b/packages/core/src/cart/cart-selector.ts index 442f620df8..faffd527a3 100644 --- a/packages/core/src/cart/cart-selector.ts +++ b/packages/core/src/cart/cart-selector.ts @@ -12,6 +12,7 @@ export default interface CartSelector { getCartOrThrow(): Cart; getLoadError(): Error | undefined; isLoading(): boolean; + getLocale(): string | undefined; } export type CartSelectorFactory = (state: CartState) => CartSelector; @@ -36,12 +37,18 @@ export function createCartSelectorFactory() { (status) => () => status, ); + const getLocale = createSelector( + (state: CartState) => state.data?.locale, + (data) => () => data, + ); + return memoizeOne((state: CartState = DEFAULT_STATE): CartSelector => { return { getCart: getCart(state), getCartOrThrow: getCartOrThrow(state), getLoadError: getLoadError(state), isLoading: isLoading(state), + getLocale: getLocale(state), }; }); } diff --git a/packages/core/src/cart/cart.ts b/packages/core/src/cart/cart.ts index 6b578c44c8..4d2274b02d 100644 --- a/packages/core/src/cart/cart.ts +++ b/packages/core/src/cart/cart.ts @@ -35,4 +35,5 @@ export default interface Cart { createdTime: string; updatedTime: string; source?: CartSource; + locale: string; } diff --git a/packages/core/src/cart/carts.mock.ts b/packages/core/src/cart/carts.mock.ts index a6bee962f7..ed4b913a6f 100644 --- a/packages/core/src/cart/carts.mock.ts +++ b/packages/core/src/cart/carts.mock.ts @@ -26,6 +26,7 @@ export function getCart(): Cart { }, createdTime: '2018-03-06T04:41:49+00:00', updatedTime: '2018-03-07T03:44:51+00:00', + locale: 'en', }; } diff --git a/packages/core/src/payment-integration/create-payment-integration-selectors.ts b/packages/core/src/payment-integration/create-payment-integration-selectors.ts index 5a1711fcb1..f1e4be23e4 100644 --- a/packages/core/src/payment-integration/create-payment-integration-selectors.ts +++ b/packages/core/src/payment-integration/create-payment-integration-selectors.ts @@ -5,7 +5,7 @@ import { cloneResult as clone } from '../common/utility'; export default function createPaymentIntegrationSelectors({ billingAddress: { getBillingAddress, getBillingAddressOrThrow }, - cart: { getCart, getCartOrThrow }, + cart: { getCart, getCartOrThrow, getLocale: getCartLocale }, checkout: { getCheckout, getCheckoutOrThrow, getOutstandingBalance }, config: { getContextConfig, @@ -50,6 +50,7 @@ export default function createPaymentIntegrationSelectors({ return { getHost: clone(getHost), getLocale: clone(getLocale), + getCartLocale: clone(getCartLocale), getBillingAddress: clone(getBillingAddress), getBillingAddressOrThrow: clone(getBillingAddressOrThrow), getCart: clone(getCart), diff --git a/packages/core/src/shipping/strategies/stripe-upe/stripe-upe-shipping-strategy.spec.ts b/packages/core/src/shipping/strategies/stripe-upe/stripe-upe-shipping-strategy.spec.ts index 403f764ab2..410c02d1fc 100644 --- a/packages/core/src/shipping/strategies/stripe-upe/stripe-upe-shipping-strategy.spec.ts +++ b/packages/core/src/shipping/strategies/stripe-upe/stripe-upe-shipping-strategy.spec.ts @@ -4,6 +4,8 @@ import { createScriptLoader } from '@bigcommerce/script-loader'; import { Observable, of } from 'rxjs'; import { + STRIPE_UPE_CLIENT_API_VERSION, + STRIPE_UPE_CLIENT_BETAS, StripeClient, StripeDisplayName, StripeElement, @@ -139,6 +141,8 @@ describe('StripeUPEShippingStrategy', () => { jest.spyOn(store.getState().paymentMethods, 'getPaymentMethodOrThrow').mockReturnValue( getStripeUPE(), ); + + jest.spyOn(store.getState().cart, 'getLocale').mockReturnValue('en'); }); afterEach(() => { @@ -152,6 +156,12 @@ describe('StripeUPEShippingStrategy', () => { ); expect(stripeScriptLoader.getStripeClient).toHaveBeenCalledTimes(1); + expect(stripeScriptLoader.getStripeClient).toHaveBeenCalledWith( + paymentMethodMock.initializationData, + 'en', + STRIPE_UPE_CLIENT_BETAS, + STRIPE_UPE_CLIENT_API_VERSION, + ); expect(stripeUPEJsMock.elements).toHaveBeenCalledTimes(1); }); diff --git a/packages/core/src/shipping/strategies/stripe-upe/stripe-upe-shipping-strategy.ts b/packages/core/src/shipping/strategies/stripe-upe/stripe-upe-shipping-strategy.ts index 2d1db4b88b..ee745b2127 100644 --- a/packages/core/src/shipping/strategies/stripe-upe/stripe-upe-shipping-strategy.ts +++ b/packages/core/src/shipping/strategies/stripe-upe/stripe-upe-shipping-strategy.ts @@ -96,6 +96,7 @@ export default class StripeUPEShippingStrategy implements ShippingStrategy { this._stripeUPEClient = await this._stripeUPEScriptLoader.getStripeClient( initializationData, + state.cart.getLocale(), STRIPE_UPE_CLIENT_BETAS, STRIPE_UPE_CLIENT_API_VERSION, ); diff --git a/packages/payment-integration-api/src/cart/cart.ts b/packages/payment-integration-api/src/cart/cart.ts index 88456f3dba..0350760bb7 100644 --- a/packages/payment-integration-api/src/cart/cart.ts +++ b/packages/payment-integration-api/src/cart/cart.ts @@ -20,4 +20,5 @@ export default interface Cart { createdTime: string; updatedTime: string; source?: CartSource; + locale: string; } diff --git a/packages/payment-integration-api/src/mocks/carts.mock.ts b/packages/payment-integration-api/src/mocks/carts.mock.ts index ee5d97d7f0..4ef5a87694 100644 --- a/packages/payment-integration-api/src/mocks/carts.mock.ts +++ b/packages/payment-integration-api/src/mocks/carts.mock.ts @@ -30,6 +30,7 @@ export default function getCart(): Cart { }, createdTime: '2018-03-06T04:41:49+00:00', updatedTime: '2018-03-07T03:44:51+00:00', + locale: 'en', }; } diff --git a/packages/payment-integration-api/src/payment-integration-selectors.ts b/packages/payment-integration-api/src/payment-integration-selectors.ts index a75d417cdc..4432665000 100644 --- a/packages/payment-integration-api/src/payment-integration-selectors.ts +++ b/packages/payment-integration-api/src/payment-integration-selectors.ts @@ -17,6 +17,7 @@ import { Consignment, ShippingAddress } from './shipping'; export default interface PaymentIntegrationSelectors { getHost(): string | undefined; getLocale(): string | undefined; + getCartLocale(): string | undefined; getBillingAddress(): BillingAddress | undefined; getBillingAddressOrThrow(): BillingAddress; diff --git a/packages/payment-integrations-test-utils/src/test-utils/carts.mock.ts b/packages/payment-integrations-test-utils/src/test-utils/carts.mock.ts index 3960ea4e00..56649a1221 100644 --- a/packages/payment-integrations-test-utils/src/test-utils/carts.mock.ts +++ b/packages/payment-integrations-test-utils/src/test-utils/carts.mock.ts @@ -30,6 +30,7 @@ export default function getCart(): Cart { }, createdTime: '2018-03-06T04:41:49+00:00', updatedTime: '2018-03-07T03:44:51+00:00', + locale: 'en', }; } diff --git a/packages/payment-integrations-test-utils/src/test-utils/payment-integration-service.mock.ts b/packages/payment-integrations-test-utils/src/test-utils/payment-integration-service.mock.ts index 2760f49e3e..7c3bc7f15c 100644 --- a/packages/payment-integrations-test-utils/src/test-utils/payment-integration-service.mock.ts +++ b/packages/payment-integrations-test-utils/src/test-utils/payment-integration-service.mock.ts @@ -32,6 +32,7 @@ const state = { getCustomerOrThrow: jest.fn(() => getCustomer()), getHost: jest.fn(), getLocale: jest.fn(), + getCartLocale: jest.fn(), getOrder: jest.fn(() => getOrder()), getOrderOrThrow: jest.fn(() => getOrder()), getOrderMeta: jest.fn(() => getOrderMeta()), diff --git a/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-button-strategy.spec.ts b/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-button-strategy.spec.ts index c7d0fe271f..0b3a570273 100644 --- a/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-button-strategy.spec.ts +++ b/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-button-strategy.spec.ts @@ -135,6 +135,7 @@ describe('StripeLinkV2ButtonStrategy', () => { jest.spyOn(paymentIntegrationService.getState(), 'getPaymentMethodOrThrow').mockReturnValue( stripePaymentMethod, ); + jest.spyOn(paymentIntegrationService.getState(), 'getCartLocale').mockReturnValue('en'); jest.spyOn(stripeIntegrationService, 'isPaymentCompleted').mockReturnValue( Promise.resolve(false), ); @@ -194,10 +195,13 @@ describe('StripeLinkV2ButtonStrategy', () => { stripePaymentMethod.initializationData.captureMethod = 'automatic'; await strategy.initialize(initialiseOptions); - expect(scriptLoader.getStripeClient).toHaveBeenCalledWith({ - ...stripePaymentMethod.initializationData, - captureMethod: 'automatic', - }); + expect(scriptLoader.getStripeClient).toHaveBeenCalledWith( + { + ...stripePaymentMethod.initializationData, + captureMethod: 'automatic', + }, + 'en', + ); expect(elements.create).toHaveBeenCalledWith( 'expressCheckout', expressCheckoutOptionsMock, @@ -215,10 +219,13 @@ describe('StripeLinkV2ButtonStrategy', () => { stripePaymentMethod.initializationData.captureMethod = 'manual'; await strategy.initialize(initialiseOptions); - expect(scriptLoader.getStripeClient).toHaveBeenCalledWith({ - ...stripePaymentMethod.initializationData, - captureMethod: 'manual', - }); + expect(scriptLoader.getStripeClient).toHaveBeenCalledWith( + { + ...stripePaymentMethod.initializationData, + captureMethod: 'manual', + }, + 'en', + ); expect(elements.create).toHaveBeenCalledWith( 'expressCheckout', expressCheckoutOptionsMock, diff --git a/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-button-strategy.ts b/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-button-strategy.ts index 3bb84007a5..91e16c28c5 100644 --- a/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-button-strategy.ts +++ b/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-button-strategy.ts @@ -90,7 +90,10 @@ export default class StripeLinkV2ButtonStrategy implements CheckoutButtonStrateg const { captureMethod } = initializationData; this._captureMethod = captureMethod; - this._stripeClient = await this.scriptLoader.getStripeClient(initializationData); + this._stripeClient = await this.scriptLoader.getStripeClient( + initializationData, + state.getCartLocale(), + ); await this.paymentIntegrationService.loadDefaultCheckout(); diff --git a/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-customer-strategy.spec.ts b/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-customer-strategy.spec.ts index fe9143d576..3e5dcd3b1b 100644 --- a/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-customer-strategy.spec.ts +++ b/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-customer-strategy.spec.ts @@ -135,6 +135,7 @@ describe('StripeLinkV2CustomerStrategy', () => { jest.spyOn(paymentIntegrationService.getState(), 'getPaymentMethodOrThrow').mockReturnValue( stripePaymentMethod, ); + jest.spyOn(paymentIntegrationService.getState(), 'getCartLocale').mockReturnValue('en'); jest.spyOn(stripeIntegrationService, 'isPaymentCompleted').mockReturnValue( Promise.resolve(false), ); @@ -200,10 +201,13 @@ describe('StripeLinkV2CustomerStrategy', () => { stripePaymentMethod.initializationData.captureMethod = 'automatic'; await strategy.initialize(initialiseOptions); - expect(scriptLoader.getStripeClient).toHaveBeenCalledWith({ - ...stripePaymentMethod.initializationData, - captureMethod: 'automatic', - }); + expect(scriptLoader.getStripeClient).toHaveBeenCalledWith( + { + ...stripePaymentMethod.initializationData, + captureMethod: 'automatic', + }, + 'en', + ); expect(elements.create).toHaveBeenCalledWith( 'expressCheckout', expressCheckoutOptionsMock, @@ -221,10 +225,13 @@ describe('StripeLinkV2CustomerStrategy', () => { stripePaymentMethod.initializationData.captureMethod = 'manual'; await strategy.initialize(initialiseOptions); - expect(scriptLoader.getStripeClient).toHaveBeenCalledWith({ - ...stripePaymentMethod.initializationData, - captureMethod: 'manual', - }); + expect(scriptLoader.getStripeClient).toHaveBeenCalledWith( + { + ...stripePaymentMethod.initializationData, + captureMethod: 'manual', + }, + 'en', + ); expect(elements.create).toHaveBeenCalledWith( 'expressCheckout', expressCheckoutOptionsMock, diff --git a/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-customer-strategy.ts b/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-customer-strategy.ts index 7b5f19d3c0..4126fc3dfa 100644 --- a/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-customer-strategy.ts +++ b/packages/stripe-integration/src/stripe-ocs/stripe-link-v2-customer-strategy.ts @@ -93,7 +93,10 @@ export default class StripeLinkV2CustomerStrategy implements CustomerStrategy { const { captureMethod } = initializationData; this._captureMethod = captureMethod; - this._stripeClient = await this.scriptLoader.getStripeClient(initializationData); + this._stripeClient = await this.scriptLoader.getStripeClient( + initializationData, + state.getCartLocale(), + ); await this._mountExpressCheckoutElement( methodId, diff --git a/packages/stripe-integration/src/stripe-ocs/stripe-ocs-payment-strategy.spec.ts b/packages/stripe-integration/src/stripe-ocs/stripe-ocs-payment-strategy.spec.ts index 72c286a14d..8b1af39422 100644 --- a/packages/stripe-integration/src/stripe-ocs/stripe-ocs-payment-strategy.spec.ts +++ b/packages/stripe-integration/src/stripe-ocs/stripe-ocs-payment-strategy.spec.ts @@ -72,6 +72,7 @@ describe('StripeOCSPaymentStrategy', () => { jest.spyOn(paymentIntegrationService.getState(), 'getPaymentMethodOrThrow').mockReturnValue( getStripeOCSMock(), ); + jest.spyOn(paymentIntegrationService.getState(), 'getCartLocale').mockReturnValue('en'); jest.spyOn(stripeScriptLoader, 'getElements').mockReturnValue( Promise.resolve(stripeUPEJsMock.elements({})), ); @@ -386,6 +387,10 @@ describe('StripeOCSPaymentStrategy', () => { await stripeOCSPaymentStrategy.initialize(stripeOptions); expect(stripeScriptLoader.getStripeClient).toHaveBeenCalledTimes(1); + expect(stripeScriptLoader.getStripeClient).toHaveBeenCalledWith( + getStripeOCSMock().initializationData, + 'en', + ); }); it('should enable Link by initialization data option', async () => { diff --git a/packages/stripe-integration/src/stripe-ocs/stripe-ocs-payment-strategy.ts b/packages/stripe-integration/src/stripe-ocs/stripe-ocs-payment-strategy.ts index 37340d51b6..f597a407be 100644 --- a/packages/stripe-integration/src/stripe-ocs/stripe-ocs-payment-strategy.ts +++ b/packages/stripe-integration/src/stripe-ocs/stripe-ocs-payment-strategy.ts @@ -242,7 +242,9 @@ export default class StripeOCSPaymentStrategy implements PaymentStrategy { return this.stripeClient; } - return this.scriptLoader.getStripeClient(initializationData); + const state = this.paymentIntegrationService.getState(); + + return this.scriptLoader.getStripeClient(initializationData, state.getCartLocale()); } private _collapseStripeElement() { diff --git a/packages/stripe-integration/src/stripe-upe/stripe-upe-customer-strategy.spec.ts b/packages/stripe-integration/src/stripe-upe/stripe-upe-customer-strategy.spec.ts index 9505978e82..e7e45b696e 100644 --- a/packages/stripe-integration/src/stripe-upe/stripe-upe-customer-strategy.spec.ts +++ b/packages/stripe-integration/src/stripe-upe/stripe-upe-customer-strategy.spec.ts @@ -14,6 +14,8 @@ import { PaymentIntegrationServiceMock, } from '@bigcommerce/checkout-sdk/payment-integrations-test-utils'; import { + STRIPE_UPE_CLIENT_API_VERSION, + STRIPE_UPE_CLIENT_BETAS, StripeClient, StripeCustomerEvent, StripeElement, @@ -58,6 +60,7 @@ describe('StripeUpeCustomerStrategy', () => { jest.spyOn(paymentIntegrationService.getState(), 'getPaymentMethod').mockReturnValue( paymentMethodMock, ); + jest.spyOn(paymentIntegrationService.getState(), 'getCartLocale').mockReturnValue('en'); jest.spyOn(paymentIntegrationService, 'loadPaymentMethod').mockResolvedValue( paymentIntegrationService.getState(), ); @@ -112,6 +115,12 @@ describe('StripeUpeCustomerStrategy', () => { await strategy.initialize(customerInitialization); expect(stripeScriptLoader.getStripeClient).toHaveBeenCalledTimes(1); + expect(stripeScriptLoader.getStripeClient).toHaveBeenCalledWith( + paymentMethodMock.initializationData, + 'en', + STRIPE_UPE_CLIENT_BETAS, + STRIPE_UPE_CLIENT_API_VERSION, + ); expect(stripeUPEJsMock.elements).toHaveBeenCalledTimes(1); }); diff --git a/packages/stripe-integration/src/stripe-upe/stripe-upe-customer-strategy.ts b/packages/stripe-integration/src/stripe-upe/stripe-upe-customer-strategy.ts index c15aa47cc7..5fa01370a6 100644 --- a/packages/stripe-integration/src/stripe-upe/stripe-upe-customer-strategy.ts +++ b/packages/stripe-integration/src/stripe-upe/stripe-upe-customer-strategy.ts @@ -106,6 +106,7 @@ export default class StripeUPECustomerStrategy implements CustomerStrategy { stripeUPEClient = await this.scriptLoader.getStripeClient( paymentMethod.initializationData, + state.getCartLocale(), STRIPE_UPE_CLIENT_BETAS, STRIPE_UPE_CLIENT_API_VERSION, ); diff --git a/packages/stripe-integration/src/stripe-upe/stripe-upe-payment-strategy.spec.ts b/packages/stripe-integration/src/stripe-upe/stripe-upe-payment-strategy.spec.ts index a5f93550fb..95af22606e 100644 --- a/packages/stripe-integration/src/stripe-upe/stripe-upe-payment-strategy.spec.ts +++ b/packages/stripe-integration/src/stripe-upe/stripe-upe-payment-strategy.spec.ts @@ -30,6 +30,8 @@ import { getRetrievePaymentIntentResponseWithError, getStripeIntegrationServiceMock, getStripeJsMock, + STRIPE_UPE_CLIENT_API_VERSION, + STRIPE_UPE_CLIENT_BETAS, StripeClient, StripeElementsOptions, StripeElementType, @@ -80,6 +82,7 @@ describe('StripeUPEPaymentStrategy', () => { jest.spyOn(paymentIntegrationService.getState(), 'getCheckoutOrThrow').mockReturnValue( checkoutMock, ); + jest.spyOn(paymentIntegrationService.getState(), 'getCartLocale').mockReturnValue('en'); jest.spyOn(paymentIntegrationService, 'updateBillingAddress').mockImplementation(jest.fn()); @@ -192,6 +195,12 @@ describe('StripeUPEPaymentStrategy', () => { await strategy.initialize(options); expect(stripeScriptLoader.getStripeClient).toHaveBeenCalledTimes(1); + expect(stripeScriptLoader.getStripeClient).toHaveBeenCalledWith( + getStripeUPEMock().initializationData, + 'en', + STRIPE_UPE_CLIENT_BETAS, + STRIPE_UPE_CLIENT_API_VERSION, + ); expect(stripeUPEJsMock.elements).toHaveBeenNthCalledWith(1, { locale: 'en', diff --git a/packages/stripe-integration/src/stripe-upe/stripe-upe-payment-strategy.ts b/packages/stripe-integration/src/stripe-upe/stripe-upe-payment-strategy.ts index 0e720bf121..14d70290cf 100644 --- a/packages/stripe-integration/src/stripe-upe/stripe-upe-payment-strategy.ts +++ b/packages/stripe-integration/src/stripe-upe/stripe-upe-payment-strategy.ts @@ -449,8 +449,11 @@ export default class StripeUPEPaymentStrategy implements PaymentStrategy { return this._stripeUPEClient; } + const state = this.paymentIntegrationService.getState(); + return this.scriptLoader.getStripeClient( initializationData, + state.getCartLocale(), STRIPE_UPE_CLIENT_BETAS, STRIPE_UPE_CLIENT_API_VERSION, ); diff --git a/packages/stripe-utils/src/index.ts b/packages/stripe-utils/src/index.ts index a0d2aa69fe..4fd9b2315b 100644 --- a/packages/stripe-utils/src/index.ts +++ b/packages/stripe-utils/src/index.ts @@ -28,6 +28,7 @@ export { StripeEvent, StripeDisplayName, StripeHostWindow, + StripeCustomerEvent, } from './stripe'; export { getStripeJsMock, diff --git a/packages/stripe-utils/src/stripe-script-loader.spec.ts b/packages/stripe-utils/src/stripe-script-loader.spec.ts index 50363a5bac..7470fef5f4 100644 --- a/packages/stripe-utils/src/stripe-script-loader.spec.ts +++ b/packages/stripe-utils/src/stripe-script-loader.spec.ts @@ -87,6 +87,7 @@ describe('StripePayScriptLoader', () => { it('get stripe client with all initialization data', async () => { await stripeUPEScriptLoader.getStripeClient( defaultInitializationData, + 'en', defaultBetas, defaultApiVersion, ); @@ -95,6 +96,7 @@ describe('StripePayScriptLoader', () => { betas: defaultBetas, stripeAccount: 'STRIPE_CONNECTED_ACCOUNT', apiVersion: defaultApiVersion, + locale: 'en', }); }); diff --git a/packages/stripe-utils/src/stripe-script-loader.ts b/packages/stripe-utils/src/stripe-script-loader.ts index 8b9cec7c65..a11ae4193b 100644 --- a/packages/stripe-utils/src/stripe-script-loader.ts +++ b/packages/stripe-utils/src/stripe-script-loader.ts @@ -18,6 +18,7 @@ export default class StripeScriptLoader { async getStripeClient( initializationData: StripeInitializationData, + locale?: string, betas?: string[], apiVersion?: string, ): Promise { @@ -29,6 +30,7 @@ export default class StripeScriptLoader { const { stripePublishableKey, stripeConnectedAccount } = initializationData; const options = { ...(stripeConnectedAccount ? { stripeAccount: stripeConnectedAccount } : {}), + ...(locale ? { locale } : {}), ...(betas ? { betas } : {}), ...(apiVersion ? { apiVersion } : {}), };