diff --git a/changelog.txt b/changelog.txt index f537fca201..126f7233c3 100644 --- a/changelog.txt +++ b/changelog.txt @@ -30,6 +30,8 @@ * Update - Add nightly task and WooCommerce tool to remove stale entries from our database cache * Dev - Make 'Add to cart' more robust in e2e tests * Dev - Ensure e2e tests enable or disable Optimized Checkout during setup +* Tweak - Improve how we cache saved payment methods for customers +* Dev - Fix some e2e issues: timing, optional flows, and WooCommerce RC support = 9.8.1 - 2025-08-15 = * Fix - Remove connection type requirement from PMC sync migration attempt diff --git a/includes/class-wc-stripe-api.php b/includes/class-wc-stripe-api.php index 4aaa84d30e..f5f62e95b1 100644 --- a/includes/class-wc-stripe-api.php +++ b/includes/class-wc-stripe-api.php @@ -504,16 +504,22 @@ public static function update_payment_method( $payment_method_id, $payment_metho public static function attach_payment_method_to_customer( string $customer_id, string $payment_method_id ) { // Sources and Payment Methods need different API calls. if ( 0 === strpos( $payment_method_id, 'src_' ) ) { - return self::request( + $result = self::request( [ 'source' => $payment_method_id ], 'customers/' . $customer_id . '/sources' ); + } else { + $result = self::request( + [ 'customer' => $customer_id ], + 'payment_methods/' . $payment_method_id . '/attach' + ); } - return self::request( - [ 'customer' => $customer_id ], - 'payment_methods/' . $payment_method_id . '/attach' - ); + if ( empty( $result->error ) ) { + WC_Stripe_Customer::clear_customer_cache( $customer_id, $payment_method_id ); + } + + return $result; } /** @@ -534,17 +540,24 @@ public static function detach_payment_method_from_customer( string $customer_id, // Sources and Payment Methods need different API calls. if ( 0 === strpos( $payment_method_id, 'src_' ) ) { - return self::request( + $result = self::request( [], 'customers/' . $customer_id . '/sources/' . $payment_method_id, 'DELETE' ); + } else { + $result = self::request( + [], + 'payment_methods/' . $payment_method_id . '/detach' + ); } - return self::request( - [], - 'payment_methods/' . $payment_method_id . '/detach' - ); + if ( empty( $result->error ) ) { + // Clear cached payment methods. + WC_Stripe_Customer::clear_customer_cache( $customer_id, $payment_method_id ); + } + + return $result; } /** diff --git a/includes/class-wc-stripe-customer.php b/includes/class-wc-stripe-customer.php index ec5bab0d42..04d5071608 100644 --- a/includes/class-wc-stripe-customer.php +++ b/includes/class-wc-stripe-customer.php @@ -33,6 +33,11 @@ class WC_Stripe_Customer { */ const PAYMENT_METHODS_TRANSIENT_KEY = 'stripe_payment_methods_'; + /** + * Cache prefix for all saved payment methods per customer. + */ + protected const ALL_PAYMENT_METHODS_CACHE_PREFIX = 'all_payment_methods_'; + /** * Queryable Stripe payment method types. */ @@ -712,6 +717,7 @@ public function attach_source( $source_id ) { } elseif ( empty( $response->id ) ) { return new WP_Error( 'error', __( 'Unable to add payment source.', 'woocommerce-gateway-stripe' ) ); } else { + $this->customer_data = []; return $response; } } @@ -813,8 +819,8 @@ public function get_all_payment_methods( array $payment_method_types = [], int $ return []; } - $cache_key = self::PAYMENT_METHODS_TRANSIENT_KEY . '__all_' . $this->get_id(); - $all_payment_methods = get_transient( $cache_key ); + $cache_key = self::ALL_PAYMENT_METHODS_CACHE_PREFIX . $this->get_id(); + $all_payment_methods = WC_Stripe_Database_Cache::get( $cache_key ); if ( false === $all_payment_methods || ! is_array( $all_payment_methods ) ) { $all_payment_methods = []; @@ -839,7 +845,7 @@ public function get_all_payment_methods( array $payment_method_types = [], int $ && 'resource_missing' === $response->error->code ) { // If the customer doesn't exist, cache an empty array. - set_transient( $cache_key, [], DAY_IN_SECONDS ); + WC_Stripe_Database_Cache::set( $cache_key, [], DAY_IN_SECONDS ); } return []; } @@ -862,7 +868,7 @@ public function get_all_payment_methods( array $payment_method_types = [], int $ } while ( null !== $last_payment_method_id ); // Always cache the result without any filters applied. - set_transient( $cache_key, $all_payment_methods, DAY_IN_SECONDS ); + WC_Stripe_Database_Cache::set( $cache_key, $all_payment_methods, DAY_IN_SECONDS ); } // If there are no payment methods, no need to apply any filters below. @@ -901,7 +907,7 @@ public function delete_source( $source_id ) { $response = WC_Stripe_API::detach_payment_method_from_customer( $this->get_id(), $source_id ); if ( empty( $response->error ) ) { - $this->clear_cache( $source_id ); + $this->customer_data = []; do_action( 'wc_stripe_delete_source', $this->get_id(), $response ); return true; @@ -923,7 +929,7 @@ public function detach_payment_method( $payment_method_id ) { $response = WC_Stripe_API::detach_payment_method_from_customer( $this->get_id(), $payment_method_id ); if ( empty( $response->error ) ) { - $this->clear_cache( $payment_method_id ); + $this->customer_data = []; do_action( 'wc_stripe_detach_payment_method', $this->get_id(), $response ); return true; @@ -988,17 +994,37 @@ public function set_default_payment_method( $payment_method_id ) { * @param string|null $payment_method_id The ID of the payment method to clear cache for, if specified. */ public function clear_cache( $payment_method_id = null ) { - delete_transient( 'stripe_sources_' . $this->get_id() ); - delete_transient( 'stripe_customer_' . $this->get_id() ); + if ( empty( $this->get_id() ) ) { + return; + } + + self::clear_customer_cache( $this->get_id(), $payment_method_id ); + $this->customer_data = []; + } + + /** + * Clears cached data, especially payment methods, for a specified customer. + * + * @param string $customer_id The ID of the customer to clear cache for. + * @param string|null $payment_method_id The optional payment method ID to clear cache for. + */ + public static function clear_customer_cache( string $customer_id, ?string $payment_method_id = null ): void { + if ( empty( $customer_id ) ) { + return; + } + + delete_transient( 'stripe_sources_' . $customer_id ); + delete_transient( 'stripe_customer_' . $customer_id ); foreach ( self::STRIPE_PAYMENT_METHODS as $payment_method_type ) { - delete_transient( self::PAYMENT_METHODS_TRANSIENT_KEY . $payment_method_type . $this->get_id() ); + delete_transient( self::PAYMENT_METHODS_TRANSIENT_KEY . $payment_method_type . $customer_id ); } - delete_transient( self::PAYMENT_METHODS_TRANSIENT_KEY . '__all_' . $this->get_id() ); + + WC_Stripe_Database_Cache::delete( self::ALL_PAYMENT_METHODS_CACHE_PREFIX . $customer_id ); + // Clear cache for the specific payment method if provided. if ( $payment_method_id ) { WC_Stripe_Database_Cache::delete( 'payment_method_for_source_' . $payment_method_id ); } - $this->customer_data = []; } /** diff --git a/readme.txt b/readme.txt index 06bcde1e4c..748ae72a71 100644 --- a/readme.txt +++ b/readme.txt @@ -140,5 +140,7 @@ If you get stuck, you can ask for help in the [Plugin Forum](https://wordpress.o * Update - Add nightly task and WooCommerce tool to remove stale entries from our database cache * Dev - Make 'Add to cart' more robust in e2e tests * Dev - Ensure e2e tests enable or disable Optimized Checkout during setup +* Tweak - Improve how we cache saved payment methods for customers +* Dev - Fix some e2e issues: timing, optional flows, and WooCommerce RC support [See changelog for full details across versions](https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-stripe/trunk/changelog.txt).