Skip to content

Commit bc82fbd

Browse files
committed
Merge branch 'trunk' into develop
2 parents e53c477 + d5bf8d8 commit bc82fbd

9 files changed

+179
-56
lines changed

changelog.txt

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

3-
= 9.8.0 - xxxx-xx-xx =
3+
= 9.9.0 - xxxx-xx-xx =
44
* Add - Includes a new notice to highlight the Optimized Checkout feature above the payment methods list in the Stripe settings page
55
* Update - Increases the default font size for the Optimized Checkout payment element to match the rest of the checkout form
66
* Fix - Checks for the subscription payment method (if it is Stripe) when verifying for the payment method detachment
7-
* Update - Removes the ability to change the title for the Optimized Checkout payment element, as it is now set to "Stripe" by default
8-
* Update - Copy for the Optimized Checkout settings and notices
97
* Dev - Implements WooCommerce constants for the tax statuses
8+
9+
= 9.8.0 - 2025-08-11 =
1010
* Add - Adds the current setting value for the Optimized Checkout to the Stripe System Status Report data
1111
* Add - A new pill to the payment methods page to indicate the credit card requirement when the Optimized Checkout feature is enabled
1212
* Add - Tracks the toggle of the Optimized Checkout feature in the promotional banner
@@ -35,8 +35,9 @@
3535
* Dev - Move some testing and compiler node dependencies to devDependencies
3636
* Dev - Minor CSS change to comply with a SASS rule deprecation
3737
* Dev - Update SCSS to replace @import with @use and @forward
38+
* Update - Copy for the Optimized Checkout settings and notices
39+
* Update - Removes the ability to change the title for the Optimized Checkout payment element, as it is now set to "Stripe" by default
3840
* Fix - Handle missing customer when calling payment_methods API
39-
* Dev - Fix some e2e issues: timing, optional flows, and WooCommerce RC support
4041
* Fix - Reduce number of calls to Stripe payment_methods API
4142
* Fix - Add `get_icon_url()` to Payment Method base class
4243

