Skip to content

Commit 92dc6da

Browse files
authored
Show unsupported payment methods at the end of the payment method list (#4523)
* Show payment methods without currency support at the end of the payment list * Add JSDoc * Memoize and refactor sorting * Fix JS unit tests * Add changelog * Use getSetting() instead of global reference; rename variable * Update unit tests to mock getSetting() and centralise currency mocking * Return early when UPE is disabled; update notes * Refactor logic into central helper function * Update unit tests * Add tests for getPaymentMethodUnavailableReason * Sort plugin conflicts before currency issues * Fix changelog entry location
1 parent 23852c7 commit 92dc6da

File tree

13 files changed

+509
-135
lines changed

13 files changed

+509
-135
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* Fix - Fix unnecessary Stripe API calls when rendering subscription details
1414
* Add - Adds a new action (`wc_stripe_webhook_received`) to allow additional actions to be taken for webhook notifications from Stripe
1515
* Fix - Allow checkout for logged-in users without an email in their account when a billing email is provided
16+
* Update - Show all available payment methods before unavailable payment methods
1617

1718
= 9.8.1 - 2025-08-15 =
1819
* Fix - Remove connection type requirement from PMC sync migration attempt

client/components/payment-method-missing-currency-pill/__tests__/index.test.js

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React from 'react';
22
import { screen, render } from '@testing-library/react';
33
import PaymentMethodMissingCurrencyPill from '..';
44
import { usePaymentMethodCurrencies } from 'utils/use-payment-method-currencies';
5+
import usePaymentMethodUnavailableReason from 'utils/use-payment-method-unavailable-reason';
6+
import { PAYMENT_METHOD_UNAVAILABLE_REASONS } from 'wcstripe/stripe-utils/constants';
57

68
jest.mock( '../../../payment-methods-map', () => ( {
79
card: { currencies: [] },
@@ -12,34 +14,33 @@ jest.mock( 'utils/use-payment-method-currencies', () => ( {
1214
usePaymentMethodCurrencies: jest.fn(),
1315
} ) );
1416

17+
jest.mock( 'utils/use-payment-method-unavailable-reason' );
18+
1519
describe( 'PaymentMethodMissingCurrencyPill', () => {
1620
beforeEach( () => {
1721
global.wcSettings = { currency: { code: 'USD' } };
1822
usePaymentMethodCurrencies.mockReturnValue( [ 'EUR' ] );
1923
} );
2024

21-
it( 'should render the "Requires currency" text', () => {
25+
it( 'should render the "Requires currency" text when currency is not supported', () => {
26+
usePaymentMethodUnavailableReason.mockReturnValue(
27+
PAYMENT_METHOD_UNAVAILABLE_REASONS.UNSUPPORTED_CURRENCY
28+
);
29+
2230
render(
2331
<PaymentMethodMissingCurrencyPill id="giropay" label="giropay" />
2432
);
2533

2634
expect( screen.queryByText( 'Requires currency' ) ).toBeInTheDocument();
2735
} );
2836

29-
it( 'should not render when currency matches', () => {
30-
global.wcSettings = { currency: { code: 'EUR' } };
37+
it( 'should not render when currency is supported', () => {
38+
usePaymentMethodUnavailableReason.mockReturnValue( null );
39+
3140
const { container } = render(
3241
<PaymentMethodMissingCurrencyPill id="giropay" label="giropay" />
3342
);
3443

3544
expect( container.firstChild ).toBeNull();
3645
} );
37-
38-
it( 'should render when currency differs', () => {
39-
render(
40-
<PaymentMethodMissingCurrencyPill id="giropay" label="giropay" />
41-
);
42-
43-
expect( screen.queryByText( 'Requires currency' ) ).toBeInTheDocument();
44-
} );
4546
} );

client/components/payment-method-missing-currency-pill/index.js

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import interpolateComponents from 'interpolate-components';
55
import { Icon, info } from '@wordpress/icons';
66
import Popover from 'wcstripe/components/popover';
77
import { usePaymentMethodCurrencies } from 'utils/use-payment-method-currencies';
8-
import { PAYMENT_METHOD_CARD } from 'wcstripe/stripe-utils/constants';
8+
import usePaymentMethodUnavailableReason from 'utils/use-payment-method-unavailable-reason';
9+
import { PAYMENT_METHOD_UNAVAILABLE_REASONS } from 'wcstripe/stripe-utils/constants';
910

1011
const StyledPill = styled.span`
1112
display: inline-flex;
@@ -47,47 +48,47 @@ const IconComponent = ( { children, ...props } ) => (
4748

4849
const PaymentMethodMissingCurrencyPill = ( { id, label } ) => {
4950
const paymentMethodCurrencies = usePaymentMethodCurrencies( id );
50-
const storeCurrency = window?.wcSettings?.currency?.code;
51+
const unavailableReason = usePaymentMethodUnavailableReason( id );
5152

5253
if (
53-
id !== PAYMENT_METHOD_CARD &&
54-
! paymentMethodCurrencies.includes( storeCurrency )
54+
unavailableReason !==
55+
PAYMENT_METHOD_UNAVAILABLE_REASONS.UNSUPPORTED_CURRENCY
5556
) {
56-
return (
57-
<StyledPill>
58-
{ __( 'Requires currency', 'woocommerce-gateway-stripe' ) }
59-
<Popover
60-
BaseComponent={ IconComponent }
61-
content={ interpolateComponents( {
62-
mixedString: sprintf(
63-
/* translators: $1: a payment method name. %2: Currency(ies). */
64-
__(
65-
'%1$s requires store currency to be set to %2$s. {{currencySettingsLink}}Set currency{{/currencySettingsLink}}',
66-
'woocommerce-gateway-stripe'
67-
),
68-
label,
69-
paymentMethodCurrencies.join( ', ' )
70-
),
71-
components: {
72-
currencySettingsLink: (
73-
<StyledLink
74-
href="/wp-admin/admin.php?page=wc-settings&tab=general"
75-
target="_blank"
76-
rel="noreferrer"
77-
onClick={ ( ev ) => {
78-
// Stop propagation is necessary so it doesn't trigger the tooltip click event.
79-
ev.stopPropagation();
80-
} }
81-
/>
82-
),
83-
},
84-
} ) }
85-
/>
86-
</StyledPill>
87-
);
57+
return null;
8858
}
8959

90-
return null;
60+
return (
61+
<StyledPill>
62+
{ __( 'Requires currency', 'woocommerce-gateway-stripe' ) }
63+
<Popover
64+
BaseComponent={ IconComponent }
65+
content={ interpolateComponents( {
66+
mixedString: sprintf(
67+
/* translators: $1: a payment method name. %2: Currency(ies). */
68+
__(
69+
'%1$s requires store currency to be set to %2$s. {{currencySettingsLink}}Set currency{{/currencySettingsLink}}',
70+
'woocommerce-gateway-stripe'
71+
),
72+
label,
73+
paymentMethodCurrencies.join( ', ' )
74+
),
75+
components: {
76+
currencySettingsLink: (
77+
<StyledLink
78+
href="/wp-admin/admin.php?page=wc-settings&tab=general"
79+
target="_blank"
80+
rel="noreferrer"
81+
onClick={ ( ev ) => {
82+
// Stop propagation is necessary so it doesn't trigger the tooltip click event.
83+
ev.stopPropagation();
84+
} }
85+
/>
86+
),
87+
},
88+
} ) }
89+
/>
90+
</StyledPill>
91+
);
9192
};
9293

9394
export default PaymentMethodMissingCurrencyPill;
Lines changed: 38 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
/* global wc_stripe_settings_params */
21
import { __, sprintf } from '@wordpress/i18n';
32
import React from 'react';
43
import styled from '@emotion/styled';
54
import interpolateComponents from 'interpolate-components';
65
import { Icon, info } from '@wordpress/icons';
6+
import usePaymentMethodUnavailableReason from 'utils/use-payment-method-unavailable-reason';
77
import Popover from 'wcstripe/components/popover';
8-
import {
9-
PAYMENT_METHOD_AFFIRM,
10-
PAYMENT_METHOD_KLARNA,
11-
} from 'wcstripe/stripe-utils/constants';
8+
import { PAYMENT_METHOD_UNAVAILABLE_REASONS } from 'wcstripe/stripe-utils/constants';
129

1310
const StyledPill = styled.span`
1411
display: inline-flex;
@@ -49,48 +46,45 @@ const IconComponent = ( { children, ...props } ) => (
4946
);
5047

5148
const PaymentMethodUnavailableDueConflictPill = ( { id, label } ) => {
49+
const unavailableReason = usePaymentMethodUnavailableReason( id );
50+
5251
if (
53-
( id === PAYMENT_METHOD_AFFIRM &&
54-
// eslint-disable-next-line camelcase
55-
wc_stripe_settings_params.has_affirm_gateway_plugin ) ||
56-
( id === PAYMENT_METHOD_KLARNA &&
57-
// eslint-disable-next-line camelcase
58-
wc_stripe_settings_params.has_klarna_gateway_plugin )
52+
unavailableReason !==
53+
PAYMENT_METHOD_UNAVAILABLE_REASONS.OFFICIAL_PLUGIN_CONFLICT
5954
) {
60-
return (
61-
<StyledPill>
62-
{ __( 'Has plugin conflict', 'woocommerce-gateway-stripe' ) }
63-
<Popover
64-
BaseComponent={ IconComponent }
65-
content={ interpolateComponents( {
66-
mixedString: sprintf(
67-
/* translators: $1: a payment method name */
68-
__(
69-
'%1$s is unavailable due to another official plugin being active.',
70-
'woocommerce-gateway-stripe'
71-
),
72-
label
73-
),
74-
components: {
75-
currencySettingsLink: (
76-
<StyledLink
77-
href="/wp-admin/admin.php?page=wc-settings&tab=general"
78-
target="_blank"
79-
rel="noreferrer"
80-
onClick={ ( ev ) => {
81-
// Stop propagation is necessary so it doesn't trigger the tooltip click event.
82-
ev.stopPropagation();
83-
} }
84-
/>
85-
),
86-
},
87-
} ) }
88-
/>
89-
</StyledPill>
90-
);
55+
return null;
9156
}
92-
93-
return null;
57+
return (
58+
<StyledPill>
59+
{ __( 'Has plugin conflict', 'woocommerce-gateway-stripe' ) }
60+
<Popover
61+
BaseComponent={ IconComponent }
62+
content={ interpolateComponents( {
63+
mixedString: sprintf(
64+
/* translators: $1: a payment method name */
65+
__(
66+
'%1$s is unavailable due to another official plugin being active.',
67+
'woocommerce-gateway-stripe'
68+
),
69+
label
70+
),
71+
components: {
72+
currencySettingsLink: (
73+
<StyledLink
74+
href="/wp-admin/admin.php?page=wc-settings&tab=general"
75+
target="_blank"
76+
rel="noreferrer"
77+
onClick={ ( ev ) => {
78+
// Stop propagation is necessary so it doesn't trigger the tooltip click event.
79+
ev.stopPropagation();
80+
} }
81+
/>
82+
),
83+
},
84+
} ) }
85+
/>
86+
</StyledPill>
87+
);
9488
};
9589

9690
export default PaymentMethodUnavailableDueConflictPill;

0 commit comments

Comments
 (0)