Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8aaf2e8
bump npm stripe js versions
Mayisha Feb 10, 2026
5ca030a
update stripe version to clover in registered script
Mayisha Feb 10, 2026
db550e8
remove apiVersion
Mayisha Feb 10, 2026
18d8230
senf flag to frontend
Mayisha Feb 10, 2026
10bb628
update stripe API version
Mayisha Feb 11, 2026
5077aee
update getDefaultValues function for checkout session
Mayisha Feb 11, 2026
2f210d9
show adaptive pricing option in classic checkout
Mayisha Feb 11, 2026
c088e36
attch currency selector div
Mayisha Feb 11, 2026
afeda2c
add changelog
Mayisha Feb 11, 2026
4f39d36
Merge branch 'develop' into add/adaptive-pricing-in-classic-checkout-2
Mayisha Feb 11, 2026
5ab6773
use clover version
Mayisha Feb 11, 2026
128c7d1
fix element loading error
Mayisha Feb 11, 2026
9c8dfeb
update flag name
Mayisha Feb 13, 2026
b6eaf7f
update currency selector element id
Mayisha Feb 13, 2026
a8c057f
remove email from default value update
Mayisha Feb 13, 2026
1a8f2f0
Merge branch 'develop' into add/adaptive-pricing-in-classic-checkout-2
Mayisha Feb 16, 2026
e896712
add chech for ff before attaching the currency selector div
Mayisha Feb 16, 2026
3d28eae
check if currency selector exists
Mayisha Feb 16, 2026
9260764
add check for clientSecret
Mayisha Feb 16, 2026
c213532
pass correct arg to function
Mayisha Feb 16, 2026
ce58091
fix error check
Mayisha Feb 20, 2026
d906064
Merge branch 'develop' into add/adaptive-pricing-in-classic-checkout-2
Mayisha Feb 20, 2026
2ce1a79
Merge branch 'develop' into add/adaptive-pricing-in-classic-checkout-2
Mayisha Feb 25, 2026
7fe6067
add deferred intent check
Mayisha Feb 25, 2026
98c1fea
re-add mistakenly removed comment
Mayisha Feb 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* Tweak - Improve PHPDoc for migration and notes; minor notes refactor
* Tweak - Update PHPDoc for express checkout classes, block support class, and intent controller
* Tweak - Update PHPDoc for UPE payment method classes
* Add - Display adaptive pricing currency selector on classic checkout page
* Dev - Fix WC beta version resolution in tests
* Tweak - Update PHPDoc and fix minor issues for subscriptions and pre-order compatibility
* Dev - Upgrade @stripe/react-stripe-js to ^5.4.1 and @stripe/stripe-js to ^8.6.0 in JavaScript dependencies
Expand Down
1 change: 0 additions & 1 deletion client/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ export default class WCStripeAPI {
createStripe( key, locale, betas = [] ) {
const options = {
locale,
apiVersion: this.options.apiVersion,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to double-check, is this removal intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional. As we are using const STRIPE_API_VERSION = '2025-09-30.clover'; now, we do not need to pass the apiVersion here anymore. Also this param is unsupported when we are using the clover version.

};

if ( betas.length ) {
Expand Down
117 changes: 99 additions & 18 deletions client/classic/upe/payment-processing.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import { handleDisplayOfSavingCheckbox } from 'wcstripe/optimized-checkout/handl

const gatewayUPEComponents = {};
const paymentMethodsConfig = getStripeServerData()?.paymentMethodsConfig;
const isAdaptivePricingSupported =
getStripeServerData()?.isAdaptivePricingSupported;

/**
* Initialize the UPE components for each payment method type.
Expand Down Expand Up @@ -81,24 +83,21 @@ export function validateElements( elements ) {

/**
* Updates the payment element's default values.
*
* @param {boolean} forCheckoutSession Whether the default values are for a Checkout Session.
*/
function updatePaymentElementDefaultValues() {
function updatePaymentElementDefaultValues( forCheckoutSession = false ) {
if ( ! gatewayUPEComponents?.card?.upeElement ) {
return;
}

const paymentElement = gatewayUPEComponents.card.upeElement;
paymentElement.update( getDefaultValues() );
paymentElement.update( getDefaultValues( forCheckoutSession ) );
}

/**
* Creates a Stripe payment element with the specified payment method type and options.
*
* If the payment method doesn't support deferred intent, the intent must be created first.
* Then, the payment element is created with the intent's client secret.
*
* Finally, the payment element is mounted and attached to the gatewayUPEComponents object.
*
* @param {Object} api The API object used to create the Stripe payment element.
* @param {string} paymentMethodType The type of Stripe payment method to create.
* @return {Object} A promise that resolves with the created Stripe payment element.
Expand Down Expand Up @@ -190,19 +189,65 @@ async function createStripePaymentElement( api, paymentMethodType ) {
}
}

const elements = api.getStripe().elements( options );
let elements;
let shouldLoadStripeElements = ! isAdaptivePricingSupported;
// If Adaptive Pricing is enabled, use the Checkout Session API to load the elements.
if ( isAdaptivePricingSupported ) {
try {
const response = await api.checkoutSessionsCreateSession();
const clientSecret = response.data?.client_secret;

if ( ! clientSecret ) {
throw new Error(
__(
'Failed to load payment method due to missing client secret.',
'woocommerce-gateway-stripe'
)
);
}

const attachDefaultValuesUpdateEvent = ( element ) => {
elements = await api.getStripe().initCheckout( {
clientSecret,
elementsOptions: {
appearance: options.appearance,
fonts: options.fonts,
},
adaptivePricing: {
allowed: true,
},
...getDefaultValues( true ),
} );

if ( elements.error ) {
throw elements.error;
}

shouldLoadStripeElements = false;
} catch ( error ) {
// eslint-disable-next-line no-console
console.error( error );
shouldLoadStripeElements = true;
}
}

// If Adaptive Pricing is not enabled, or if there was an error loading the AP elements,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice idea!

// load the Stripe elements as fallback.
if ( shouldLoadStripeElements ) {
elements = api.getStripe().elements( options );
}

const attachDefaultValuesUpdateEvent = (
element,
forCheckoutSession = false
) => {
if ( document.getElementById( element ) ) {
document.getElementById( element ).onblur = function () {
updatePaymentElementDefaultValues();
updatePaymentElementDefaultValues( forCheckoutSession );
};
}
};

let paymentElementOptions = {
...getUpeSettings(),
...getDefaultValues(),
wallets: {
applePay: 'never',
googlePay: 'never',
Expand All @@ -224,10 +269,24 @@ async function createStripePaymentElement( api, paymentMethodType ) {
};
}

const createdStripePaymentElement = elements.create(
'payment',
paymentElementOptions
);
let createdStripePaymentElement = null;

if ( shouldLoadStripeElements ) {
paymentElementOptions = {
...paymentElementOptions,
...getDefaultValues(),
...getUpeSettings(),
};
createdStripePaymentElement = elements.create(
'payment',
paymentElementOptions
);
} else {
createdStripePaymentElement = elements.createPaymentElement(
paymentElementOptions
);
mountCurrencySelectorElement( elements );
}

gatewayUPEComponents[ paymentMethodType ].elements = elements;
gatewayUPEComponents[ paymentMethodType ].upeElement =
Expand All @@ -240,13 +299,35 @@ async function createStripePaymentElement( api, paymentMethodType ) {
isLinkEnabled() &&
paymentMethodType === PAYMENT_METHOD_CARD
) {
attachDefaultValuesUpdateEvent( 'billing_email' );
attachDefaultValuesUpdateEvent( 'billing_phone' );
attachDefaultValuesUpdateEvent(
'billing_email',
! shouldLoadStripeElements
);
attachDefaultValuesUpdateEvent(
'billing_phone',
! shouldLoadStripeElements
);
}

return createdStripePaymentElement;
}

/**
* Mounts the currency selector element to the DOM element.
*
* @param {Object} elements The Stripe elements object.
*/
function mountCurrencySelectorElement( elements ) {
const currencySelectorContainer = document.getElementById(
'wc-stripe-currency-selector'
);
if ( ! currencySelectorContainer ) {
return;
}
const currencySelector = elements.createCurrencySelectorElement();
currencySelector.mount( currencySelectorContainer );
}

/**
* Submits the provided jQuery form and removes the 'processing' class from it.
*
Expand Down
21 changes: 20 additions & 1 deletion client/stripe-utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -459,9 +459,10 @@ export const getUpeSettings = () => {
* On order pay and change payment method pages, also preloads all billing details
* from the customer billing data passed from the server.
*
* @param {boolean} forCheckoutSession Whether the default values are for a Checkout Session.
* @return {Object} The defaultValues object for the Payment Element.
*/
export const getDefaultValues = () => {
export const getDefaultValues = ( forCheckoutSession = false ) => {
const stripeServerData = getStripeServerData();
const isOrderPay = stripeServerData?.isOrderPay;
const isChangingPayment = stripeServerData?.isChangingPayment;
Expand Down Expand Up @@ -500,6 +501,20 @@ export const getDefaultValues = () => {
address.postal_code = postalCode;
}

if ( forCheckoutSession ) {
return {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Personally, I prefer to keep the return statements clean and extract this object into a variable, but that's mostly a stylistic preference and not a blocker.

defaultValues: {
billingAddress: {
name: billingData.name?.trim() || undefined,
...( Object.keys( address ).length > 0
? { address }
: {} ),
},
phoneNumber: billingData.phone?.trim() || undefined,
},
};
}

return {
defaultValues: {
billingDetails: {
Expand All @@ -525,6 +540,10 @@ export const getDefaultValues = () => {
document.getElementById( 'billing_phone' )?.value ||
document.getElementById( 'shipping_phone' )?.value;

if ( forCheckoutSession ) {
return {};
}

return {
defaultValues: {
billingDetails: {
Expand Down
2 changes: 1 addition & 1 deletion includes/abstracts/abstract-wc-stripe-payment-gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -2007,7 +2007,7 @@ public function payment_scripts() {
wp_register_style( 'stripe_styles', plugins_url( 'assets/css/stripe-styles.css', WC_STRIPE_MAIN_FILE ), [], WC_STRIPE_VERSION );
wp_enqueue_style( 'stripe_styles' );

wp_register_script( 'stripe', 'https://js.stripe.com/v3/', '', '3.0', true );
wp_register_script( 'stripe', 'https://js.stripe.com/clover/stripe.js', '', null, true );
wp_register_script( 'woocommerce_stripe', plugins_url( 'assets/js/stripe' . $suffix . '.js', WC_STRIPE_MAIN_FILE ), [ 'jquery-payment', 'stripe' ], WC_STRIPE_VERSION, true );

wp_localize_script(
Expand Down
2 changes: 1 addition & 1 deletion includes/class-wc-stripe-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class WC_Stripe_API {
* Stripe API Endpoint
*/
const ENDPOINT = 'https://api.stripe.com/v1/';
const STRIPE_API_VERSION = '2024-06-20';
const STRIPE_API_VERSION = '2025-09-30.clover';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a significant update that might need its own PR, given that it will affect all payment flows (not just checkout sessions), but e2es seem to pass, so it looks like all is well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are absolutely correct in calling this out. We have a separate PR for updating the Stripe API and JS versions #4977. I have included the changes here as well for testing. I will merge the other PR first to ensure there are no regressions.


/**
* The invalid API key error count cache key.
Expand Down
4 changes: 2 additions & 2 deletions includes/class-wc-stripe-blocks-support.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ public function get_payment_method_script_handles() {
// Ensure Stripe JS is enqueued
wp_register_script(
'stripe',
'https://js.stripe.com/v3/',
'https://js.stripe.com/clover/stripe.js',
[],
'3.0',
null,
true
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ private function get_asset_data() {
private function register_express_checkout_script() {
$asset_data = $this->get_asset_data();

wp_register_script( 'stripe', 'https://js.stripe.com/v3/', '', '3.0', true );
wp_register_script( 'stripe', 'https://js.stripe.com/clover/stripe.js', '', null, true );
wp_register_script(
'wc_stripe_express_checkout',
WC_STRIPE_PLUGIN_URL . '/build/express-checkout.js',
Expand Down
30 changes: 28 additions & 2 deletions includes/payment-methods/class-wc-stripe-upe-payment-gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@ public function __construct() {
add_action( 'customize_save_after', [ $this, 'clear_appearance_transients' ] );
add_action( 'save_post', [ $this, 'clear_appearance_transients_block_theme' ], 10, 2 );

// Attach the currency selector div to the classic checkout page.
add_action( 'woocommerce_review_order_before_payment', [ $this, 'attach_currency_selector_element' ] );

// Hide action buttons for pending orders if they take a while to be confirmed.
add_filter( 'woocommerce_my_account_my_orders_actions', [ $this, 'filter_my_account_my_orders_actions' ], 10, 2 );

Expand Down Expand Up @@ -415,9 +418,9 @@ public function payment_scripts() {

wp_register_script(
'stripe',
'https://js.stripe.com/v3/',
'https://js.stripe.com/clover/stripe.js',
[],
'3.0',
null,
true
);

Expand Down Expand Up @@ -529,6 +532,9 @@ public function javascript_params() {
$stripe_params['excludedPaymentMethodTypes'] = $this->get_excluded_payment_method_types();
}

// Adaptive Pricing feature flag and setting.
$stripe_params['isAdaptivePricingSupported'] = WC_Stripe_Feature_Flags::is_checkout_sessions_available() && 'yes' === $this->get_option( 'adaptive_pricing', 'no' );

// Checking for other BNPL extensions.
$stripe_params['hasAffirmGatewayPlugin'] = WC_Stripe_Helper::has_gateway_plugin_active( WC_Stripe_Helper::OFFICIAL_PLUGIN_ID_AFFIRM );
$stripe_params['hasKlarnaGatewayPlugin'] = WC_Stripe_Helper::has_gateway_plugin_active( WC_Stripe_Helper::OFFICIAL_PLUGIN_ID_KLARNA );
Expand Down Expand Up @@ -928,6 +934,26 @@ public function payment_fields() {
}
}

/**
* Attaches the currency selector div to the classic checkout page.
* This is used to render the currency selector element in the checkout page.
*
* @return void
*/
public function attach_currency_selector_element() {
// Bail if checkout sessionsfeature flag is not enabled.
if ( ! WC_Stripe_Feature_Flags::is_checkout_sessions_available() ) {
return;
}

// Bail if not on the checkout page.
if ( ! is_checkout() ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also bail out if WC_Stripe_Feature_Flags::is_checkout_sessions_available() returns false

so that we don't attach this element when not supported.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. I will add the check. 👍

return;
}

echo '<div id="wc-stripe-currency-selector" class="wc-stripe-currency-selector" style="margin: 12px 0;"></div>';
}

/**
* Process the payment for a given order.
*
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ If you get stuck, you can ask for help in the [Plugin Forum](https://wordpress.o
* Tweak - Improve PHPDoc for migration and notes; minor notes refactor
* Tweak - Update PHPDoc for express checkout classes, block support class, and intent controller
* Tweak - Update PHPDoc for UPE payment method classes
* Add - Display adaptive pricing currency selector on classic checkout page
* Dev - Fix WC beta version resolution in tests
* Tweak - Update PHPDoc and fix minor issues for subscriptions and pre-order compatibility
* Dev - Upgrade @stripe/react-stripe-js to ^5.4.1 and @stripe/stripe-js to ^8.6.0 in JavaScript dependencies
Expand Down
Loading