Skip to content

Commit 87802e2

Browse files
authored
Merge branch 'add/settings-synchronization' into add/hook-up-enabled-payment-methods
2 parents 73e0690 + 581556a commit 87802e2

File tree

87 files changed

+1358
-640
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+1358
-640
lines changed

assets/images/alipay.svg

+3-10
Loading

assets/images/bank-debit.svg

+3-1
Loading

assets/images/stripe.svg

+1
Loading

changelog.txt

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,30 @@
11
*** Changelog ***
22

33
= 9.4.0 - xxxx-xx-xx =
4+
* Dev - Implements the new Stripe order class into the express checkout classes.
5+
* Dev - Implements the new Stripe order class into the wp-admin related classes.
6+
* Dev - Replaces references to order status values with their respective constants from the WooCommerce plugin.
7+
* Tweak - Updates the Smart Checkout (classic/shortcode checkout version) to make all the payment methods look as similar as possible to any other WooCommerce payment method.
8+
* Tweak - Updates the Smart Checkout (block checkout version) to make all the payment methods look as similar as possible to any other WooCommerce payment method.
9+
* Fix - Improves the subscriptions detached admin notice, making it less intrusive and limiting the querying to 5 subscriptions (avoiding slow loading times).
10+
* Dev - Implements the new Stripe order class into the PHP unit tests.
411
* Dev - Introduces new payment method constants for the express methods: Google Pay, Apple Pay, Link, and Amazon Pay (backend version).
512
* Dev - Introduces a new Stripe Order class to wrap Stripe-specific logic and data on the backend.
613
* Dev - Improves how we handle express payment method titles by introducing new constants and methods to replace duplicate code.
714
* Fix - Fixes an issue where the order signature retrieval method could throw a fatal error when the received order parameter is actually an OrderRefund object (instead of a WC_Order).
815
* Fix - Fixes a possible fatal error when a product added to the cart cannot be found (with Payment Request Buttons).
916
* Add - Add Amazon Pay payment method class.
17+
* Add - Implements the Single Payment Element feature for the new checkout experience on the classic/shortcode checkout page.
1018
* Tweak - Record a Tracks event when enabling/disabling SPE
1119
* Tweak - Updates the Single Payment Element setting copy. Now it is labeled "Smart Checkout".
1220
* Update - Enable/disable Amazon Pay by adding/removing it from the enabled payment methods list.
1321
* Add - Add ACSS payment tokenization.
22+
* Fix - Prevent reuse of payment intents when order total doesn't match intent amount.
1423
* Update - Update payment method type for Amazon Pay orders.
15-
* Dev - Fetch Stripe settings with Stripe configuration API.
24+
* Fix - Compatibility with email preview in the Auth Requested email
25+
* Update - Update Alipay and bank debit icons.
26+
* Tweak - Update payment method type check for charge.succeeded webhook.
27+
* Add - Disable unsupported payment methods in Stripe settings
1628
* Dev - Fetch Stripe settings with Stripe configuration API.
1729
* Add - Hook up payment methods configuration.
1830

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { applySinglePaymentElementStyles } from 'wcstripe/blocks/upe/apply-single-payment-element-styles';
2+
3+
describe( 'applySinglePaymentElementStyles', () => {
4+
it( 'Correctly apply the required styles to HTML elements', () => {
5+
document.body.innerHTML = `
6+
<label class="wc-block-components-radio-control__option">
7+
<input type="radio" name="radio-control-wc-payment-method-options" value="stripe" />
8+
</label>
9+
<div id="radio-control-wc-payment-method-options-stripe__content"></div>
10+
<div id="radio-control-wc-payment-method-options-stripe__label"></div>
11+
<div class="wcstripe-payment-element">
12+
<iframe></iframe>
13+
</div>
14+
`;
15+
16+
applySinglePaymentElementStyles();
17+
18+
const paymentMethodOptions = document.querySelectorAll(
19+
'input[name=radio-control-wc-payment-method-options]'
20+
);
21+
expect( paymentMethodOptions.length ).toBe( 1 );
22+
23+
const paymentMethodStripeOption = document.querySelector(
24+
'.wc-block-components-radio-control__option'
25+
);
26+
expect( paymentMethodStripeOption.style.display ).toBe( 'none' );
27+
28+
const stripeContent = document.getElementById(
29+
'radio-control-wc-payment-method-options-stripe__content'
30+
);
31+
expect(
32+
stripeContent.classList.contains( 'single-payment-element' )
33+
).toBe( true );
34+
35+
const stripeLabel = document.getElementById(
36+
'radio-control-wc-payment-method-options-stripe__label'
37+
);
38+
expect(
39+
stripeLabel.classList.contains( 'single-payment-element' )
40+
).toBe( true );
41+
42+
const stripeIframe = document.querySelector(
43+
'.wcstripe-payment-element iframe'
44+
);
45+
expect( stripeIframe.style.margin ).toBe( '0px' );
46+
} );
47+
} );
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
export const applySinglePaymentElementStyles = () => {
2+
// Hide the Stripe radio button when it is the only available payment method.
3+
const paymentMethodOptions = document.querySelectorAll(
4+
'input[name=radio-control-wc-payment-method-options]'
5+
);
6+
if ( paymentMethodOptions.length === 1 ) {
7+
document.querySelector(
8+
'.wc-block-components-radio-control__option'
9+
).style.display = 'none';
10+
}
11+
12+
// Add the single payment element class to the Stripe payment method elements.
13+
document
14+
.getElementById(
15+
'radio-control-wc-payment-method-options-stripe__content'
16+
)
17+
.classList.add( 'single-payment-element' );
18+
document
19+
.getElementById(
20+
'radio-control-wc-payment-method-options-stripe__label'
21+
)
22+
.classList.add( 'single-payment-element' );
23+
24+
// Style the Stripe iframe to remove the margin and set width to 100%.
25+
const stripeIframe = document.querySelector(
26+
'.wcstripe-payment-element iframe'
27+
);
28+
stripeIframe.style.margin = 0;
29+
stripeIframe.style.width = '100%';
30+
};

