Skip to content

Commit

Permalink
Add Amazon Pay customize express checkout settings page (#3763)
Browse files Browse the repository at this point in the history
* Add the short blurb

* Add Amazon Pay hook

* Add tests for settings endpoint controller and settings payment request section

* Update payment request section tests

* Update changelog entry

* Add customize settings link

* Add Amazon Pay settings to entry point

* Add Amazon Pay enable section

* Add Amazon Pay button and locations hooks and tests

* Add Amazon Pay settings section

* Add amazon settings to settings endpoint

* Remove unused theme and button type settings

* Render the preview Amazon Pay button

* Remove Amazon Pay button type and button theme

* Import payment request settings style

* Add tests for Amazon Pay settings

* Add changelog entry

* Fix up tests

* Remove Amazon Pay payment request button preview

* Fix translation

* Remove unused property
  • Loading branch information
hsingyuc authored Jan 30, 2025
1 parent eb16df9 commit 527e877
Show file tree
Hide file tree
Showing 17 changed files with 785 additions and 8 deletions.
2 changes: 2 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* Fix - Adds support for the Reunion country when checking out using the new checkout experience.
* Add - Support zero-amount refunds.
* Fix - A potential fix to prevent duplicate charges.
* Fix - Prevent empty settings screen when cancelling changes to the payment methods display order.
* Fix - Improve product page caching when Express Payment buttons are not enabled.
* Fix - Allow editing uncaptured orders but show a warning about the possible failure scenario.
* Fix - Fetch the payment intent status on order edit page only for unpaid orders if manual capture is enabled.
Expand All @@ -25,6 +26,7 @@
* Add - Add total tax amount to metadata.
* Update - Update the translation for payment requests settings section notice.
* Add - Add Amazon Pay to settings express checkout section.
* Add - Add Amazon Pay customize express checkout page.

= 9.1.1 - 2025-01-10 =
* Fix - Fixes the webhook order retrieval by intent charges. The processed event is an object, not an array.
Expand Down
21 changes: 21 additions & 0 deletions client/data/settings/__tests__/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import {
useIsShortAccountStatementEnabled,
useDebugLog,
useManualCapture,
useAmazonPayEnabledSettings,
useAmazonPayLocations,
useAmazonPayButtonSize,
} from '../hooks';
import { STORE_NAME } from '../../constants';
import {
Expand Down Expand Up @@ -311,6 +314,24 @@ describe( 'Settings hooks tests', () => {
testedValue: [ 'checkout', 'cart' ],
fallbackValue: [],
},
useAmazonPayEnabledSettings: {
hook: useAmazonPayEnabledSettings,
storeKey: 'is_amazon_pay_enabled',
testedValue: true,
fallbackValue: false,
},
useAmazonPayButtonSize: {
hook: useAmazonPayButtonSize,
storeKey: 'amazon_pay_button_size',
testedValue: 'large',
fallbackValue: '',
},
useAmazonPayLocations: {
hook: useAmazonPayLocations,
storeKey: 'amazon_pay_button_locations',
testedValue: [ 'checkout', 'cart' ],
fallbackValue: [],
},
};

describe.each( Object.entries( generatedHookExpectations ) )(
Expand Down
14 changes: 11 additions & 3 deletions client/data/settings/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,6 @@ export const useEnabledPaymentMethodIds = makeSettingsHook(
export const usePaymentRequestEnabledSettings = makeSettingsHook(
'is_payment_request_enabled'
);
export const useAmazonPayEnabledSettings = makeSettingsHook(
'is_amazon_pay_enabled'
);
export const usePaymentRequestButtonSize = makeSettingsHook(
'payment_request_button_size',
''
Expand All @@ -159,6 +156,17 @@ export const usePaymentRequestLocations = makeSettingsHook(
'payment_request_button_locations',
EMPTY_ARR
);
export const useAmazonPayEnabledSettings = makeSettingsHook(
'is_amazon_pay_enabled'
);
export const useAmazonPayButtonSize = makeSettingsHook(
'amazon_pay_button_size',
''
);
export const useAmazonPayLocations = makeSettingsHook(
'amazon_pay_button_locations',
EMPTY_ARR
);
export const useIsStripeEnabled = makeSettingsHook( 'is_stripe_enabled' );
export const useTestMode = makeSettingsHook( 'is_test_mode_enabled' );
export const useSavedCards = makeSettingsHook( 'is_saved_cards_enabled' );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import AmazonPaySettingsSection from '../amazon-pay-settings-section';
import {
useAmazonPayEnabledSettings,
useAmazonPayLocations,
} from 'wcstripe/data';

jest.mock( 'wcstripe/data', () => ( {
useAmazonPayEnabledSettings: jest.fn(),
useAmazonPayLocations: jest.fn(),
useAmazonPayButtonSize: jest.fn().mockReturnValue( [ 'default' ] ),
} ) );

jest.mock( 'wcstripe/data/account/hooks', () => ( {
useAccount: jest.fn().mockReturnValue( { data: {} } ),
} ) );
jest.mock( 'wcstripe/data/account-keys/hooks', () => ( {
useAccountKeys: jest.fn().mockReturnValue( {} ),
useAccountKeysPublishableKey: jest.fn().mockReturnValue( [ '' ] ),
useAccountKeysTestPublishableKey: jest.fn().mockReturnValue( [ '' ] ),
} ) );

describe( 'AmazonPaySettingsSection', () => {
const globalValues = global.wc_stripe_amazon_pay_settings_params;

beforeEach( () => {
useAmazonPayEnabledSettings.mockReturnValue( [ true, jest.fn() ] );

useAmazonPayLocations.mockReturnValue( [
[ 'checkout', 'product', 'cart' ],
jest.fn(),
] );

global.wc_stripe_amazon_pay_settings_params = {
...globalValues,
key: 'pk_test_123',
locale: 'en',
};
} );

afterEach( () => {
jest.clearAllMocks();
global.wc_stripe_amazon_pay_settings_params = globalValues;
} );

it( 'should enable express checkout locations when express checkout is enabled', () => {
render( <AmazonPaySettingsSection /> );

const [
checkoutCheckbox,
productPageCheckbox,
cartCheckbox,
] = screen.getAllByRole( 'checkbox' );

expect( checkoutCheckbox ).not.toBeDisabled();
expect( checkoutCheckbox ).toBeChecked();
expect( productPageCheckbox ).not.toBeDisabled();
expect( productPageCheckbox ).toBeChecked();
expect( cartCheckbox ).not.toBeDisabled();
expect( cartCheckbox ).toBeChecked();
} );

it( 'should trigger an action to save the checked locations when un-checking the location checkboxes', () => {
const updateAmazonPayLocationsHandler = jest.fn();
useAmazonPayEnabledSettings.mockReturnValue( [ true, jest.fn() ] );
useAmazonPayLocations.mockReturnValue( [
[ 'checkout', 'product', 'cart' ],
updateAmazonPayLocationsHandler,
] );

render( <AmazonPaySettingsSection /> );

// Uncheck each checkbox, and verify them what kind of action should have been called
userEvent.click( screen.getByText( 'Product page' ) );
expect( updateAmazonPayLocationsHandler ).toHaveBeenLastCalledWith( [
'checkout',
'cart',
] );

userEvent.click( screen.getByText( 'Checkout' ) );
expect( updateAmazonPayLocationsHandler ).toHaveBeenLastCalledWith( [
'product',
'cart',
] );

userEvent.click( screen.getByText( 'Cart' ) );
expect( updateAmazonPayLocationsHandler ).toHaveBeenLastCalledWith( [
'checkout',
'product',
] );
} );

it( 'should trigger an action to save the checked locations when checking the location checkboxes', () => {
const updateAmazonPayLocationsHandler = jest.fn();
useAmazonPayEnabledSettings.mockReturnValue( [ true, jest.fn() ] );
useAmazonPayLocations.mockReturnValue( [
[],
updateAmazonPayLocationsHandler,
] );

render( <AmazonPaySettingsSection /> );

userEvent.click( screen.getByText( 'Cart' ) );
expect( updateAmazonPayLocationsHandler ).toHaveBeenLastCalledWith( [
'cart',
] );

userEvent.click( screen.getByText( 'Product page' ) );
expect( updateAmazonPayLocationsHandler ).toHaveBeenLastCalledWith( [
'product',
] );

userEvent.click( screen.getByText( 'Checkout' ) );
expect( updateAmazonPayLocationsHandler ).toHaveBeenLastCalledWith( [
'checkout',
] );
} );
} );
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import AmazonPaySettingsSection from '../amazon-pay-settings-section';
import {
useAmazonPayEnabledSettings,
useAmazonPayLocations,
useAmazonPayButtonSize,
} from 'wcstripe/data';

jest.mock( 'wcstripe/data', () => ( {
useAmazonPayEnabledSettings: jest.fn(),
useAmazonPayLocations: jest.fn(),
useAmazonPayButtonSize: jest.fn().mockReturnValue( [ 'default' ] ),
} ) );
jest.mock( 'wcstripe/data/account/hooks', () => ( {
useAccount: jest.fn().mockReturnValue( { data: {} } ),
} ) );
jest.mock( 'wcstripe/data/account-keys/hooks', () => ( {
useAccountKeys: jest.fn().mockReturnValue( {} ),
useAccountKeysPublishableKey: jest.fn().mockReturnValue( [ '' ] ),
useAccountKeysTestPublishableKey: jest.fn().mockReturnValue( [ '' ] ),
} ) );

describe( 'AmazonPaySettingsSection', () => {
const globalValues = global.wc_stripe_amazon_pay_settings_params;
beforeEach( () => {
useAmazonPayEnabledSettings.mockReturnValue( [ true, jest.fn() ] );

useAmazonPayLocations.mockReturnValue( [
[ 'checkout', 'product', 'cart' ],
jest.fn(),
] );

global.wc_stripe_amazon_pay_settings_params = {
...globalValues,
key: 'pk_test_123',
locale: 'en',
};
} );

afterEach( () => {
jest.clearAllMocks();
global.wc_stripe_amazon_pay_settings_params = globalValues;
} );

it( 'renders settings with defaults', () => {
render( <AmazonPaySettingsSection /> );

// confirm settings headings.
expect(
screen.queryByRole( 'heading', { name: 'Appearance' } )
).toBeInTheDocument();

// confirm radio button groups displayed.
const [ sizeRadio ] = screen.queryAllByRole( 'radio' );
expect( sizeRadio ).toBeInTheDocument();

// confirm default values.
expect( screen.getByLabelText( 'Default (48 px)' ) ).toBeChecked();
} );

it( 'triggers the hooks when the settings are being interacted with', () => {
const setButtonSizeMock = jest.fn();

useAmazonPayButtonSize.mockReturnValue( [
'default',
setButtonSizeMock,
] );
useAmazonPayEnabledSettings.mockReturnValue( [ true, jest.fn() ] );

render( <AmazonPaySettingsSection /> );

expect( setButtonSizeMock ).not.toHaveBeenCalled();

userEvent.click( screen.getByLabelText( 'Large (56 px)' ) );
expect( setButtonSizeMock ).toHaveBeenCalledWith( 'large' );
} );
} );
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { __ } from '@wordpress/i18n';
import React from 'react';
import { Card, CheckboxControl } from '@wordpress/components';
import { useAmazonPayEnabledSettings } from 'wcstripe/data';
import CardBody from 'wcstripe/settings/card-body';
const AmazonPayEnableSection = () => {
const [
isAmazonPayEnabled,
updateIsAmazonPayEnabled,
] = useAmazonPayEnabledSettings();

return (
<Card className="express-checkout-settings">
<CardBody>
<CheckboxControl
checked={ isAmazonPayEnabled }
onChange={ updateIsAmazonPayEnabled }
label={ __(
'Enable Amazon Pay',
'woocommerce-gateway-stripe'
) }
help={ __(
'When enabled, customers who have configured Amazon Pay enabled devices ' +
'will be able to pay with their respective choice of Wallet.',
'woocommerce-gateway-stripe'
) }
/>
</CardBody>
</Card>
);
};

export default AmazonPayEnableSection;
60 changes: 60 additions & 0 deletions client/entrypoints/amazon-pay-settings/amazon-pay-page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { __ } from '@wordpress/i18n';
import React from 'react';
import AmazonPayIcon from '../../payment-method-icons/amazon-pay';
import AmazonPayEnableSection from './amazon-pay-enable-section';
import AmazonPaySettingsSection from './amazon-pay-settings-section';
import SettingsSection from 'wcstripe/settings/settings-section';
import SettingsLayout from 'wcstripe/settings/settings-layout';
import LoadableSettingsSection from 'wcstripe/settings/loadable-settings-section';
import SaveSettingsSection from 'wcstripe/settings/save-settings-section';
import '../payment-request-settings/style.scss';

const EnableDescription = () => (
<>
<div className="express-checkout-settings__icon">
<AmazonPayIcon size="medium" />
</div>
<p>
{ __(
'Decide how buttons for digital wallets Amazon Pay ' +
'is displayed in your store. Depending on ' +
'their web browser and their wallet configurations.',
'woocommerce-gateway-stripe'
) }
</p>
</>
);

const SettingsDescription = () => (
<>
<h2>{ __( 'Settings', 'woocommerce-gateway-stripe' ) }</h2>
<p>
{ __(
'Configure the display of Amazon Pay button on your store.',
'woocommerce-gateway-stripe'
) }
</p>
</>
);

const AmazonPayPage = () => {
return (
<SettingsLayout>
<SettingsSection Description={ EnableDescription }>
<LoadableSettingsSection numLines={ 30 }>
<AmazonPayEnableSection />
</LoadableSettingsSection>
</SettingsSection>

<SettingsSection Description={ SettingsDescription }>
<LoadableSettingsSection numLines={ 30 }>
<AmazonPaySettingsSection />
</LoadableSettingsSection>
</SettingsSection>

<SaveSettingsSection />
</SettingsLayout>
);
};

export default AmazonPayPage;
Loading

0 comments on commit 527e877

Please sign in to comment.