Skip to content

Commit 6ae72f2

Browse files
committed
Merge remote-tracking branch 'origin/release/8.8.1' into trunk
2 parents 5fbe0b8 + eab2f2a commit 6ae72f2

29 files changed

+453
-179
lines changed

changelog.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
*** Changelog ***
22

3+
= 8.8.1 - 2024-10-28 =
4+
* Tweak - Disables APMs when using the legacy checkout experience due Stripe deprecation by October 29, 2024.
5+
* Fix - Prevent marking orders on-hold with order note "Process order to take payment" when the payment has failed.
6+
* Fix - Prevent subscriptions from being marked as "Pending" when a customer attempts to change their payment method to a declining card.
7+
* Fix - Delay updating the subscription's payment method until after the intent is confirmed when using the new checkout experience.
8+
* Fix - Display a success notice to customers after successfully changing their subscription payment method to a card that required 3DS authentication.
9+
310
= 8.8.0 - 2024-10-17 =
411
* Fix - Update URL and path constants to support use of symlinked plugin.
512
* Tweak - Disable ECE when cart has virtual products and tax is based on customer billing or shipping address.

client/classic/upe/payment-processing.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,14 +405,15 @@ export const confirmVoucherPayment = async ( api, jQueryForm ) => {
405405
*/
406406
export const confirmWalletPayment = async ( api, jQueryForm ) => {
407407
const isOrderPay = getStripeServerData()?.isOrderPay;
408+
const isChangingPayment = getStripeServerData()?.isChangingPayment;
408409

409410
// The Order Pay page does a hard refresh when the hash changes, so we need to block the UI again.
410411
if ( isOrderPay ) {
411412
blockUI( jQueryForm );
412413
}
413414

414415
const partials = window.location.href.match(
415-
/#wc-stripe-wallet-(.+):(.+):(.+):(.+):(.+)$/
416+
/#wc-stripe-wallet-(.+):(.+):(.+):(.+):(.+):(.+)$/
416417
);
417418

418419
if ( ! partials ) {
@@ -495,7 +496,27 @@ export const confirmWalletPayment = async ( api, jQueryForm ) => {
495496
// Do not redirect to the order received page if the modal is closed without payment.
496497
// Otherwise redirect to the order received page.
497498
if ( intentObject.status !== 'requires_action' ) {
498-
window.location.href = returnURL;
499+
if ( ! isChangingPayment ) {
500+
window.location.href = returnURL;
501+
}
502+
503+
// If we're changing a subscription's payment method, there's an extra step needed.
504+
// We need to confirm the change payment intent via the confirm_change_payment AJAX request and then redirect to the return URL.
505+
const response = await api.request(
506+
api.getAjaxUrl( 'confirm_change_payment' ),
507+
{
508+
order_id: orderId,
509+
intent_id: intentObject.id,
510+
payment_method_id: intentObject.payment_method || null,
511+
_ajax_nonce: partials[ 6 ],
512+
}
513+
);
514+
515+
if ( response.success ) {
516+
window.location.href = response.data.return_url;
517+
} else {
518+
throw new Error( response.data.error.message );
519+
}
499520
}
500521
} catch ( error ) {
501522
showErrorCheckout( error.message );
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from 'react';
2+
import { screen, render } from '@testing-library/react';
3+
import PaymentMethodDeprecationPill from '..';
4+
import UpeToggleContext from 'wcstripe/settings/upe-toggle/context';
5+
6+
describe( 'PaymentMethodDeprecationPill', () => {
7+
it( 'should render', () => {
8+
render(
9+
<UpeToggleContext.Provider>
10+
<PaymentMethodDeprecationPill />
11+
</UpeToggleContext.Provider>
12+
);
13+
14+
expect( screen.queryByText( 'Deprecated' ) ).toBeInTheDocument();
15+
} );
16+
} );
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { __ } from '@wordpress/i18n';
2+
import React from 'react';
3+
import styled from '@emotion/styled';
4+
import { Icon, info } from '@wordpress/icons';
5+
import interpolateComponents from 'interpolate-components';
6+
import Pill from 'wcstripe/components/pill';
7+
import Popover from 'wcstripe/components/popover';
8+
9+
const StyledPill = styled( Pill )`
10+
display: inline-flex;
11+
align-items: center;
12+
gap: 4px;
13+
padding: 4px 8px;
14+
border: 1px solid #fcf9e8;
15+
border-radius: 2px;
16+
background-color: #fcf9e8;
17+
color: #674600;
18+
font-size: 12px;
19+
font-weight: 400;
20+
line-height: 16px;
21+
width: fit-content;
22+
`;
23+
24+
const StyledLink = styled.a`
25+
&:focus,
26+
&:visited {
27+
box-shadow: none;
28+
}
29+
`;
30+
31+
const IconWrapper = styled.span`
32+
height: 16px;
33+
cursor: pointer;
34+
`;
35+
36+
const AlertIcon = styled( Icon )`
37+
fill: #674600;
38+
`;
39+
40+
const IconComponent = ( { children, ...props } ) => (
41+
<IconWrapper { ...props }>
42+
<AlertIcon icon={ info } size="16" />
43+
{ children }
44+
</IconWrapper>
45+
);
46+
47+
const PaymentMethodDeprecationPill = () => {
48+
return (
49+
<StyledPill>
50+
{ __( 'Deprecated', 'woocommerce-gateway-stripe' ) }
51+
<Popover
52+
BaseComponent={ IconComponent }
53+
content={ interpolateComponents( {
54+
mixedString:
55+
/* translators: $1: a payment method name. %2: Currency(ies). */
56+
__(
57+
'This payment method is deprecated on the {{currencySettingsLink}}legacy checkout as of Oct 29th, 2024{{/currencySettingsLink}}.',
58+
'woocommerce-gateway-stripe'
59+
),
60+
components: {
61+
currencySettingsLink: (
62+
<StyledLink
63+
href="https://support.stripe.com/topics/shutdown-of-the-legacy-sources-api-for-non-card-payment-methods"
64+
target="_blank"
65+
rel="noreferrer"
66+
onClick={ ( ev ) => {
67+
// Stop propagation is necessary so it doesn't trigger the tooltip click event.
68+
ev.stopPropagation();
69+
} }
70+
/>
71+
),
72+
},
73+
} ) }
74+
/>
75+
</StyledPill>
76+
);
77+
};
78+
79+
export default PaymentMethodDeprecationPill;

client/settings/general-settings-section/__tests__/general-settings-section.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ describe( 'GeneralSettingsSection', () => {
4646

4747
beforeEach( () => {
4848
global.wcSettings = { currency: { code: 'EUR' } };
49+
global.wc_stripe_settings_params = { are_apms_deprecated: false };
4950
useGetCapabilities.mockReturnValue( {
5051
card_payments: 'active',
5152
alipay_payments: 'active',

client/settings/general-settings-section/payment-method-checkbox.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@ const IconWrapper = styled.span`
2727
flex-shrink: 0;
2828
`;
2929

30-
const PaymentMethodCheckbox = ( { id, label, isAllowingManualCapture } ) => {
30+
const PaymentMethodCheckbox = ( {
31+
id,
32+
label,
33+
isAllowingManualCapture,
34+
disabled,
35+
} ) => {
3136
const [ isManualCaptureEnabled ] = useManualCapture();
3237
const [ isConfirmationModalOpen, setIsConfirmationModalOpen ] = useState(
3338
false
@@ -40,6 +45,9 @@ const PaymentMethodCheckbox = ( { id, label, isAllowingManualCapture } ) => {
4045
const { isUpeEnabled } = useContext( UpeToggleContext );
4146

4247
const handleCheckboxChange = ( hasBeenChecked ) => {
48+
if ( disabled ) {
49+
return;
50+
}
4351
if ( ! hasBeenChecked ) {
4452
setIsConfirmationModalOpen( true );
4553
return;
@@ -98,7 +106,10 @@ const PaymentMethodCheckbox = ( { id, label, isAllowingManualCapture } ) => {
98106
<StyledCheckbox
99107
label={ <VisuallyHidden>{ label }</VisuallyHidden> }
100108
onChange={ handleCheckboxChange }
101-
checked={ enabledPaymentMethods.includes( id ) }
109+
checked={
110+
disabled ? false : enabledPaymentMethods.includes( id )
111+
}
112+
disabled={ disabled }
102113
/>
103114
) }
104115
{ isConfirmationModalOpen && (

client/settings/general-settings-section/payment-method-description.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import styled from '@emotion/styled';
33
import PaymentMethodMissingCurrencyPill from '../../components/payment-method-missing-currency-pill';
44
import PaymentMethodCapabilityStatusPill from 'wcstripe/components/payment-method-capability-status-pill';
5+
import PaymentMethodDeprecationPill from 'wcstripe/components/payment-method-deprecation-pill';
56

67
const Wrapper = styled.div`
78
display: flex;
@@ -43,6 +44,7 @@ const PaymentMethodDescription = ( {
4344
label,
4445
description,
4546
id,
47+
deprecated,
4648
...restProps
4749
} ) => {
4850
return (
@@ -53,14 +55,19 @@ const PaymentMethodDescription = ( {
5355
<div>
5456
<LabelWrapper>
5557
<Label>{ label }</Label>
56-
<PaymentMethodMissingCurrencyPill
57-
id={ id }
58-
label={ label }
59-
/>
60-
<PaymentMethodCapabilityStatusPill
61-
id={ id }
62-
label={ label }
63-
/>
58+
{ deprecated && <PaymentMethodDeprecationPill /> }
59+
{ ! deprecated && (
60+
<>
61+
<PaymentMethodMissingCurrencyPill
62+
id={ id }
63+
label={ label }
64+
/>
65+
<PaymentMethodCapabilityStatusPill
66+
id={ id }
67+
label={ label }
68+
/>
69+
</>
70+
) }
6471
</LabelWrapper>
6572
<Description>{ description }</Description>
6673
</div>

client/settings/general-settings-section/payment-methods-list.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* global wc_stripe_settings_params */
12
import { __, sprintf } from '@wordpress/i18n';
23
import React, { useState } from 'react';
34
import styled from '@emotion/styled';
@@ -235,6 +236,15 @@ const GeneralSettingsSection = ( {
235236
return null;
236237
}
237238

239+
// Remove APMs (legacy checkout) due deprecation by Stripe on Oct 31st, 2024.
240+
if (
241+
// eslint-disable-next-line camelcase
242+
wc_stripe_settings_params.are_apms_deprecated &&
243+
method !== 'card'
244+
) {
245+
return null;
246+
}
247+
238248
const {
239249
Icon,
240250
label,
@@ -288,6 +298,12 @@ const GeneralSettingsSection = ( {
288298
allows_manual_capture: isAllowingManualCapture,
289299
} = PaymentMethodsMap[ method ];
290300

301+
// Remove APMs (legacy checkout) due deprecation by Stripe on Oct 31st, 2024.
302+
const deprecated =
303+
// eslint-disable-next-line camelcase
304+
wc_stripe_settings_params.are_apms_deprecated &&
305+
method !== 'card';
306+
291307
return (
292308
<div key={ method }>
293309
<ListElement
@@ -305,6 +321,7 @@ const GeneralSettingsSection = ( {
305321
isAllowingManualCapture={
306322
isAllowingManualCapture
307323
}
324+
disabled={ deprecated }
308325
/>
309326
<PaymentMethodWrapper>
310327
<PaymentMethodDescription
@@ -315,6 +332,7 @@ const GeneralSettingsSection = ( {
315332
data.account?.default_currency
316333
) }
317334
label={ label }
335+
deprecated={ deprecated }
318336
/>
319337
<StyledFees id={ method } />
320338
</PaymentMethodWrapper>
@@ -327,6 +345,7 @@ const GeneralSettingsSection = ( {
327345
[ method ]: true,
328346
} )
329347
}
348+
disabled={ deprecated }
330349
>
331350
{ __(
332351
'Customize',

client/settings/payment-settings/__tests__/promotional-banner-section.test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ useDispatch.mockImplementation( ( storeName ) => {
3232
const setShowPromotionalBanner = jest.fn();
3333

3434
describe( 'PromotionalBanner', () => {
35+
beforeEach( () => {
36+
global.wc_stripe_settings_params = { are_apms_deprecated: false };
37+
} );
38+
3539
it( 'dismiss function should be called', () => {
3640
render(
3741
<PromotionalBannerSection

client/settings/payment-settings/promotional-banner-section.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* global wc_stripe_settings_params */
12
import { __ } from '@wordpress/i18n';
23
import { useDispatch } from '@wordpress/data';
34
import { React } from 'react';
@@ -163,6 +164,20 @@ const PromotionalBannerSection = ( {
163164
</CardBody>
164165
);
165166

167+
let newCheckoutExperienceAPMsBannerDescription = '';
168+
// eslint-disable-next-line camelcase
169+
if ( wc_stripe_settings_params.are_apms_deprecated ) {
170+
newCheckoutExperienceAPMsBannerDescription = __(
171+
'Stripe ended support for non-card payment methods in the {{StripeLegacyLink}}legacy checkout on October 29, 2024{{/StripeLegacyLink}}. To continue accepting non-card payments, you must enable the new checkout experience or remove non-card payment methods from your checkout to avoid payment disruptions.',
172+
'woocommerce-gateway-stripe'
173+
);
174+
} else {
175+
newCheckoutExperienceAPMsBannerDescription = __(
176+
'Stripe will end support for non-card payment methods in the {{StripeLegacyLink}}legacy checkout on October 29, 2024{{/StripeLegacyLink}}. To continue accepting non-card payments, you must enable the new checkout experience or remove non-card payment methods from your checkout to avoid payment disruptions.',
177+
'woocommerce-gateway-stripe'
178+
);
179+
}
180+
166181
const NewCheckoutExperienceAPMsBanner = () => (
167182
<CardBody data-testid="new-checkout-apms-banner">
168183
<CardInner>
@@ -178,10 +193,7 @@ const PromotionalBannerSection = ( {
178193
</h4>
179194
<p>
180195
{ interpolateComponents( {
181-
mixedString: __(
182-
'Stripe will end support for non-card payment methods in the {{StripeLegacyLink}}legacy checkout on October 29, 2024{{/StripeLegacyLink}}. To continue accepting non-card payments, you must enable the new checkout experience or remove non-card payment methods from your checkout to avoid payment disruptions.',
183-
'woocommerce-gateway-stripe'
184-
),
196+
mixedString: newCheckoutExperienceAPMsBannerDescription,
185197
components: {
186198
StripeLegacyLink: (
187199
<ExternalLink href="https://support.stripe.com/topics/shutdown-of-the-legacy-sources-api-for-non-card-payment-methods" />

0 commit comments

Comments
 (0)