includes/class-wc-stripe-helper.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1896,4 +1896,91 @@ public static function has_gateway_plugin_active( $plugin_id ) {
18961896
}
18971897
return false;
18981898
}
1899+
1900+
/**
1901+
* Checks if the given payment intent is valid for the order.
1902+
* This checks the currency, amount, and payment method types.
1903+
* The function will log a critical error if there is a mismatch.
1904+
*
1905+
* @param WC_Order $order The order to check.
1906+
* @param object|string $intent The payment intent to check, can either be an object or an intent ID.
1907+
* @param string|null $selected_payment_type The selected payment type, which is generally applicable for updates. If null, we will use the stored payment type for the order.
1908+
*
1909+
* @throws Exception Throws an exception if the intent is not valid for the order.
1910+
*/
1911+
public static function validate_intent_for_order( $order, $intent, ?string $selected_payment_type = null ): void {
1912+
$intent_id = null;
1913+
if ( is_string( $intent ) ) {
1914+
$intent_id = $intent;
1915+
$is_setup_intent = substr( $intent_id, 0, 4 ) === 'seti';
1916+
if ( $is_setup_intent ) {
1917+
$intent = WC_Stripe_API::retrieve( 'setup_intents/' . $intent_id . '?expand[]=payment_method' );
1918+
} else {
1919+
$intent = WC_Stripe_API::retrieve( 'payment_intents/' . $intent_id . '?expand[]=payment_method' );
1920+
}
1921+
}
1922+
1923+
if ( ! is_object( $intent ) ) {
1924+
throw new Exception( __( "We're not able to process this request. Please try again later.", 'woocommerce-gateway-stripe' ) );
1925+
}
1926+
1927+
if ( null === $intent_id ) {
1928+
$intent_id = $intent->id ?? null;
1929+
}
1930+
1931+
// Make sure we actually fetched the intent.
1932+
if ( ! empty( $intent->error ) ) {
1933+
WC_Stripe_Logger::error(
1934+
'Error: failed to fetch requested Stripe intent',
1935+
[
1936+
'intent_id' => $intent_id,
1937+
'error' => $intent->error,
1938+
]
1939+
);
1940+
throw new Exception( __( "We're not able to process this request. Please try again later.", 'woocommerce-gateway-stripe' ) );
1941+
}
1942+
1943+
if ( null === $selected_payment_type ) {
1944+
$selected_payment_type = $order->get_meta( '_stripe_upe_payment_type', true );
1945+
}
1946+
1947+
// If we don't have a selected payment type, that implies we have no stored value and a new payment type is permitted.
1948+
$is_valid_payment_type = empty( $selected_payment_type ) || ( ! empty( $intent->payment_method_types ) && in_array( $selected_payment_type, $intent->payment_method_types, true ) );
1949+
$order_currency = strtolower( $order->get_currency() );
1950+
$order_amount = WC_Stripe_Helper::get_stripe_amount( $order->get_total(), $order->get_currency() );
1951+
$order_intent_id = self::get_intent_id_from_order( $order );
1952+
1953+
if ( 'payment_intent' === $intent->object ) {
1954+
$is_valid = $order_currency === $intent->currency
1955+
&& $is_valid_payment_type
1956+
&& $order_amount === $intent->amount
1957+
&& ( ! $order_intent_id || $order_intent_id === $intent->id );
1958+
} else {
1959+
// Setup intents don't have an amount or currency.
1960+
$is_valid = $is_valid_payment_type
1961+
&& ( ! $order_intent_id || $order_intent_id === $intent->id );
1962+
}
1963+
1964+
// Return early if we have a valid intent.
1965+
if ( $is_valid ) {
1966+
return;
1967+
}
1968+
1969+
$permitted_payment_types = implode( '/', $intent->payment_method_types );
1970+
WC_Stripe_Logger::critical(
1971+
"Error: Invalid payment intent for order. Intent: {$intent->currency} {$intent->amount} via {$permitted_payment_types}, Order: {$order_currency} {$order_amount} {$selected_payment_type}",
1972+
[
1973+
'order_id' => $order->get_id(),
1974+
'intent_id' => $intent->id,
1975+
'intent_currency' => $intent->currency,
1976+
'intent_amount' => $intent->amount,
1977+
'intent_payment_method_types' => $intent->payment_method_types,
1978+
'selected_payment_type' => $selected_payment_type,
1979+
'order_currency' => $order->get_currency(),
1980+
'order_total' => $order->get_total(),
1981+
]
1982+
);
1983+
1984+
throw new Exception( __( "We're not able to process this request. Please try again later.", 'woocommerce-gateway-stripe' ) );
1985+
}
18991986
}

includes/class-wc-stripe-intent-controller.php

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,20 @@ public function update_payment_intent_ajax() {
414414
throw new Exception( __( 'Unable to verify your request. Please reload the page and try again.', 'woocommerce-gateway-stripe' ) );
415415
}
416416

