Skip to content

Commit

Permalink
Merge branch 'develop' into fix/render-chekout-scripts-on-shipping-ch…
Browse files Browse the repository at this point in the history
…ange
  • Loading branch information
zmaglica authored Dec 18, 2024
2 parents 8cde574 + 99db1fb commit 89c3d58
Show file tree
Hide file tree
Showing 18 changed files with 428 additions and 80 deletions.
4 changes: 4 additions & 0 deletions changelog/fix-9716-disputes-api-requests
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: fix

Performance improvements for Disputes Needing Response task shown in WooCommerce admin.
4 changes: 4 additions & 0 deletions changelog/fix-9794-refresh-page-when-ece-dismissed
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: add

Refresh the cart and checkout pages when ECE is dismissed and the shipping options were modified in the payment sheet.
5 changes: 5 additions & 0 deletions changelog/fix-tokenized-cart-multiple-variations
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Significance: patch
Type: fix
Comment: fix: tokenized cart & multiple variations.


4 changes: 4 additions & 0 deletions changelog/fix-unhandled-promises
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: fix

Fix payment method filtering when billing country changes in Blocks checkout.
8 changes: 2 additions & 6 deletions client/express-checkout/blocks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ const expressCheckoutElementApplePay = ( api ) => ( {
return false;
}

return new Promise( ( resolve ) => {
checkPaymentMethodIsAvailable( 'applePay', cart, resolve );
} );
return checkPaymentMethodIsAvailable( 'applePay', cart );
},
} );

Expand Down Expand Up @@ -77,9 +75,7 @@ const expressCheckoutElementGooglePay = ( api ) => {
return false;
}

return new Promise( ( resolve ) => {
checkPaymentMethodIsAvailable( 'googlePay', cart, resolve );
} );
return checkPaymentMethodIsAvailable( 'googlePay', cart );
},
};
};
Expand Down
10 changes: 10 additions & 0 deletions client/express-checkout/event-handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ import {
normalizeShippingAddress,
normalizeLineItems,
getExpressCheckoutData,
updateShippingAddressUI,
} from './utils';
import {
trackExpressCheckoutButtonClick,
trackExpressCheckoutButtonLoad,
} from './tracking';

let lastSelectedAddress = null;