client/blocks/upe/styles.scss

+13-1
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,23 @@ button.stripe-gateway-stripelink-modal-trigger:hover {
1515
border-color: transparent;
1616
}
1717
.wc-block-checkout__payment-method .wc-block-components-radio-control__label > span {
18-
width:95%;
18+
width: 100%;
1919
}
2020
.wc-block-checkout__payment-method .wc-block-components-radio-control__label > span > span {
2121
float:right;
2222
display: flex;
2323
align-items: center;
2424
justify-content: center;
2525
}
26+
/* stylelint-disable selector-id-pattern */
27+
#radio-control-wc-payment-method-options-stripe__content.single-payment-element {
28+
padding: 0;
29+
30+
.content {
31+
display: none;
32+
}
33+
}
34+
#radio-control-wc-payment-method-options-stripe__label > span > span {
35+
border: none;
36+
}
37+
/* stylelint-enable selector-id-pattern */

client/blocks/upe/upe-deferred-intent-creation/payment-elements.js

+11
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,17 @@ const PaymentElements = ( {
133133
};
134134

135135
if ( getBlocksConfiguration()?.isSPEEnabled ) {
136+
options.appearance.rules = {
137+
...options.appearance?.rules,
138+
...{
139+
'.RadioIcon': {
140+
width: '2.1em',
141+
},
142+
'.RadioIconOuter': {
143+
strokeWidth: '2px',
144+
},
145+
},
146+
};
136147
options = {
137148
...options,
138149
...{

client/blocks/upe/upe-deferred-intent-creation/payment-processor.js

+19-5
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,14 @@ import {
2121
removeCashAppLimitNotice,
2222
} from 'wcstripe/stripe-utils/cash-app-limit-notice-handler';
2323
import { isLinkEnabled } from 'wcstripe/stripe-utils';
24-
import { PAYMENT_METHOD_CASHAPP } from 'wcstripe/stripe-utils/constants';
24+
import {
25+
PAYMENT_METHOD_CARD,
26+
PAYMENT_METHOD_CASHAPP,
27+
} from 'wcstripe/stripe-utils/constants';
28+
import { applySinglePaymentElementStyles } from 'wcstripe/blocks/upe/apply-single-payment-element-styles';
2529

2630
const noop = () => null;
31+
2732
/**
2833
* Gets the Stripe element options.
2934
*
@@ -137,9 +142,11 @@ const PaymentProcessor = ( {
137142
const [ isPaymentElementComplete, setIsPaymentElementComplete ] = useState(
138143
false
139144
);
140-
const testingInstructionsIfAppropriate = getBlocksConfiguration()?.testMode
141-
? testingInstructions
142-
: '';
145+
const testingInstructionsIfAppropriate =
146+
getBlocksConfiguration()?.testMode &&
147+
! getBlocksConfiguration()?.isSPEEnabled // @todo Temporary disabling testing instructions for SPE.
148+
? testingInstructions
149+
: '';
143150
const paymentMethodsConfig = getBlocksConfiguration()?.paymentMethodsConfig;
144151
const gatewayConfig = getPaymentMethods()[ upeMethods[ paymentMethodId ] ];
145152

@@ -283,8 +290,8 @@ const PaymentProcessor = ( {
283290
]
284291
);
285292

286-
// Show the Cash App limit notice if the payment method is selected and the cart amount is higher than 2000 USD.
287293
useEffect( () => {
294+
// Show the Cash App limit notice if the payment method is selected and the cart amount is higher than 2000 USD.
288295
if ( selectedPaymentMethodType === PAYMENT_METHOD_CASHAPP ) {
289296
maybeShowCashAppLimitNotice(
290297
'.wc-block-checkout__payment-method .wc-block-components-notices',
@@ -294,6 +301,13 @@ const PaymentProcessor = ( {
294301
} else {
295302
removeCashAppLimitNotice();
296303
}
304+
// Apply single payment element styles if the selected payment method is card and SPE is enabled.
305+
if (
306+
selectedPaymentMethodType === PAYMENT_METHOD_CARD &&
307+
getBlocksConfiguration()?.isSPEEnabled
308+
) {
309+
applySinglePaymentElementStyles();
310+
}
297311
}, [ selectedPaymentMethodType ] );
298312

299313
usePaymentCompleteHandler(

client/blocks/upe/upe-element.js

+26-13
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,7 @@ const upeMethods = getPaymentMethodsConstants();
2727
* @return {Object} The UPE payment method configuration.
2828
*/
2929
export const upeElement = ( paymentMethod, api, upeConfig ) => {
30-
let iconName = paymentMethod;
31-
32-
// Afterpay/Clearpay have different icons for UK merchants.
33-
if ( paymentMethod === PAYMENT_METHOD_AFTERPAY_CLEARPAY ) {
34-
iconName =
35-
getBlocksConfiguration()?.accountCountry === 'GB'
36-
? PAYMENT_METHOD_CLEARPAY
37-
: PAYMENT_METHOD_AFTERPAY;
38-
}
39-
40-
// Use checkout icons if available, otherwise fallback to default Icons
41-
const Icon =
42-
( checkoutIcons && checkoutIcons[ iconName ] ) || Icons[ iconName ];
30+
const Icon = getUpeElementIcon( paymentMethod );
4331
const supports = {
4432
// Use `false` as fallback values in case server provided configuration is missing.
4533
showSavedCards: getBlocksConfiguration()?.showSavedCards ?? false,
@@ -105,3 +93,28 @@ export const upeElement = ( paymentMethod, api, upeConfig ) => {
10593
supports,
10694
};
10795
};
96+
97+
/**
98+
* Returns the icon for the UPE payment method.
99+
*
100+
* @param {string} paymentMethod The payment method name.
101+
* @return {JSX.Element} The icon element.
102+
*/
103+
const getUpeElementIcon = ( paymentMethod ) => {
104+
let iconName = paymentMethod;
105+
106+
if ( getBlocksConfiguration()?.isSPEEnabled ) {
107+
iconName = 'stripe';
108+
}
109+
110+
// Afterpay/Clearpay have different icons for UK merchants.
111+
if ( paymentMethod === PAYMENT_METHOD_AFTERPAY_CLEARPAY ) {
112+
iconName =
113+
getBlocksConfiguration()?.accountCountry === 'GB'
114+
? PAYMENT_METHOD_CLEARPAY
115+
: PAYMENT_METHOD_AFTERPAY;
116+
}
117+
118+
// Use checkout icons if available, otherwise fallback to default Icons
119+
return ( checkoutIcons && checkoutIcons[ iconName ] ) || Icons[ iconName ];
120+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { applySinglePaymentElementStyles } from 'wcstripe/classic/upe/apply-single-payment-element-styles';
2+
3+
describe( 'applySinglePaymentElementStyles', () => {
4+
it( 'Correctly apply the required styles to HTML elements', () => {
5+
document.body.innerHTML = `
6+
<input type="radio" name="payment_method" value="stripe" />
7+
<label for="payment_method_stripe">Stripe</label>
8+
`;
9+
10+
applySinglePaymentElementStyles();
11+
12+
const stripeLabel = document.querySelector(
13+
'label[for=payment_method_stripe]'
14+
);
15+
expect( stripeLabel.style.display ).toBe( 'none' );
16+
} );
17+
} );
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export const applySinglePaymentElementStyles = () => {
2+
// Hide the Stripe radio button when it is the only available payment method.
3+
const paymentMethodOptions = document.querySelectorAll(
4+
'input[name=payment_method]'
5+
);
6+
if ( paymentMethodOptions.length === 1 ) {
7+
document.querySelector(
8+
'label[for=payment_method_stripe]'
9+
).style.display = 'none';
10+
}
11+
};

client/classic/upe/deferred-intent.js

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
mountStripePaymentElement,
1818
processPayment,
1919
} from './payment-processing';
20+
import { applySinglePaymentElementStyles } from 'wcstripe/classic/upe/apply-single-payment-element-styles';
2021

2122
jQuery( function ( $ ) {
2223
// Create an API object, which will be used throughout the checkout.
@@ -114,6 +115,10 @@ jQuery( function ( $ ) {
114115

115116
await mountStripePaymentElement( api, upeElement );
116117
}
118+
119+
if ( getStripeServerData()?.isSPEEnabled ) {
120+
applySinglePaymentElementStyles();
121+
}
117122
}
118123

119124
function restrictPaymentMethodToLocation( upeElement ) {

client/classic/upe/payment-processing.js

+21-7
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ async function createStripePaymentElement( api, paymentMethodType ) {
8585
paymentMethodsConfig[ paymentMethodType ] || {};
8686
let intent, options;
8787

88+
options = {
89+
appearance: initializeUPEAppearance( api ),
90+
paymentMethodCreation: 'manual',
91+
fonts: getFontRulesFromPage(),
92+
};
93+
8894
// If the payment method doesn't support deferred intent, the intent must be created here.
8995
if ( ! supportsDeferredIntent ) {
9096
try {
@@ -119,24 +125,31 @@ async function createStripePaymentElement( api, paymentMethodType ) {
119125
gatewayUPEComponents[ paymentMethodType ].intentId = intent.id;
120126

121127
options = {
122-
appearance: initializeUPEAppearance( api ),
123-
paymentMethodCreation: 'manual',
124-
fonts: getFontRulesFromPage(),
128+
...options,
125129
clientSecret: intent.client_secret,
126130
};
127131
} else {
128132
const amount = Number( getStripeServerData()?.cartTotal );
129133
const paymentMethodTypes = getPaymentMethodTypes( paymentMethodType );
130134

131135
options = {
136+
...options,
132137
mode: amount < 1 ? 'setup' : 'payment',
133138
currency: getStripeServerData()?.currency.toLowerCase(),
134139
amount,
135-
paymentMethodCreation: 'manual',
136-
paymentMethodTypes,
137-
appearance: initializeUPEAppearance( api ),
138-
fonts: getFontRulesFromPage(),
139140
};
141+
142+
if ( getStripeServerData()?.isSPEEnabled ) {
143+
options = {
144+
...options,
145+
paymentMethodConfiguration: 'pmc_...',
146+
};
147+
} else {
148+
options = {
149+
...options,
150+
paymentMethodTypes,
151+
};
152+
}
140153
}
141154

142155
const elements = api.getStripe().elements( options );
@@ -156,6 +169,7 @@ async function createStripePaymentElement( api, paymentMethodType ) {
156169
applePay: 'never',
157170
googlePay: 'never',
158171
},
172+
layout: 'accordion',
159173
} );
160174

161175
gatewayUPEComponents[ paymentMethodType ].elements = elements;

0 commit comments

Comments
 (0)