417-
wp_send_json_success( $this->update_intent( $payment_intent_id, $order_id, $save_payment_method, $selected_upe_payment_type ), 200 );
417+
$update_intent_result = $this->update_intent( $payment_intent_id, $order_id, $save_payment_method, $selected_upe_payment_type );
418+
419+
if ( ! ( $update_intent_result['success'] ?? false ) ) {
420+
$error_message = $update_intent_result['error'] ?? __( "We're not able to process this request. Please try again later.", 'woocommerce-gateway-stripe' );
421+
wp_send_json_error(
422+
[
423+
'error' => [
424+
'message' => $error_message,
425+
],
426+
]
427+
);
428+
} else {
429+
wp_send_json_success( $update_intent_result, 200 );
430+
}
418431
} catch ( Exception $e ) {
419432
// Send back error so it can be displayed to the customer.
420433
wp_send_json_error(
@@ -433,10 +446,10 @@ public function update_payment_intent_ajax() {
433446
* @since 5.6.0
434447
* @version 9.4.0
435448
*
436-
* @param {string} $intent_id The id of the payment intent or setup intent to update.
437-
* @param {int} $order_id The id of the order if intent created from Order.
438-
* @param {boolean} $save_payment_method True if saving the payment method.
439-
* @param {string} $selected_upe_payment_type The name of the selected UPE payment type or empty string.
449+
* @param string $intent_id The id of the payment intent or setup intent to update.
450+
* @param int $order_id The id of the order if intent created from Order.
451+
* @param boolean $save_payment_method True if saving the payment method.
452+
* @param string $selected_upe_payment_type The name of the selected UPE payment type or empty string.
440453
*
441454
* @throws Exception If the update intent call returns with an error.
442455
* @return array|null An array with result of the update, or nothing
@@ -445,9 +458,15 @@ public function update_intent( $intent_id = '', $order_id = null, $save_payment_
445458
$order = wc_get_order( $order_id );
446459

447460
if ( ! is_a( $order, 'WC_Order' ) ) {
448-
return;
461+
return [
462+
'success' => false,
463+
'error' => __( 'Unable to find a matching order.', 'woocommerce-gateway-stripe' ),
464+
];
449465
}
450466

467+
$selected_payment_type = '' !== $selected_upe_payment_type && is_string( $selected_upe_payment_type ) ? $selected_upe_payment_type : null;
468+
WC_Stripe_Helper::validate_intent_for_order( $order, $intent_id, $selected_payment_type );
469+
451470
$gateway = $this->get_upe_gateway();
452471
$amount = $order->get_total();
453472
$currency = $order->get_currency();
@@ -497,13 +516,42 @@ public function update_intent( $intent_id = '', $order_id = null, $save_payment_
497516

498517
// Use "setup_intents" endpoint if `$intent_id` starts with `seti_`.
499518
$endpoint = $is_setup_intent ? 'setup_intents' : 'payment_intents';
500-
WC_Stripe_API::request_with_level3_data(
519+
$result = WC_Stripe_API::request_with_level3_data(
501520
$request,
502521
"{$endpoint}/{$intent_id}",
503522
$level3_data,
504523
$order
505524
);
506525

526+
if ( ! empty( $result->error ) ) {
527+
if ( 'payment_intent_unexpected_state' === $result->error->code ) {
528+
WC_Stripe_Logger::critical(
529+
'Error: Failed to update intent due to invalid operation',
530+
[
531+
'intent_id' => $intent_id,
532+
'order_id' => $order_id,
533+
'error' => $result->error,
534+
]
535+
);
536+
537+
throw new Exception( __( "We're not able to process this request. Please try again later.", 'woocommerce-gateway-stripe' ) );
538+
}
539+
540+
WC_Stripe_Logger::error(
541+
'Error: Failed to update Stripe intent',
542+
[
543+
'intent_id' => $intent_id,
544+
'order_id' => $order_id,
545+
'error' => $result->error,
546+
]
547+
);
548+
549+
return [
550+
'success' => false,
551+
'error' => $result->error->message,
552+
];
553+
}
554+
507555
// Prevent any failures if updating the status of a subscription order.
508556
if ( ! $gateway->has_subscription( $order_id ) ) {
509557
$order->update_status( OrderStatus::PENDING, __( 'Awaiting payment.', 'woocommerce-gateway-stripe' ) );

includes/payment-methods/class-wc-stripe-upe-payment-gateway.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,11 @@ public function process_payment( $order_id, $retry = true, $force_save_source =
835835
if ( $payment_intent_id && ! $this->payment_methods[ $selected_payment_type ]->supports_deferred_intent() ) {
836836
// Adds customer and metadata to PaymentIntent.
837837
// These parameters cannot be added upon updating the intent via the `/confirm` API.
838-
$this->intent_controller->update_intent( $payment_intent_id, $order_id, $save_payment_method, $selected_payment_type );
838+
try {
839+
$this->intent_controller->update_intent( $payment_intent_id, $order_id, $save_payment_method, $selected_payment_type );
840+
} catch ( Exception $update_intent_exception ) {
841+
throw new Exception( __( "We're not able to process this payment. Please try again later.", 'woocommerce-gateway-stripe' ) );
842+
}
839843
}
840844

841845
// Flag for using a deferred intent. To be removed.
@@ -1658,6 +1662,13 @@ public function process_order_for_confirmed_intent( $order, $intent_id, $save_pa
16581662
throw new WC_Stripe_Exception( __( "We're not able to process this payment. Please try again later.", 'woocommerce-gateway-stripe' ) );
16591663
}
16601664

1665+
// Validates the intent can be applied to the order.
1666+
try {
1667+
WC_Stripe_Helper::validate_intent_for_order( $order, $intent );
1668+
} catch ( Exception $e ) {
1669+
throw new Exception( __( "We're not able to process this payment. Please try again later.", 'woocommerce-gateway-stripe' ) );
1670+
}
1671+
16611672
list( $payment_method_type, $payment_method_details ) = $this->get_payment_method_data_from_intent( $intent );
16621673

16631674
if ( ! isset( $this->payment_methods[ $payment_method_type ] ) ) {

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "woocommerce-gateway-stripe",
33
"title": "WooCommerce Gateway Stripe",
4-
"version": "9.7.1",
4+
"version": "9.8.0",
55
"license": "GPL-3.0",
66
"homepage": "http://wordpress.org/plugins/woocommerce-gateway-stripe/",
77
"repository": {

readme.txt

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -110,43 +110,10 @@ If you get stuck, you can ask for help in the [Plugin Forum](https://wordpress.o
110110

111111
== Changelog ==
112112

113-
= 9.8.0 - xxxx-xx-xx =
113+
= 9.9.0 - xxxx-xx-xx =
114114
* Add - Includes a new notice to highlight the Optimized Checkout feature above the payment methods list in the Stripe settings page
115115
* Update - Increases the default font size for the Optimized Checkout payment element to match the rest of the checkout form
116116
* Fix - Checks for the subscription payment method (if it is Stripe) when verifying for the payment method detachment
117-
* Update - Removes the ability to change the title for the Optimized Checkout payment element, as it is now set to "Stripe" by default
118-
* Update - Copy for the Optimized Checkout settings and notices
119117
* Dev - Implements WooCommerce constants for the tax statuses
120-
* Add - Adds the current setting value for the Optimized Checkout to the Stripe System Status Report data
121-
* Add - A new pill to the payment methods page to indicate the credit card requirement when the Optimized Checkout feature is enabled
122-
* Add - Tracks the toggle of the Optimized Checkout feature in the promotional banner
123-
* Fix - Force the card payment method to be enabled when the Optimized Checkout is enabled in the merchant's Payment Method Configuration
124-
* Update - Deactivates Affirm or Klarna when other official plugins are active in merchant's Payment Method Configuration
125-
* Fix - Fixes issues related to booking multiple slots with express checkout payment methods enabled
126-
* Fix - Update the Optimized Checkout promotional inbox note to link to the relevant section in the Stripe settings page
127-
* Add - Makes the Optimized Checkout feature available for all merchants by default
128-
* Add - Adds a new bulk action option to the subscriptions listing screen to check for detached payment methods
129-
* Dev - Use product type constants that were added in WooCommerce 9.7
130-
* Dev - Removes the inclusion of the deprecated WC_Stripe_Order class
131-
* Add - Introduces a new banner to promote the Optimized Checkout feature in the Stripe settings page for versions 9.8 and above
132-
* Add - Introduces a new inbox note to promote the Optimized Checkout feature on version 9.8 and later
133-
* Tweak - Use wp_ajax prefix for its built-in security for Add Payment Method action
134-
* Fix - Prevent Stripe API calls after several consecutive 401 (Unauthorized) responses
135-
* Fix - 3DS authentication modal not shown when using Google Pay
136-
* Update - Improve Stripe API connector logging to include request/response context
137-
* Fix - Require credit cards to be enabled before Apple Pay and Google Pay can be enabled in PMC
138-
* Fix - Free trial subscription orders with payment methods that require redirection (eg: iDeal, Bancontact)
139-
* Tweak - Update checkout error message for invalid API key to be more generic and user-friendly
140-
* Tweak - Disable Amazon Pay in the merchant's Payment Method Configuration object if it is still behind a feature flag
141-
* Fix - Only clear customer cache when an action has been performed
142-
* Fix - Remove validation error check from classic checkout before payment method creation
143-
* Dev - Clean up LPM (Local Payment Method) feature flags and related code
144-
* Dev - Move some testing and compiler node dependencies to devDependencies
145-
* Dev - Minor CSS change to comply with a SASS rule deprecation
146-
* Dev - Update SCSS to replace @import with @use and @forward
147-
* Fix - Handle missing customer when calling payment_methods API
148-
* Dev - Fix some e2e issues: timing, optional flows, and WooCommerce RC support
149-
* Fix - Reduce number of calls to Stripe payment_methods API
150-
* Fix - Add `get_icon_url()` to Payment Method base class
151118

152119
[See changelog for full details across versions](https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-stripe/trunk/changelog.txt).

0 commit comments

Comments
 (0)