export const shippingAddressChangeHandler = async ( api, event, elements ) => {
try {
const response = await api.expressCheckoutECECalculateShippingOptions(
Expand All @@ -29,6 +32,9 @@ export const shippingAddressChangeHandler = async ( api, event, elements ) => {
elements.update( {
amount: response.total.amount,
} );

lastSelectedAddress = event.address;

event.resolve( {
shippingRates: response.shipping_options,
lineItems: normalizeLineItems( response.displayItems ),
Expand Down Expand Up @@ -171,5 +177,9 @@ export const onCompletePaymentHandler = () => {
};

export const onCancelHandler = () => {
if ( lastSelectedAddress ) {
updateShippingAddressUI( lastSelectedAddress );
}
lastSelectedAddress = null;
unblockUI();
};
122 changes: 63 additions & 59 deletions client/express-checkout/utils/checkPaymentMethodIsAvailable.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,71 +14,75 @@ import WCPayAPI from 'wcpay/checkout/api';
import { getUPEConfig } from 'wcpay/utils/checkout';

export const checkPaymentMethodIsAvailable = memoize(
( paymentMethod, cart, resolve ) => {
// Create the DIV container on the fly
const containerEl = document.createElement( 'div' );
( paymentMethod, cart ) => {
return new Promise( ( resolve ) => {
// Create the DIV container on the fly
const containerEl = document.createElement( 'div' );

// Ensure the element is hidden and doesn’t interfere with the page layout.
containerEl.style.display = 'none';
// Ensure the element is hidden and doesn’t interfere with the page layout.
containerEl.style.display = 'none';

document.querySelector( 'body' ).appendChild( containerEl );
document.querySelector( 'body' ).appendChild( containerEl );

const root = ReactDOM.createRoot( containerEl );
const root = ReactDOM.createRoot( containerEl );

const api = new WCPayAPI(
{
publishableKey: getUPEConfig( 'publishableKey' ),
accountId: getUPEConfig( 'accountId' ),
forceNetworkSavedCards: getUPEConfig(
'forceNetworkSavedCards'
),
locale: getUPEConfig( 'locale' ),
isStripeLinkEnabled: isLinkEnabled(
getUPEConfig( 'paymentMethodsConfig' )
),
},
request
);
const api = new WCPayAPI(
{
publishableKey: getUPEConfig( 'publishableKey' ),
accountId: getUPEConfig( 'accountId' ),
forceNetworkSavedCards: getUPEConfig(
'forceNetworkSavedCards'
),
locale: getUPEConfig( 'locale' ),
isStripeLinkEnabled: isLinkEnabled(
getUPEConfig( 'paymentMethodsConfig' )
),
},
request
);

root.render(
<Elements
stripe={ api.loadStripeForExpressCheckout() }
options={ {
mode: 'payment',
paymentMethodCreation: 'manual',
amount: Number( cart.cartTotals.total_price ),
currency: cart.cartTotals.currency_code.toLowerCase(),
} }
>
<ExpressCheckoutElement
onLoadError={ () => resolve( false ) }
root.render(
<Elements
stripe={ api.loadStripeForExpressCheckout() }
options={ {
paymentMethods: {
amazonPay: 'never',
applePay:
paymentMethod === 'applePay'
? 'always'
: 'never',
googlePay:
paymentMethod === 'googlePay'
? 'always'
: 'never',
link: 'never',
paypal: 'never',
},
mode: 'payment',
paymentMethodCreation: 'manual',
amount: Number( cart.cartTotals.total_price ),
currency: cart.cartTotals.currency_code.toLowerCase(),
} }
onReady={ ( event ) => {
let canMakePayment = false;
if ( event.availablePaymentMethods ) {
canMakePayment =
event.availablePaymentMethods[ paymentMethod ];
}
resolve( canMakePayment );
root.unmount();
containerEl.remove();
} }
/>
</Elements>
);
>
<ExpressCheckoutElement
onLoadError={ () => resolve( false ) }
options={ {
paymentMethods: {
amazonPay: 'never',
applePay:
paymentMethod === 'applePay'
? 'always'
: 'never',
googlePay:
paymentMethod === 'googlePay'
? 'always'
: 'never',
link: 'never',
paypal: 'never',
},
} }
onReady={ ( event ) => {
let canMakePayment = false;
if ( event.availablePaymentMethods ) {
canMakePayment =
event.availablePaymentMethods[
paymentMethod
];
}
resolve( canMakePayment );
root.unmount();
containerEl.remove();
} }
/>
</Elements>
);
} );
}
);
1 change: 1 addition & 0 deletions client/express-checkout/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Internal dependencies
*/
export * from './normalize';
export * from './shipping-fields';
import { getDefaultBorderRadius } from 'wcpay/utils/express-checkout';

interface MyWindow extends Window {
Expand Down
131 changes: 131 additions & 0 deletions client/express-checkout/utils/shipping-fields.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/* global jQuery */
/**
* Internal dependencies
*/
import { normalizeShippingAddress, getExpressCheckoutData } from '.';

/**
* Checks if the intermediate address is redacted for the given country.
* CA and GB addresses are redacted and are causing errors until WooCommerce is able to
* handle redacted addresses.
* https://developers.google.com/pay/api/web/reference/response-objects#IntermediateAddress
*
* @param {string} country - The country code.
*
* @return {boolean} True if the postcode is redacted for the country, false otherwise.
*/
const isPostcodeRedactedForCountry = ( country ) => {
return [ 'CA', 'GB' ].includes( country );
};

/*
* Updates a field in a form with a new value.
*
* @param {String} formSelector - The selector for the form containing the field.
* @param {Object} fieldName - The name of the field to update.
* @param {Object} value - The new value for the field.
*/
const updateShortcodeField = ( formSelector, fieldName, value ) => {
const field = document.querySelector(
`${ formSelector } [name="${ fieldName }"]`
);

if ( ! field ) return;

// Check if the field is a dropdown (country/state).
if ( field.tagName === 'SELECT' && /country|state/.test( fieldName ) ) {
const options = Array.from( field.options );
const match = options.find(
( opt ) =>
opt.value === value ||
opt.textContent.trim().toLowerCase() === value.toLowerCase()
);

if ( match ) {
field.value = match.value;
jQuery( field ).trigger( 'change' ).trigger( 'close' );
}
} else {
// Default behavior for text inputs.
field.value = value;
jQuery( field ).trigger( 'change' );
}
};

/**
* Updates the WooCommerce Blocks shipping UI to reflect a new shipping address.
*
* @param {Object} eventAddress - The shipping address returned by the payment event.
*/
const updateBlocksShippingUI = ( eventAddress ) => {
wp?.data
?.dispatch( 'wc/store/cart' )
?.setShippingAddress( normalizeShippingAddress( eventAddress ) );
};

/**
* Updates the WooCommerce shortcode cart/checkout shipping UI to reflect a new shipping address.
*
* @param {Object} eventAddress - The shipping address returned by the payment event.
*/
const updateShortcodeShippingUI = ( eventAddress ) => {
const context = getExpressCheckoutData( 'button_context' );
const address = normalizeShippingAddress( eventAddress );

const keys = [ 'country', 'state', 'city', 'postcode' ];

if ( context === 'cart' ) {
keys.forEach( ( key ) => {
if ( address[ key ] ) {
updateShortcodeField(
'form.woocommerce-shipping-calculator',
`calc_shipping_${ key }`,
address[ key ]
);
}
} );
document
.querySelector(
'form.woocommerce-shipping-calculator [name="calc_shipping"]'
)
?.click();
} else if ( context === 'checkout' ) {
keys.forEach( ( key ) => {
if ( address[ key ] ) {
updateShortcodeField(
'form.woocommerce-checkout',
`billing_${ key }`,
address[ key ]
);
}
} );
}
};

/**
* Updates the WooCommerce shipping UI to reflect a new shipping address.
*
* Determines the current context (cart or checkout) and updates either
* WooCommerce Blocks or shortcode-based shipping forms, if applicable.
*
* @param {Object} newAddress - The new shipping address object returned by the payment event.
* @param {string} newAddress.country - The country code of the shipping address.
* @param {string} [newAddress.state] - The state/province of the shipping address.
* @param {string} [newAddress.city] - The city of the shipping address.
* @param {string} [newAddress.postcode] - The postal/ZIP code of the shipping address.
*/
export const updateShippingAddressUI = ( newAddress ) => {
const context = getExpressCheckoutData( 'button_context' );
const isBlocks = getExpressCheckoutData( 'has_block' );

if (
[ 'cart', 'checkout' ].includes( context ) &&
! isPostcodeRedactedForCountry( newAddress.country )
) {
if ( isBlocks ) {
updateBlocksShippingUI( newAddress );
} else {
updateShortcodeShippingUI( newAddress );
}
}
};
14 changes: 13 additions & 1 deletion client/tokenized-express-checkout/event-handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import { applyFilters } from '@wordpress/hooks';
/**
* Internal dependencies
*/
import { getErrorMessageFromNotice, getExpressCheckoutData } from './utils';
import {
getErrorMessageFromNotice,
getExpressCheckoutData,
updateShippingAddressUI,
} from './utils';
import {
trackExpressCheckoutButtonClick,
trackExpressCheckoutButtonLoad,
Expand All @@ -24,6 +28,7 @@ import {
transformPrice,
} from './transformers/wc-to-stripe';

let lastSelectedAddress = null;
let cartApi = new ExpressCheckoutCartApi();
export const setCartApiHandler = ( handler ) => ( cartApi = handler );
export const getCartApiHandler = () => cartApi;
Expand Down Expand Up @@ -56,6 +61,9 @@ export const shippingAddressChangeHandler = async ( event, elements ) => {
cartData.totals
),
} );

lastSelectedAddress = event.address;

event.resolve( {
shippingRates: transformCartDataForShippingRates( cartData ),
lineItems: transformCartDataForDisplayItems( cartData ),
Expand Down Expand Up @@ -216,5 +224,9 @@ export const onCompletePaymentHandler = () => {
};

export const onCancelHandler = () => {
if ( lastSelectedAddress ) {
updateShippingAddressUI( lastSelectedAddress );
}
lastSelectedAddress = null;
unblockUI();
};
Loading

0 comments on commit 89c3d58

Please sign in to comment.