Skip to content

Commit 1dff874

Browse files
committed
Merge remote-tracking branch 'origin/release/9.5.1' into trunk
2 parents 646308a + 272af5c commit 1dff874

16 files changed

+187
-143
lines changed

changelog.txt

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

3+
= 9.5.1 - 2025-05-17 =
4+
* Fix - Add a fetch cooldown to the payment method configuration retrieval endpoint to prevent excessive requests.
5+
* Fix - Prevent further Stripe API calls if API keys are invalid (401 response).
6+
* Fix - Stop checking for detached subscriptions for admin users, as it was slowing down wp-admin.
7+
* Fix - Fix fatal error when checking for a payment method availability using a specific order ID.
8+
39
= 9.5.0 - 2025-05-13 =
410
* Fix - Fixes the listing of payment methods on the classic checkout when the Optimized Checkout is enabled.
511
* Fix - Fixes the availability of WeChat Pay when the Optimized Checkout is enabled on the block checkout. Removes it from the classic/shortcode checkout to avoid issues.

client/settings/account-details/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,11 @@ const AccountDetails = () => {
133133
{ createInterpolateElement(
134134
isTestModeEnabled
135135
? __(
136-
"Seems like the test API keys we've saved for you are no longer valid. If you recently updated them, use the <strong>Configure Connection</strong> button below to reconnect.",
136+
"We couldn't connect to your account, it seems like the test API keys we've saved for you are no longer valid. Please use the <strong>Configure connection</strong> button below to reconnect.",
137137
'woocommerce-gateway-stripe'
138138
)
139139
: __(
140-
"Seems like the live API keys we've saved for you are no longer valid. If you recently updated them, use the <strong>Configure Connection</strong> button below to reconnect.",
140+
"We couldn't connect to your account, it seems like the live API keys we've saved for you are no longer valid. Please use the <strong>Configure connection</strong> button below to reconnect.",
141141
'woocommerce-gateway-stripe'
142142
),
143143
{

client/settings/stripe-auth-account/account-status-panel.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ const getAccountStatus = ( accountKeys, data, testMode ) => {
150150
},
151151
};
152152

153-
if ( ! hasKeys ) {
153+
if ( ! hasKeys || data?.account === null ) {
154154
return accountStatusMap.disconnected;
155155
}
156156

includes/admin/class-wc-stripe-admin-notices.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,6 @@ public function admin_notices() {
6666
// All other payment methods.
6767
$this->payment_methods_check_environment();
6868

69-
// Subscription related checks.
70-
if ( WC_Stripe_Subscriptions_Helper::is_subscriptions_enabled() ) {
71-
$this->subscriptions_check_environment();
72-
}
73-
7469
foreach ( (array) $this->notices as $notice_key => $notice ) {
7570
echo '<div class="' . esc_attr( $notice['class'] ) . '" style="position:relative;">';
7671

@@ -467,8 +462,11 @@ public function payment_methods_check_environment() {
467462
* Environment check for subscriptions.
468463
*
469464
* @return void
465+
*
466+
* @deprecated 9.6.0 This method is no longer used and will be removed in a future version.
470467
*/
471468
public function subscriptions_check_environment() {
469+
_deprecated_function( __METHOD__, '9.6.0' );
472470
$options = WC_Stripe_Helper::get_stripe_settings();
473471
if ( 'yes' === ( $options['enabled'] ?? null ) && 'no' !== get_option( 'wc_stripe_show_subscriptions_notice' ) ) {
474472
$subscriptions = WC_Stripe_Subscriptions_Helper::get_some_detached_subscriptions();

includes/class-wc-stripe-api.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@ class WC_Stripe_API {
1616
const ENDPOINT = 'https://api.stripe.com/v1/';
1717
const STRIPE_API_VERSION = '2024-06-20';
1818

19+
/**
20+
* The test mode invalid API keys option key.
21+
*
22+
* @var string
23+
*/
24+
const TEST_MODE_INVALID_API_KEYS_OPTION_KEY = 'wc_stripe_test_invalid_api_keys_detected';
25+
26+
/**
27+
* The live mode invalid API keys option key.
28+
*
29+
* @var string
30+
*/
31+
const LIVE_MODE_INVALID_API_KEYS_OPTION_KEY = 'wc_stripe_live_invalid_api_keys_detected';
32+
1933
/**
2034
* Secret API Key.
2135
*
@@ -231,6 +245,13 @@ public static function request( $request, $api = 'charges', $method = 'POST', $w
231245
* @param string $api
232246
*/
233247
public static function retrieve( $api ) {
248+
// If we have an option flag indicating that the secret key is not valid, we don't attempt the API call and we return an error.
249+
$invalid_api_keys_option_key = WC_Stripe_Mode::is_test() ? self::TEST_MODE_INVALID_API_KEYS_OPTION_KEY : self::LIVE_MODE_INVALID_API_KEYS_OPTION_KEY;
250+
$invalid_api_keys_detected = get_option( $invalid_api_keys_option_key );
251+
if ( $invalid_api_keys_detected ) {
252+
return null; // The UI expects this empty response in case of invalid API keys.
253+
}
254+
234255
WC_Stripe_Logger::log( "{$api}" );
235256

236257
$response = wp_safe_remote_get(
@@ -242,6 +263,18 @@ public static function retrieve( $api ) {
242263
]
243264
);
244265

266+
// If we get a 401 error, we know the secret key is not valid.
267+
if ( is_array( $response ) && isset( $response['response'] ) && is_array( $response['response'] ) && isset( $response['response']['code'] ) && 401 === $response['response']['code'] ) {
268+
// We save a flag in the options to avoid making calls until the secret key gets updated.
269+
update_option( $invalid_api_keys_option_key, true );
270+
update_option( $invalid_api_keys_option_key . '_at', time() );
271+
272+
// We delete the transient for the account data to trigger the not-connected UI in the admin dashboard.
273+
delete_transient( WC_Stripe_Mode::is_test() ? WC_Stripe_Account::TEST_ACCOUNT_OPTION : WC_Stripe_Account::LIVE_ACCOUNT_OPTION );
274+
275+
return null; // The UI expects this empty response in case of invalid API keys.
276+
}
277+
245278
if ( is_wp_error( $response ) || empty( $response['body'] ) ) {
246279
WC_Stripe_Logger::log( 'Error Response: ' . print_r( $response, true ) );
247280
return new WP_Error( 'stripe_error', __( 'There was a problem connecting to the Stripe API endpoint.', 'woocommerce-gateway-stripe' ) );

includes/class-wc-stripe-payment-method-configurations.php

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,36 +51,64 @@ class WC_Stripe_Payment_Method_Configurations {
5151
*/
5252
const CONFIGURATION_CACHE_TRANSIENT_EXPIRATION = 10 * MINUTE_IN_SECONDS;
5353

54+
/**
55+
* The payment method configuration fetch cooldown option key.
56+
*/
57+
const FETCH_COOLDOWN_OPTION_KEY = 'wcstripe_payment_method_config_fetch_cooldown';
58+
5459
/**
5560
* Get the merchant payment method configuration in Stripe.
5661
*
5762
* @param bool $force_refresh Whether to force a refresh of the payment method configuration from Stripe.
5863
* @return object|null
5964
*/
6065
private static function get_primary_configuration( $force_refresh = false ) {
61-
if ( ! $force_refresh ) {
66+
// Only allow fetching payment configuration once per minute.
67+
$fetch_cooldown = get_option( self::FETCH_COOLDOWN_OPTION_KEY, 0 );
68+
$is_in_cooldown = $fetch_cooldown > time();
69+
if ( ! $force_refresh || $is_in_cooldown ) {
6270
$cached_primary_configuration = self::get_payment_method_configuration_from_cache();
6371
if ( $cached_primary_configuration ) {
6472
return $cached_primary_configuration;
6573
}
74+
75+
// If we are hitting the API too much, and our main cache is not working, use the fallback cache.
76+
if ( $is_in_cooldown ) {
77+
$fallback_cache = self::get_payment_method_configuration_from_cache( true );
78+
if ( $fallback_cache ) {
79+
return $fallback_cache;
80+
}
81+
}
82+
83+
// Intentionally fall through to fetching the data from Stripe if we don't have it locally,
84+
// even when $force_refresh = false and/or $is_in_cooldown is true.
85+
// We _need_ the payment method configuration for things to work as expected,
86+
// so we will fetch it if we don't have anything locally.
6687
}
6788

89+
update_option( self::FETCH_COOLDOWN_OPTION_KEY, time() + MINUTE_IN_SECONDS );
90+
6891
return self::get_payment_method_configuration_from_stripe();
6992
}
7093

7194
/**
7295
* Get the payment method configuration from cache.
7396
*
97+
* @param bool $use_fallback Whether to use the fallback cache if the transient is not available.
98+
*
7499
* @return object|null
75100
*/
76-
private static function get_payment_method_configuration_from_cache() {
101+
private static function get_payment_method_configuration_from_cache( $use_fallback = false ) {
77102
if ( null !== self::$primary_configuration ) {
78103
return self::$primary_configuration;
79104
}
80105

81-
$cache_key = WC_Stripe_Mode::is_test() ? self::TEST_MODE_CONFIGURATION_CACHE_TRANSIENT_KEY : self::LIVE_MODE_CONFIGURATION_CACHE_TRANSIENT_KEY;
106+
$cache_key = WC_Stripe_Mode::is_test() ? self::TEST_MODE_CONFIGURATION_CACHE_TRANSIENT_KEY : self::LIVE_MODE_CONFIGURATION_CACHE_TRANSIENT_KEY;
82107
$cached_primary_configuration = get_transient( $cache_key );
83108
if ( false === $cached_primary_configuration || null === $cached_primary_configuration ) {
109+
if ( $use_fallback ) {
110+
return get_option( $cache_key );
111+
}
84112
return null;
85113
}
86114

@@ -93,8 +121,9 @@ private static function get_payment_method_configuration_from_cache() {
93121
*/
94122
public static function clear_payment_method_configuration_cache() {
95123
self::$primary_configuration = null;
96-
$cache_key = WC_Stripe_Mode::is_test() ? self::TEST_MODE_CONFIGURATION_CACHE_TRANSIENT_KEY : self::LIVE_MODE_CONFIGURATION_CACHE_TRANSIENT_KEY;
124+
$cache_key = WC_Stripe_Mode::is_test() ? self::TEST_MODE_CONFIGURATION_CACHE_TRANSIENT_KEY : self::LIVE_MODE_CONFIGURATION_CACHE_TRANSIENT_KEY;
97125
delete_transient( $cache_key );
126+
delete_option( $cache_key );
98127
}
99128

100129
/**
@@ -104,8 +133,11 @@ public static function clear_payment_method_configuration_cache() {
104133
*/
105134
private static function set_payment_method_configuration_cache( $configuration ) {
106135
self::$primary_configuration = $configuration;
107-
$cache_key = WC_Stripe_Mode::is_test() ? self::TEST_MODE_CONFIGURATION_CACHE_TRANSIENT_KEY : self::LIVE_MODE_CONFIGURATION_CACHE_TRANSIENT_KEY;
136+
$cache_key = WC_Stripe_Mode::is_test() ? self::TEST_MODE_CONFIGURATION_CACHE_TRANSIENT_KEY : self::LIVE_MODE_CONFIGURATION_CACHE_TRANSIENT_KEY;
108137
set_transient( $cache_key, $configuration, self::CONFIGURATION_CACHE_TRANSIENT_EXPIRATION );
138+
139+
// To be used as fallback if we are in API cooldown and the transient is not available.
140+
update_option( $cache_key, $configuration );
109141
}
110142

111143
/**
@@ -115,11 +147,7 @@ private static function set_payment_method_configuration_cache( $configuration )
115147
*/
116148
private static function get_payment_method_configuration_from_stripe() {
117149
$result = WC_Stripe_API::get_instance()->get_payment_method_configurations();
118-
$configurations = $result->data ?? null;
119-
120-
if ( ! $configurations ) {
121-
return null;
122-
}
150+
$configurations = $result->data ?? [];
123151

124152
// When connecting to the WooCommerce Platform account a new payment method configuration is created for the merchant.
125153
// This new payment method configuration has the WooCommerce Platform payment method configuration as parent, and inherits it's default payment methods.

includes/compat/class-wc-stripe-subscriptions-helper.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@ public static function is_subscriptions_enabled() {
2727
* Loads up to 50 subscriptions, and attempts to return up to 5 of those that are detached from the customer.
2828
*
2929
* @return array
30+
*
31+
* @deprecated 9.6.0 This method is no longer used and will be removed in a future version.
3032
*/
3133
public static function get_some_detached_subscriptions() {
34+
_deprecated_function( __METHOD__, '9.6.0' );
3235
// Check if we have a cached result.
3336
$cached_subscriptions = get_transient( self::DETACHED_SUBSCRIPTIONS_TRANSIENT_KEY );
3437
if ( ! empty( $cached_subscriptions ) ) {

includes/connect/class-wc-stripe-connect.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,11 @@ private function save_stripe_keys( $result, $type = 'connect', $mode = 'live' )
183183
update_option( 'wc_stripe_' . $prefix . 'oauth_failed_attempts', 0 );
184184
update_option( 'wc_stripe_' . $prefix . 'oauth_last_failed_at', '' );
185185

186+
// Clear the invalid API keys transient.
187+
$invalid_api_keys_option_key = $is_test ? WC_Stripe_API::TEST_MODE_INVALID_API_KEYS_OPTION_KEY : WC_Stripe_API::LIVE_MODE_INVALID_API_KEYS_OPTION_KEY;
188+
update_option( $invalid_api_keys_option_key, false );
189+
update_option( $invalid_api_keys_option_key . '_at', time() );
190+
186191
if ( 'app' === $type ) {
187192
// Stripe App OAuth access_tokens expire after 1 hour:
188193
// https://docs.stripe.com/stripe-apps/api-authentication/oauth#refresh-access-token

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,8 @@ public function is_enabled_at_checkout( $order_id = null, $account_domestic_curr
287287
$currencies = $this->get_supported_currencies();
288288
if ( ! empty( $currencies ) ) {
289289
if ( is_wc_endpoint_url( 'order-pay' ) && isset( $_GET['key'] ) ) {
290-
$order = wc_get_order( $order_id ? $order_id : absint( get_query_var( 'order-pay' ) ) );
291-
$order_currency = $order->get_currency();
292-
if ( ! in_array( $order_currency, $currencies, true ) ) {
290+
$order = wc_get_order( $order_id ? $order_id : absint( get_query_var( 'order-pay' ) ) );
291+
if ( ! $order || ! in_array( $order->get_currency(), $currencies, true ) ) {
293292
return false;
294293
}
295294
} elseif ( ! in_array( $current_store_currency, $currencies, true ) ) {

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.

0 commit comments

Comments
 (0)