From 924e7baa189b9ae8397d3a2b1c16c579ec9ee4e8 Mon Sep 17 00:00:00 2001 From: rvishwanathbhat Date: Fri, 23 Jan 2026 08:33:13 -0800 Subject: [PATCH 1/6] W-20975340: Display SPM at checkout --- .../app/hooks/use-current-customer.js | 10 ++- .../checkout/partials/sf-payments-sheet.jsx | 30 ++++++-- .../app/utils/sf-payments-utils.js | 76 +++++++++++++++++++ 3 files changed, 110 insertions(+), 6 deletions(-) diff --git a/packages/template-retail-react-app/app/hooks/use-current-customer.js b/packages/template-retail-react-app/app/hooks/use-current-customer.js index a3b1ef7e2b..78be4ea522 100644 --- a/packages/template-retail-react-app/app/hooks/use-current-customer.js +++ b/packages/template-retail-react-app/app/hooks/use-current-customer.js @@ -14,7 +14,15 @@ import {useCustomer, useCustomerId, useCustomerType} from '@salesforce/commerce- export const useCurrentCustomer = () => { const customerId = useCustomerId() const {isRegistered, isGuest, customerType} = useCustomerType() - const query = useCustomer({parameters: {customerId}}, {enabled: !!customerId && isRegistered}) + const query = useCustomer( + { + parameters: { + customerId, + expand: ['paymentmethodreferences'] + } + }, + {enabled: !!customerId && isRegistered} + ) const value = { ...query, data: { diff --git a/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.jsx b/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.jsx index 3cfb9ce925..2c84f3d6c3 100644 --- a/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.jsx +++ b/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.jsx @@ -48,6 +48,7 @@ import { buildTheme, getSFPaymentsInstrument, createPaymentInstrumentBody, + transformPaymentMethodReferences, getClientSecret } from '@salesforce/retail-react-app/app/utils/sf-payments-utils' import logger from '@salesforce/retail-react-app/app/utils/logger-instance' @@ -486,18 +487,35 @@ const SFPaymentsSheet = forwardRef((props, ref) => { confirmPayment })) + const savedPaymentMethods = useMemo(() => { + if (!customer?.paymentMethodReferences || !paymentConfig?.paymentMethodSetAccounts) { + return [] + } + return transformPaymentMethodReferences( + customer.paymentMethodReferences, + paymentConfig.paymentMethodSetAccounts + ) + }, [customer?.paymentMethodReferences, paymentConfig?.paymentMethodSetAccounts]) + useEffect(() => { if (sfp && metadata && containerElementRef.current && paymentConfig) { + const paymentMethodSetAccounts = (paymentConfig.paymentMethodSetAccounts || []).map( + (account) => ({ + ...account, + gatewayId: account.gatewayId || account.accountId + }) + ) + const paymentMethodSet = { paymentMethods: paymentConfig.paymentMethods, - paymentMethodSetAccounts: paymentConfig.paymentMethodSetAccounts + paymentMethodSetAccounts: paymentMethodSetAccounts } config.current = { theme: buildTheme(), actions: { createIntent: createPaymentInstrument, - onClick: () => {} // No-op: return empty function since its not applicable and SDK proceeds immediately + onClick: () => {} }, options: { useManualCapture: !cardCaptureAutomatic, @@ -505,8 +523,8 @@ const SFPaymentsSheet = forwardRef((props, ref) => { showSaveForFutureUsageCheckbox: !!( customer?.isRegistered && customer?.customerId ), - // Suppress "Make payment method default" checkbox since we don't support default SPM yet - showSaveAsDefaultCheckbox: false + showSaveAsDefaultCheckbox: false, + savedPaymentMethods: savedPaymentMethods } } @@ -549,7 +567,9 @@ const SFPaymentsSheet = forwardRef((props, ref) => { containerElementRef.current, paymentConfig, cardCaptureAutomatic, - customer?.isRegistered + customer?.isRegistered, + customer?.customerId, + savedPaymentMethods ]) useEffect(() => { diff --git a/packages/template-retail-react-app/app/utils/sf-payments-utils.js b/packages/template-retail-react-app/app/utils/sf-payments-utils.js index 7aacbcdb89..15165752da 100644 --- a/packages/template-retail-react-app/app/utils/sf-payments-utils.js +++ b/packages/template-retail-react-app/app/utils/sf-payments-utils.js @@ -278,6 +278,82 @@ export const createPaymentInstrumentBody = ({ } } +/** + * Transforms payment method references from API format to SF Payments SDK format. + * @param {Array} paymentMethodReferences - Array of payment method references + * @param {Array} paymentMethodSetAccounts - Array of payment method set accounts + * @returns {Array} Transformed payment method references for SF Payments SDK + */ +export const transformPaymentMethodReferences = ( + paymentMethodReferences, + paymentMethodSetAccounts = [] +) => { + if (!paymentMethodReferences || !Array.isArray(paymentMethodReferences)) { + return [] + } + + return paymentMethodReferences + .map((pmr) => { + const generateDisplayName = () => { + if (pmr.brand && pmr.last4) { + const brandName = pmr.brand.charAt(0).toUpperCase() + pmr.brand.slice(1) + return `${brandName} •••• ${pmr.last4}` + } + if (pmr.type === 'card' && pmr.last4) { + return `Card •••• ${pmr.last4}` + } + if (pmr.type === 'sepa_debit' && pmr.last4) { + return `Account ending in ${pmr.last4}` + } + return 'Saved Payment Method' + } + + // Determine gatewayId for SDK matching + if ( + !pmr.accountId || + !paymentMethodSetAccounts || + !Array.isArray(paymentMethodSetAccounts) + ) { + return null + } + + const matchingAccount = paymentMethodSetAccounts.find( + (account) => account.accountId === pmr.accountId + ) + if (!matchingAccount) { + return null + } + + const gatewayId = matchingAccount.gatewayId || matchingAccount.accountId + + if (!gatewayId || typeof gatewayId !== 'string') { + return null + } + + return { + accountId: pmr.accountId || null, + name: generateDisplayName(), + status: 'Active', + isDefault: false, + type: pmr.type || null, + accountHolderName: null, + id: pmr.id || null, + gatewayTokenId: pmr.id || null, + usageType: 'OffSession', + gatewayId: gatewayId, + gatewayCustomerId: null, + last4: pmr.last4 || null, + network: pmr.brand || null, + issuer: null, + expiryMonth: null, + expiryYear: null, + bankName: null, + savedByMerchant: false + } + }) + .filter((spm) => spm !== null) +} + /** * Returns a theme object containing CSS information for use with SF Payments components. * @param {*} options - theme override options From ca3dceb91e1837673dcfa2e1175af2ebe10ca0f4 Mon Sep 17 00:00:00 2001 From: rvishwanathbhat Date: Mon, 26 Jan 2026 08:12:34 -0800 Subject: [PATCH 2/6] W-20975340: Adding test cases --- .../partials/sf-payments-sheet.test.js | 208 +++++++++++++++++- 1 file changed, 198 insertions(+), 10 deletions(-) diff --git a/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.test.js b/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.test.js index 05993a5ee1..691d083c94 100644 --- a/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.test.js +++ b/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.test.js @@ -139,16 +139,24 @@ jest.mock('@salesforce/retail-react-app/app/hooks/use-current-basket', () => ({ useCurrentBasket: () => mockUseCurrentBasket() })) -jest.mock('@salesforce/retail-react-app/app/hooks/use-current-customer', () => ({ - useCurrentCustomer: () => ({ - data: { - customerId: 'customer123', - isGuest: false, - isRegistered: true, - email: 'test@example.com' - } - }) -})) +const mockCustomer = { + customerId: 'customer123', + isGuest: false, + isRegistered: true, + email: 'test@example.com', + paymentMethodReferences: [] +} + +let mockUseCurrentCustomer + +jest.mock('@salesforce/retail-react-app/app/hooks/use-current-customer', () => { + mockUseCurrentCustomer = jest.fn(() => ({ + data: mockCustomer + })) + return { + useCurrentCustomer: mockUseCurrentCustomer + } +}) jest.mock('@salesforce/retail-react-app/app/hooks/use-einstein', () => { return jest.fn(() => ({ @@ -995,6 +1003,186 @@ describe('SFPaymentsSheet', () => { expect(paymentIntent.setup_future_usage).toBeUndefined() }) + + test('confirmPayment sets setup_future_usage to off_session when futureUsageOffSession is true', async () => { + const ref = React.createRef() + setupConfirmPaymentMocks() + + const useShopperConfigurationModule = require('@salesforce/retail-react-app/app/hooks/use-shopper-configuration') + const originalMock = useShopperConfigurationModule.useShopperConfiguration + + useShopperConfigurationModule.useShopperConfiguration = jest.fn((configId) => { + if (configId === 'futureUsageOffSession') return true + if (configId === 'cardCaptureAutomatic') return true + if (configId === 'zoneId') return 'default' + return undefined + }) + + renderWithCheckoutContext( + + ) + + await waitFor(() => { + expect(ref.current).toBeDefined() + }) + + await waitFor(() => { + expect(mockCheckout).toHaveBeenCalled() + }) + + const paymentElement = mockCheckout.mock.calls[0][4] + + await act(async () => { + paymentElement.dispatchEvent( + new CustomEvent('sfp:paymentmethodselected', { + bubbles: true, + composed: true, + detail: { + selectedPaymentMethod: 'card', + savePaymentMethodForFutureUse: true + } + }) + ) + }) + + await ref.current.confirmPayment() + + await waitFor(() => { + expect(mockCheckoutConfirm).toHaveBeenCalled() + }) + + const confirmCall = mockCheckoutConfirm.mock.calls[0] + const paymentIntentFunction = confirmCall[0] + const paymentIntent = await paymentIntentFunction() + + expect(paymentIntent.setup_future_usage).toBe('off_session') + + useShopperConfigurationModule.useShopperConfiguration = originalMock + }) + }) + + describe('SPM (Saved Payment Methods) Display', () => { + beforeEach(() => { + jest.clearAllMocks() + mockCustomer.paymentMethodReferences = [] + mockUseCurrentCustomer.mockImplementation(() => ({ + data: {...mockCustomer} + })) + }) + + test('passes empty savedPaymentMethods to SDK when customer has no payment method references', async () => { + mockCustomer.paymentMethodReferences = [] + + renderWithCheckoutContext( + + ) + + await waitFor(() => { + expect(mockCheckout).toHaveBeenCalled() + }) + + const checkoutCall = mockCheckout.mock.calls[0] + const config = checkoutCall[2] + + expect(config.options.savedPaymentMethods).toEqual([]) + }) + + test('passes empty savedPaymentMethods to SDK when paymentMethodReferences is null', async () => { + mockCustomer.paymentMethodReferences = null + + renderWithCheckoutContext( + + ) + + await waitFor(() => { + expect(mockCheckout).toHaveBeenCalled() + }) + + const checkoutCall = mockCheckout.mock.calls[0] + const config = checkoutCall[2] + + expect(config.options.savedPaymentMethods).toEqual([]) + }) + + test('passes empty savedPaymentMethods to SDK when paymentMethodReferences is undefined', async () => { + mockCustomer.paymentMethodReferences = undefined + + renderWithCheckoutContext( + + ) + + await waitFor(() => { + expect(mockCheckout).toHaveBeenCalled() + }) + + const checkoutCall = mockCheckout.mock.calls[0] + const config = checkoutCall[2] + + expect(config.options.savedPaymentMethods).toEqual([]) + }) + + test('passes empty savedPaymentMethods to SDK when paymentMethodSetAccounts is missing', async () => { + mockCustomer.paymentMethodReferences = [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + } + ] + + jest.spyOn( + require('@salesforce/commerce-sdk-react'), + 'usePaymentConfiguration' + ).mockReturnValue({ + data: { + paymentMethods: [ + { + id: 'card', + name: 'Card', + paymentMethodType: 'card', + accountId: 'stripe-account-1' + } + ], + paymentMethodSetAccounts: null + } + }) + + renderWithCheckoutContext( + + ) + + await waitFor(() => { + expect(mockCheckout).toHaveBeenCalled() + }) + + const checkoutCall = mockCheckout.mock.calls[0] + const config = checkoutCall[2] + + expect(config.options.savedPaymentMethods).toEqual([]) + }) + }) describe('lifecycle', () => { From 9d905dec55a8fb3c6b31b7abc1ac2dd69d282078 Mon Sep 17 00:00:00 2001 From: rvishwanathbhat Date: Wed, 28 Jan 2026 06:21:30 -0800 Subject: [PATCH 3/6] Addressing comments --- .../checkout/partials/sf-payments-sheet.jsx | 79 ++-- .../partials/sf-payments-sheet.test.js | 28 +- .../app/utils/sf-payments-utils.js | 12 +- .../app/utils/sf-payments-utils.test.js | 376 ++++++++++++++++-- 4 files changed, 406 insertions(+), 89 deletions(-) diff --git a/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.jsx b/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.jsx index 2c84f3d6c3..817252a3ec 100644 --- a/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.jsx +++ b/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.jsx @@ -20,9 +20,13 @@ import { Divider } from '@salesforce/retail-react-app/app/components/shared/ui' import {useForm} from 'react-hook-form' -import {useShopperBasketsMutation} from '@salesforce/commerce-sdk-react' +import { + useShopperBasketsMutation, + useCustomer, + useCustomerId, + useCustomerType +} from '@salesforce/commerce-sdk-react' import {useCurrentBasket} from '@salesforce/retail-react-app/app/hooks/use-current-basket' -import {useCurrentCustomer} from '@salesforce/retail-react-app/app/hooks/use-current-customer' import {useCurrency} from '@salesforce/retail-react-app/app/hooks/use-currency' import {useCheckout} from '@salesforce/retail-react-app/app/pages/checkout/util/checkout-context' import {usePaymentConfiguration} from '@salesforce/commerce-sdk-react' @@ -48,8 +52,7 @@ import { buildTheme, getSFPaymentsInstrument, createPaymentInstrumentBody, - transformPaymentMethodReferences, - getClientSecret + transformPaymentMethodReferences } from '@salesforce/retail-react-app/app/utils/sf-payments-utils' import logger from '@salesforce/retail-react-app/app/utils/logger-instance' @@ -61,7 +64,25 @@ const SFPaymentsSheet = forwardRef((props, ref) => { const navigate = useNavigation() const {data: basket} = useCurrentBasket() - const {data: customer} = useCurrentCustomer() + const customerId = useCustomerId() + const {isRegistered} = useCustomerType() + const {data: customerData} = useCustomer( + { + parameters: { + customerId, + expand: ['paymentmethodreferences'] + } + }, + {enabled: !!customerId && isRegistered} + ) + // Add customerId and isRegistered to customer data for consistency with useCurrentCustomer + const customer = customerData + ? { + ...customerData, + customerId, + isRegistered + } + : null const isPickupOnly = basket?.shipments?.length > 0 && @@ -359,11 +380,20 @@ const SFPaymentsSheet = forwardRef((props, ref) => { // Track created payment intent const paymentIntent = { - client_secret: getClientSecret(orderPaymentInstrument), + client_secret: + orderPaymentInstrument?.paymentReference?.gatewayProperties?.stripe + ?.clientSecret, id: orderPaymentInstrument.paymentReference.paymentReferenceId } - if (futureUsageOffSession) { + // Read setup_future_usage from backend response, fallback to manual calculation if not available + // TODO: The fallback is temporary that's to be removed in next iteration. + const setupFutureUsage = + orderPaymentInstrument?.paymentReference?.gatewayProperties?.stripe + ?.setup_future_usage + if (setupFutureUsage) { + paymentIntent.setup_future_usage = setupFutureUsage + } else if (futureUsageOffSession) { paymentIntent.setup_future_usage = 'off_session' } else if (shouldSavePaymentMethod) { paymentIntent.setup_future_usage = 'on_session' @@ -406,22 +436,9 @@ const SFPaymentsSheet = forwardRef((props, ref) => { // Update the redirect return URL to include the related order no config.current.options.returnUrl += '?orderNo=' + updatedOrder.orderNo - // Update Elements to match Payment Intent's setup_future_usage before SDK confirms - const paymentIntentFunction = async () => { - const selectedPaymentMethod = checkoutComponent.current?.selectedPaymentMethod - if (selectedPaymentMethod?.asSavedPaymentMethodComponent) { - const spmComponent = selectedPaymentMethod.asSavedPaymentMethodComponent() - if (spmComponent) { - spmComponent.setSavePaymentMethodFuture(futureUsageOffSession) - } - } - - return paymentIntent - } - // Confirm the payment const result = await checkoutComponent.current.confirm( - paymentIntentFunction, + async () => paymentIntent, billingDetails, shippingDetails ) @@ -487,15 +504,14 @@ const SFPaymentsSheet = forwardRef((props, ref) => { confirmPayment })) - const savedPaymentMethods = useMemo(() => { - if (!customer?.paymentMethodReferences || !paymentConfig?.paymentMethodSetAccounts) { - return [] - } - return transformPaymentMethodReferences( - customer.paymentMethodReferences, - paymentConfig.paymentMethodSetAccounts - ) - }, [customer?.paymentMethodReferences, paymentConfig?.paymentMethodSetAccounts]) + const savedPaymentMethods = useMemo( + () => + transformPaymentMethodReferences( + customer?.paymentMethodReferences, + paymentConfig?.paymentMethodSetAccounts + ), + [customer?.paymentMethodReferences, paymentConfig?.paymentMethodSetAccounts] + ) useEffect(() => { if (sfp && metadata && containerElementRef.current && paymentConfig) { @@ -515,7 +531,7 @@ const SFPaymentsSheet = forwardRef((props, ref) => { theme: buildTheme(), actions: { createIntent: createPaymentInstrument, - onClick: () => {} + onClick: () => {} // No-op: return empty function since its not applicable and SDK proceeds immediately }, options: { useManualCapture: !cardCaptureAutomatic, @@ -523,6 +539,7 @@ const SFPaymentsSheet = forwardRef((props, ref) => { showSaveForFutureUsageCheckbox: !!( customer?.isRegistered && customer?.customerId ), + // Suppress "Make payment method default" checkbox since we don't support default SPM yet showSaveAsDefaultCheckbox: false, savedPaymentMethods: savedPaymentMethods } diff --git a/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.test.js b/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.test.js index 691d083c94..a4fd4302b7 100644 --- a/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.test.js +++ b/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.test.js @@ -101,7 +101,14 @@ jest.mock('@salesforce/commerce-sdk-react', () => { defaultShippingMethodId: 'DefaultShippingMethod' }, refetch: mockRefetchShippingMethods - }) + }), + useCustomerId: () => 'customer123', + useCustomerType: () => ({ + isRegistered: true, + isGuest: false, + customerType: 'registered' + }), + useCustomer: jest.fn() } }) @@ -147,16 +154,14 @@ const mockCustomer = { paymentMethodReferences: [] } -let mockUseCurrentCustomer +// Get the mocked useCustomer from commerce-sdk-react +// eslint-disable-next-line @typescript-eslint/no-var-requires +const mockUseCustomer = require('@salesforce/commerce-sdk-react').useCustomer -jest.mock('@salesforce/retail-react-app/app/hooks/use-current-customer', () => { - mockUseCurrentCustomer = jest.fn(() => ({ - data: mockCustomer - })) - return { - useCurrentCustomer: mockUseCurrentCustomer - } -}) +// Set default implementation +mockUseCustomer.mockImplementation(() => ({ + data: mockCustomer +})) jest.mock('@salesforce/retail-react-app/app/hooks/use-einstein', () => { return jest.fn(() => ({ @@ -1069,7 +1074,7 @@ describe('SFPaymentsSheet', () => { beforeEach(() => { jest.clearAllMocks() mockCustomer.paymentMethodReferences = [] - mockUseCurrentCustomer.mockImplementation(() => ({ + mockUseCustomer.mockImplementation(() => ({ data: {...mockCustomer} })) }) @@ -1182,7 +1187,6 @@ describe('SFPaymentsSheet', () => { expect(config.options.savedPaymentMethods).toEqual([]) }) - }) describe('lifecycle', () => { diff --git a/packages/template-retail-react-app/app/utils/sf-payments-utils.js b/packages/template-retail-react-app/app/utils/sf-payments-utils.js index 15165752da..3d11d90690 100644 --- a/packages/template-retail-react-app/app/utils/sf-payments-utils.js +++ b/packages/template-retail-react-app/app/utils/sf-payments-utils.js @@ -267,10 +267,6 @@ export const createPaymentInstrumentBody = ({ } } - if (gateway === PAYMENT_GATEWAYS.ADYEN && storePaymentMethod) { - paymentReferenceRequest.gateway = PAYMENT_GATEWAYS.ADYEN - } - return { paymentMethodId: 'Salesforce Payments', amount: amount, @@ -288,7 +284,7 @@ export const transformPaymentMethodReferences = ( paymentMethodReferences, paymentMethodSetAccounts = [] ) => { - if (!paymentMethodReferences || !Array.isArray(paymentMethodReferences)) { + if (!Array.isArray(paymentMethodReferences) || !Array.isArray(paymentMethodSetAccounts)) { return [] } @@ -309,11 +305,7 @@ export const transformPaymentMethodReferences = ( } // Determine gatewayId for SDK matching - if ( - !pmr.accountId || - !paymentMethodSetAccounts || - !Array.isArray(paymentMethodSetAccounts) - ) { + if (!pmr.accountId) { return null } diff --git a/packages/template-retail-react-app/app/utils/sf-payments-utils.test.js b/packages/template-retail-react-app/app/utils/sf-payments-utils.test.js index 9ed407cb59..ded92ffb82 100644 --- a/packages/template-retail-react-app/app/utils/sf-payments-utils.test.js +++ b/packages/template-retail-react-app/app/utils/sf-payments-utils.test.js @@ -16,7 +16,8 @@ import { createPaymentInstrumentBody, getClientSecret, getGatewayFromPaymentMethod, - getSetupFutureUsage + getSetupFutureUsage, + transformPaymentMethodReferences } from '@salesforce/retail-react-app/app/utils/sf-payments-utils' describe('sf-payments-utils', () => { @@ -1263,41 +1264,6 @@ describe('sf-payments-utils', () => { expect(result.paymentReferenceRequest.shippingPreference).toBeUndefined() }) - test('includes gateway for Adyen when storePaymentMethod is true', () => { - const paymentMethods = [{paymentMethodType: 'card', accountId: 'adyen_acct_123'}] - const paymentMethodSetAccounts = [{vendor: 'Adyen', accountId: 'adyen_acct_123'}] - const result = createPaymentInstrumentBody({ - amount: 100.0, - paymentMethodType: 'card', - zoneId: 'default', - shippingPreference: undefined, - storePaymentMethod: true, - futureUsageOffSession: false, - paymentMethods, - paymentMethodSetAccounts - }) - - expect(result.paymentReferenceRequest.gateway).toBe('adyen') - expect(result.paymentReferenceRequest.gatewayProperties).toBeUndefined() - }) - - test('does not include gateway for Adyen when storePaymentMethod is false', () => { - const paymentMethods = [{paymentMethodType: 'card', accountId: 'adyen_acct_123'}] - const paymentMethodSetAccounts = [{vendor: 'Adyen', accountId: 'adyen_acct_123'}] - const result = createPaymentInstrumentBody({ - amount: 100.0, - paymentMethodType: 'card', - zoneId: 'default', - shippingPreference: undefined, - storePaymentMethod: false, - futureUsageOffSession: false, - paymentMethods, - paymentMethodSetAccounts - }) - - expect(result.paymentReferenceRequest.gateway).toBeUndefined() - }) - test('does not include setupFutureUsage in POST request even when storePaymentMethod is true', () => { const paymentMethods = [{paymentMethodType: 'card', accountId: 'acct_123'}] const paymentMethodSetAccounts = [{vendor: 'Stripe', accountId: 'acct_123'}] @@ -1492,4 +1458,342 @@ describe('sf-payments-utils', () => { expect(getClientSecret({paymentReference: {}})).toBeUndefined() }) }) + + describe('transformPaymentMethodReferences', () => { + test('returns empty array when paymentMethodReferences is null', () => { + const result = transformPaymentMethodReferences(null, []) + expect(result).toEqual([]) + }) + + test('returns empty array when paymentMethodReferences is undefined', () => { + const result = transformPaymentMethodReferences(undefined, []) + expect(result).toEqual([]) + }) + + test('returns empty array when paymentMethodReferences is not an array', () => { + const result = transformPaymentMethodReferences({}, []) + expect(result).toEqual([]) + }) + + test('transforms payment method reference with brand and last4', () => { + const paymentMethodReferences = [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + } + ] + const paymentMethodSetAccounts = [ + { + accountId: 'stripe-account-1', + gatewayId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + + const result = transformPaymentMethodReferences( + paymentMethodReferences, + paymentMethodSetAccounts + ) + + expect(result).toHaveLength(1) + expect(result[0]).toEqual({ + accountId: 'stripe-account-1', + name: 'Visa •••• 4242', + status: 'Active', + isDefault: false, + type: 'card', + accountHolderName: null, + id: 'pm_123', + gatewayTokenId: 'pm_123', + usageType: 'OffSession', + gatewayId: 'stripe-account-1', + gatewayCustomerId: null, + last4: '4242', + network: 'visa', + issuer: null, + expiryMonth: null, + expiryYear: null, + bankName: null, + savedByMerchant: false + }) + }) + + test('transforms payment method reference with type card and last4', () => { + const paymentMethodReferences = [ + { + id: 'pm_456', + accountId: 'stripe-account-1', + type: 'card', + last4: '1234' + } + ] + const paymentMethodSetAccounts = [ + { + accountId: 'stripe-account-1', + gatewayId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + + const result = transformPaymentMethodReferences( + paymentMethodReferences, + paymentMethodSetAccounts + ) + + expect(result).toHaveLength(1) + expect(result[0].name).toBe('Card •••• 1234') + }) + + test('transforms payment method reference with sepa_debit type', () => { + const paymentMethodReferences = [ + { + id: 'pm_789', + accountId: 'stripe-account-1', + type: 'sepa_debit', + last4: '5678' + } + ] + const paymentMethodSetAccounts = [ + { + accountId: 'stripe-account-1', + gatewayId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + + const result = transformPaymentMethodReferences( + paymentMethodReferences, + paymentMethodSetAccounts + ) + + expect(result).toHaveLength(1) + expect(result[0].name).toBe('Account ending in 5678') + }) + + test('uses default name when brand and last4 are missing', () => { + const paymentMethodReferences = [ + { + id: 'pm_999', + accountId: 'stripe-account-1', + type: 'card' + } + ] + const paymentMethodSetAccounts = [ + { + accountId: 'stripe-account-1', + gatewayId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + + const result = transformPaymentMethodReferences( + paymentMethodReferences, + paymentMethodSetAccounts + ) + + expect(result).toHaveLength(1) + expect(result[0].name).toBe('Saved Payment Method') + }) + + test('filters out payment methods without matching account', () => { + const paymentMethodReferences = [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + }, + { + id: 'pm_456', + accountId: 'non-existent-account', + type: 'card', + brand: 'mastercard', + last4: '5555' + } + ] + const paymentMethodSetAccounts = [ + { + accountId: 'stripe-account-1', + gatewayId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + + const result = transformPaymentMethodReferences( + paymentMethodReferences, + paymentMethodSetAccounts + ) + + expect(result).toHaveLength(1) + expect(result[0].id).toBe('pm_123') + }) + + test('filters out payment methods without accountId', () => { + const paymentMethodReferences = [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + }, + { + id: 'pm_456', + type: 'card', + brand: 'mastercard', + last4: '5555' + } + ] + const paymentMethodSetAccounts = [ + { + accountId: 'stripe-account-1', + gatewayId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + + const result = transformPaymentMethodReferences( + paymentMethodReferences, + paymentMethodSetAccounts + ) + + expect(result).toHaveLength(1) + expect(result[0].id).toBe('pm_123') + }) + + test('filters out payment methods when paymentMethodSetAccounts is empty', () => { + const paymentMethodReferences = [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + } + ] + + const result = transformPaymentMethodReferences(paymentMethodReferences, []) + + expect(result).toHaveLength(0) + }) + + test('uses gatewayId from matching account when available', () => { + const paymentMethodReferences = [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + } + ] + const paymentMethodSetAccounts = [ + { + accountId: 'stripe-account-1', + gatewayId: 'custom-gateway-id', + vendor: 'Stripe' + } + ] + + const result = transformPaymentMethodReferences( + paymentMethodReferences, + paymentMethodSetAccounts + ) + + expect(result).toHaveLength(1) + expect(result[0].gatewayId).toBe('custom-gateway-id') + }) + + test('uses accountId as gatewayId when gatewayId is missing from account', () => { + const paymentMethodReferences = [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + } + ] + const paymentMethodSetAccounts = [ + { + accountId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + + const result = transformPaymentMethodReferences( + paymentMethodReferences, + paymentMethodSetAccounts + ) + + expect(result).toHaveLength(1) + expect(result[0].gatewayId).toBe('stripe-account-1') + }) + + test('filters out payment methods with invalid gatewayId', () => { + const paymentMethodReferences = [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + } + ] + const paymentMethodSetAccounts = [ + { + accountId: 'stripe-account-1', + gatewayId: null, + vendor: 'Stripe' + } + ] + + const result = transformPaymentMethodReferences( + paymentMethodReferences, + paymentMethodSetAccounts + ) + + expect(result).toHaveLength(1) + expect(result[0].gatewayId).toBe('stripe-account-1') + }) + + test('transforms multiple payment method references', () => { + const paymentMethodReferences = [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + }, + { + id: 'pm_456', + accountId: 'stripe-account-1', + type: 'card', + brand: 'mastercard', + last4: '5555' + } + ] + const paymentMethodSetAccounts = [ + { + accountId: 'stripe-account-1', + gatewayId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + + const result = transformPaymentMethodReferences( + paymentMethodReferences, + paymentMethodSetAccounts + ) + + expect(result).toHaveLength(2) + expect(result[0].name).toBe('Visa •••• 4242') + expect(result[1].name).toBe('Mastercard •••• 5555') + }) + }) }) From 15d8ca83ea637f827942bfd60864abae19e7b48c Mon Sep 17 00:00:00 2001 From: rvishwanathbhat Date: Wed, 28 Jan 2026 06:23:07 -0800 Subject: [PATCH 4/6] Addressing comments --- .../app/hooks/use-current-customer.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/template-retail-react-app/app/hooks/use-current-customer.js b/packages/template-retail-react-app/app/hooks/use-current-customer.js index 78be4ea522..a3b1ef7e2b 100644 --- a/packages/template-retail-react-app/app/hooks/use-current-customer.js +++ b/packages/template-retail-react-app/app/hooks/use-current-customer.js @@ -14,15 +14,7 @@ import {useCustomer, useCustomerId, useCustomerType} from '@salesforce/commerce- export const useCurrentCustomer = () => { const customerId = useCustomerId() const {isRegistered, isGuest, customerType} = useCustomerType() - const query = useCustomer( - { - parameters: { - customerId, - expand: ['paymentmethodreferences'] - } - }, - {enabled: !!customerId && isRegistered} - ) + const query = useCustomer({parameters: {customerId}}, {enabled: !!customerId && isRegistered}) const value = { ...query, data: { From 9fa4e45e231611a6acc055b07a9f17d171889efd Mon Sep 17 00:00:00 2001 From: rvishwanathbhat Date: Wed, 28 Jan 2026 08:35:28 -0800 Subject: [PATCH 5/6] W-20975340: Addressing comments --- .../app/hooks/use-current-customer.js | 10 +- .../checkout/partials/sf-payments-sheet.jsx | 38 +- .../partials/sf-payments-sheet.test.js | 2 + .../app/utils/sf-payments-utils.js | 14 +- .../app/utils/sf-payments-utils.test.js | 451 ++++++++---------- 5 files changed, 227 insertions(+), 288 deletions(-) diff --git a/packages/template-retail-react-app/app/hooks/use-current-customer.js b/packages/template-retail-react-app/app/hooks/use-current-customer.js index a3b1ef7e2b..1ab176c822 100644 --- a/packages/template-retail-react-app/app/hooks/use-current-customer.js +++ b/packages/template-retail-react-app/app/hooks/use-current-customer.js @@ -9,12 +9,16 @@ import {useCustomer, useCustomerId, useCustomerType} from '@salesforce/commerce- /** * A hook that returns the current customer. - * + * @param {Array} [expand] - Optional array of fields to expand in the customer query */ -export const useCurrentCustomer = () => { +export const useCurrentCustomer = (expand = undefined) => { const customerId = useCustomerId() const {isRegistered, isGuest, customerType} = useCustomerType() - const query = useCustomer({parameters: {customerId}}, {enabled: !!customerId && isRegistered}) + const parameters = { + customerId, + ...(expand && {expand}) + } + const query = useCustomer({parameters}, {enabled: !!customerId && isRegistered}) const value = { ...query, data: { diff --git a/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.jsx b/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.jsx index 817252a3ec..7de1e2d2f1 100644 --- a/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.jsx +++ b/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.jsx @@ -20,13 +20,9 @@ import { Divider } from '@salesforce/retail-react-app/app/components/shared/ui' import {useForm} from 'react-hook-form' -import { - useShopperBasketsMutation, - useCustomer, - useCustomerId, - useCustomerType -} from '@salesforce/commerce-sdk-react' +import {useShopperBasketsMutation} from '@salesforce/commerce-sdk-react' import {useCurrentBasket} from '@salesforce/retail-react-app/app/hooks/use-current-basket' +import {useCurrentCustomer} from '@salesforce/retail-react-app/app/hooks/use-current-customer' import {useCurrency} from '@salesforce/retail-react-app/app/hooks/use-currency' import {useCheckout} from '@salesforce/retail-react-app/app/pages/checkout/util/checkout-context' import {usePaymentConfiguration} from '@salesforce/commerce-sdk-react' @@ -64,25 +60,7 @@ const SFPaymentsSheet = forwardRef((props, ref) => { const navigate = useNavigation() const {data: basket} = useCurrentBasket() - const customerId = useCustomerId() - const {isRegistered} = useCustomerType() - const {data: customerData} = useCustomer( - { - parameters: { - customerId, - expand: ['paymentmethodreferences'] - } - }, - {enabled: !!customerId && isRegistered} - ) - // Add customerId and isRegistered to customer data for consistency with useCurrentCustomer - const customer = customerData - ? { - ...customerData, - customerId, - isRegistered - } - : null + const {data: customer} = useCurrentCustomer(['paymentmethodreferences']) const isPickupOnly = basket?.shipments?.length > 0 && @@ -505,12 +483,8 @@ const SFPaymentsSheet = forwardRef((props, ref) => { })) const savedPaymentMethods = useMemo( - () => - transformPaymentMethodReferences( - customer?.paymentMethodReferences, - paymentConfig?.paymentMethodSetAccounts - ), - [customer?.paymentMethodReferences, paymentConfig?.paymentMethodSetAccounts] + () => transformPaymentMethodReferences(customer, paymentConfig), + [customer, paymentConfig] ) useEffect(() => { @@ -518,7 +492,7 @@ const SFPaymentsSheet = forwardRef((props, ref) => { const paymentMethodSetAccounts = (paymentConfig.paymentMethodSetAccounts || []).map( (account) => ({ ...account, - gatewayId: account.gatewayId || account.accountId + gatewayId: account.accountId }) ) diff --git a/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.test.js b/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.test.js index a4fd4302b7..d275229326 100644 --- a/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.test.js +++ b/packages/template-retail-react-app/app/pages/checkout/partials/sf-payments-sheet.test.js @@ -1013,6 +1013,7 @@ describe('SFPaymentsSheet', () => { const ref = React.createRef() setupConfirmPaymentMocks() + // eslint-disable-next-line @typescript-eslint/no-var-requires const useShopperConfigurationModule = require('@salesforce/retail-react-app/app/hooks/use-shopper-configuration') const originalMock = useShopperConfigurationModule.useShopperConfiguration @@ -1154,6 +1155,7 @@ describe('SFPaymentsSheet', () => { ] jest.spyOn( + // eslint-disable-next-line @typescript-eslint/no-var-requires require('@salesforce/commerce-sdk-react'), 'usePaymentConfiguration' ).mockReturnValue({ diff --git a/packages/template-retail-react-app/app/utils/sf-payments-utils.js b/packages/template-retail-react-app/app/utils/sf-payments-utils.js index 3d11d90690..6b0eb21e2d 100644 --- a/packages/template-retail-react-app/app/utils/sf-payments-utils.js +++ b/packages/template-retail-react-app/app/utils/sf-payments-utils.js @@ -276,14 +276,14 @@ export const createPaymentInstrumentBody = ({ /** * Transforms payment method references from API format to SF Payments SDK format. - * @param {Array} paymentMethodReferences - Array of payment method references - * @param {Array} paymentMethodSetAccounts - Array of payment method set accounts + * @param {Object} customer - Customer object with paymentMethodReferences property + * @param {Object} paymentConfig - Payment configuration object with paymentMethodSetAccounts property * @returns {Array} Transformed payment method references for SF Payments SDK */ -export const transformPaymentMethodReferences = ( - paymentMethodReferences, - paymentMethodSetAccounts = [] -) => { +export const transformPaymentMethodReferences = (customer, paymentConfig) => { + const paymentMethodReferences = customer?.paymentMethodReferences + const paymentMethodSetAccounts = paymentConfig?.paymentMethodSetAccounts || [] + if (!Array.isArray(paymentMethodReferences) || !Array.isArray(paymentMethodSetAccounts)) { return [] } @@ -316,7 +316,7 @@ export const transformPaymentMethodReferences = ( return null } - const gatewayId = matchingAccount.gatewayId || matchingAccount.accountId + const gatewayId = matchingAccount.accountId if (!gatewayId || typeof gatewayId !== 'string') { return null diff --git a/packages/template-retail-react-app/app/utils/sf-payments-utils.test.js b/packages/template-retail-react-app/app/utils/sf-payments-utils.test.js index ded92ffb82..3135305319 100644 --- a/packages/template-retail-react-app/app/utils/sf-payments-utils.test.js +++ b/packages/template-retail-react-app/app/utils/sf-payments-utils.test.js @@ -1460,43 +1460,44 @@ describe('sf-payments-utils', () => { }) describe('transformPaymentMethodReferences', () => { - test('returns empty array when paymentMethodReferences is null', () => { - const result = transformPaymentMethodReferences(null, []) + test('returns empty array when customer is null', () => { + const result = transformPaymentMethodReferences(null, {}) expect(result).toEqual([]) }) - test('returns empty array when paymentMethodReferences is undefined', () => { - const result = transformPaymentMethodReferences(undefined, []) + test('returns empty array when customer is undefined', () => { + const result = transformPaymentMethodReferences(undefined, {}) expect(result).toEqual([]) }) - test('returns empty array when paymentMethodReferences is not an array', () => { - const result = transformPaymentMethodReferences({}, []) + test('returns empty array when customer has no paymentMethodReferences', () => { + const result = transformPaymentMethodReferences({}, {}) expect(result).toEqual([]) }) test('transforms payment method reference with brand and last4', () => { - const paymentMethodReferences = [ - { - id: 'pm_123', - accountId: 'stripe-account-1', - type: 'card', - brand: 'visa', - last4: '4242' - } - ] - const paymentMethodSetAccounts = [ - { - accountId: 'stripe-account-1', - gatewayId: 'stripe-account-1', - vendor: 'Stripe' - } - ] + const customer = { + paymentMethodReferences: [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + } + ] + } + const paymentConfig = { + paymentMethodSetAccounts: [ + { + accountId: 'stripe-account-1', + gatewayId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + } - const result = transformPaymentMethodReferences( - paymentMethodReferences, - paymentMethodSetAccounts - ) + const result = transformPaymentMethodReferences(customer, paymentConfig) expect(result).toHaveLength(1) expect(result[0]).toEqual({ @@ -1522,274 +1523,232 @@ describe('sf-payments-utils', () => { }) test('transforms payment method reference with type card and last4', () => { - const paymentMethodReferences = [ - { - id: 'pm_456', - accountId: 'stripe-account-1', - type: 'card', - last4: '1234' - } - ] - const paymentMethodSetAccounts = [ - { - accountId: 'stripe-account-1', - gatewayId: 'stripe-account-1', - vendor: 'Stripe' - } - ] + const customer = { + paymentMethodReferences: [ + { + id: 'pm_456', + accountId: 'stripe-account-1', + type: 'card', + last4: '1234' + } + ] + } + const paymentConfig = { + paymentMethodSetAccounts: [ + { + accountId: 'stripe-account-1', + gatewayId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + } - const result = transformPaymentMethodReferences( - paymentMethodReferences, - paymentMethodSetAccounts - ) + const result = transformPaymentMethodReferences(customer, paymentConfig) expect(result).toHaveLength(1) expect(result[0].name).toBe('Card •••• 1234') }) test('transforms payment method reference with sepa_debit type', () => { - const paymentMethodReferences = [ - { - id: 'pm_789', - accountId: 'stripe-account-1', - type: 'sepa_debit', - last4: '5678' - } - ] - const paymentMethodSetAccounts = [ - { - accountId: 'stripe-account-1', - gatewayId: 'stripe-account-1', - vendor: 'Stripe' - } - ] + const customer = { + paymentMethodReferences: [ + { + id: 'pm_789', + accountId: 'stripe-account-1', + type: 'sepa_debit', + last4: '5678' + } + ] + } + const paymentConfig = { + paymentMethodSetAccounts: [ + { + accountId: 'stripe-account-1', + gatewayId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + } - const result = transformPaymentMethodReferences( - paymentMethodReferences, - paymentMethodSetAccounts - ) + const result = transformPaymentMethodReferences(customer, paymentConfig) expect(result).toHaveLength(1) expect(result[0].name).toBe('Account ending in 5678') }) test('uses default name when brand and last4 are missing', () => { - const paymentMethodReferences = [ - { - id: 'pm_999', - accountId: 'stripe-account-1', - type: 'card' - } - ] - const paymentMethodSetAccounts = [ - { - accountId: 'stripe-account-1', - gatewayId: 'stripe-account-1', - vendor: 'Stripe' - } - ] + const customer = { + paymentMethodReferences: [ + { + id: 'pm_999', + accountId: 'stripe-account-1', + type: 'card' + } + ] + } + const paymentConfig = { + paymentMethodSetAccounts: [ + { + accountId: 'stripe-account-1', + gatewayId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + } - const result = transformPaymentMethodReferences( - paymentMethodReferences, - paymentMethodSetAccounts - ) + const result = transformPaymentMethodReferences(customer, paymentConfig) expect(result).toHaveLength(1) expect(result[0].name).toBe('Saved Payment Method') }) test('filters out payment methods without matching account', () => { - const paymentMethodReferences = [ - { - id: 'pm_123', - accountId: 'stripe-account-1', - type: 'card', - brand: 'visa', - last4: '4242' - }, - { - id: 'pm_456', - accountId: 'non-existent-account', - type: 'card', - brand: 'mastercard', - last4: '5555' - } - ] - const paymentMethodSetAccounts = [ - { - accountId: 'stripe-account-1', - gatewayId: 'stripe-account-1', - vendor: 'Stripe' - } - ] + const customer = { + paymentMethodReferences: [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + }, + { + id: 'pm_456', + accountId: 'non-existent-account', + type: 'card', + brand: 'mastercard', + last4: '5555' + } + ] + } + const paymentConfig = { + paymentMethodSetAccounts: [ + { + accountId: 'stripe-account-1', + gatewayId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + } - const result = transformPaymentMethodReferences( - paymentMethodReferences, - paymentMethodSetAccounts - ) + const result = transformPaymentMethodReferences(customer, paymentConfig) expect(result).toHaveLength(1) expect(result[0].id).toBe('pm_123') }) test('filters out payment methods without accountId', () => { - const paymentMethodReferences = [ - { - id: 'pm_123', - accountId: 'stripe-account-1', - type: 'card', - brand: 'visa', - last4: '4242' - }, - { - id: 'pm_456', - type: 'card', - brand: 'mastercard', - last4: '5555' - } - ] - const paymentMethodSetAccounts = [ - { - accountId: 'stripe-account-1', - gatewayId: 'stripe-account-1', - vendor: 'Stripe' - } - ] + const customer = { + paymentMethodReferences: [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + }, + { + id: 'pm_456', + type: 'card', + brand: 'mastercard', + last4: '5555' + } + ] + } + const paymentConfig = { + paymentMethodSetAccounts: [ + { + accountId: 'stripe-account-1', + gatewayId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + } - const result = transformPaymentMethodReferences( - paymentMethodReferences, - paymentMethodSetAccounts - ) + const result = transformPaymentMethodReferences(customer, paymentConfig) expect(result).toHaveLength(1) expect(result[0].id).toBe('pm_123') }) test('filters out payment methods when paymentMethodSetAccounts is empty', () => { - const paymentMethodReferences = [ - { - id: 'pm_123', - accountId: 'stripe-account-1', - type: 'card', - brand: 'visa', - last4: '4242' - } - ] + const customer = { + paymentMethodReferences: [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + } + ] + } + const paymentConfig = { + paymentMethodSetAccounts: [] + } - const result = transformPaymentMethodReferences(paymentMethodReferences, []) + const result = transformPaymentMethodReferences(customer, paymentConfig) expect(result).toHaveLength(0) }) - test('uses gatewayId from matching account when available', () => { - const paymentMethodReferences = [ - { - id: 'pm_123', - accountId: 'stripe-account-1', - type: 'card', - brand: 'visa', - last4: '4242' - } - ] - const paymentMethodSetAccounts = [ - { - accountId: 'stripe-account-1', - gatewayId: 'custom-gateway-id', - vendor: 'Stripe' - } - ] - - const result = transformPaymentMethodReferences( - paymentMethodReferences, - paymentMethodSetAccounts - ) - - expect(result).toHaveLength(1) - expect(result[0].gatewayId).toBe('custom-gateway-id') - }) - - test('uses accountId as gatewayId when gatewayId is missing from account', () => { - const paymentMethodReferences = [ - { - id: 'pm_123', - accountId: 'stripe-account-1', - type: 'card', - brand: 'visa', - last4: '4242' - } - ] - const paymentMethodSetAccounts = [ - { - accountId: 'stripe-account-1', - vendor: 'Stripe' - } - ] - - const result = transformPaymentMethodReferences( - paymentMethodReferences, - paymentMethodSetAccounts - ) - - expect(result).toHaveLength(1) - expect(result[0].gatewayId).toBe('stripe-account-1') - }) + test('uses accountId for gatewayId', () => { + const customer = { + paymentMethodReferences: [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + } + ] + } + const paymentConfig = { + paymentMethodSetAccounts: [ + { + accountId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + } - test('filters out payment methods with invalid gatewayId', () => { - const paymentMethodReferences = [ - { - id: 'pm_123', - accountId: 'stripe-account-1', - type: 'card', - brand: 'visa', - last4: '4242' - } - ] - const paymentMethodSetAccounts = [ - { - accountId: 'stripe-account-1', - gatewayId: null, - vendor: 'Stripe' - } - ] - - const result = transformPaymentMethodReferences( - paymentMethodReferences, - paymentMethodSetAccounts - ) + const result = transformPaymentMethodReferences(customer, paymentConfig) expect(result).toHaveLength(1) expect(result[0].gatewayId).toBe('stripe-account-1') }) test('transforms multiple payment method references', () => { - const paymentMethodReferences = [ - { - id: 'pm_123', - accountId: 'stripe-account-1', - type: 'card', - brand: 'visa', - last4: '4242' - }, - { - id: 'pm_456', - accountId: 'stripe-account-1', - type: 'card', - brand: 'mastercard', - last4: '5555' - } - ] - const paymentMethodSetAccounts = [ - { - accountId: 'stripe-account-1', - gatewayId: 'stripe-account-1', - vendor: 'Stripe' - } - ] + const customer = { + paymentMethodReferences: [ + { + id: 'pm_123', + accountId: 'stripe-account-1', + type: 'card', + brand: 'visa', + last4: '4242' + }, + { + id: 'pm_456', + accountId: 'stripe-account-1', + type: 'card', + brand: 'mastercard', + last4: '5555' + } + ] + } + const paymentConfig = { + paymentMethodSetAccounts: [ + { + accountId: 'stripe-account-1', + gatewayId: 'stripe-account-1', + vendor: 'Stripe' + } + ] + } - const result = transformPaymentMethodReferences( - paymentMethodReferences, - paymentMethodSetAccounts - ) + const result = transformPaymentMethodReferences(customer, paymentConfig) expect(result).toHaveLength(2) expect(result[0].name).toBe('Visa •••• 4242') From 29e2e82dda8d98a51de6bfa712bd641732c6ae98 Mon Sep 17 00:00:00 2001 From: rvishwanathbhat Date: Wed, 28 Jan 2026 09:07:33 -0800 Subject: [PATCH 6/6] W-20975340: Addressing comments --- .../template-retail-react-app/app/hooks/use-current-customer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/template-retail-react-app/app/hooks/use-current-customer.js b/packages/template-retail-react-app/app/hooks/use-current-customer.js index 1ab176c822..841026de0e 100644 --- a/packages/template-retail-react-app/app/hooks/use-current-customer.js +++ b/packages/template-retail-react-app/app/hooks/use-current-customer.js @@ -11,7 +11,7 @@ import {useCustomer, useCustomerId, useCustomerType} from '@salesforce/commerce- * A hook that returns the current customer. * @param {Array} [expand] - Optional array of fields to expand in the customer query */ -export const useCurrentCustomer = (expand = undefined) => { +export const useCurrentCustomer = (expand) => { const customerId = useCustomerId() const {isRegistered, isGuest, customerType} = useCustomerType() const parameters = {