diff --git a/client/stripe-utils/cash-app-limit-notice-handler.js b/client/stripe-utils/cash-app-limit-notice-handler.js
index 34ec2d536..e2b5cad76 100644
--- a/client/stripe-utils/cash-app-limit-notice-handler.js
+++ b/client/stripe-utils/cash-app-limit-notice-handler.js
@@ -1,8 +1,6 @@
import { __ } from '@wordpress/i18n';
import { callWhenElementIsAvailable } from 'wcstripe/blocks/upe/call-when-element-is-available';
-
-/** The amount threshold for displaying the notice. */
-export const CASH_APP_NOTICE_AMOUNT_THRESHOLD = 200000;
+import { CASH_APP_NOTICE_AMOUNT_THRESHOLD } from 'wcstripe/data/constants';
/** The class name for the limit notice element. */
const LIMIT_NOTICE_CLASSNAME = 'wc-block-checkout__payment-method-limit-notice';
diff --git a/client/stripe-utils/utils.js b/client/stripe-utils/utils.js
index 302d388bf..8611acdd3 100644
--- a/client/stripe-utils/utils.js
+++ b/client/stripe-utils/utils.js
@@ -220,6 +220,8 @@ export { getStripeServerData, getErrorMessageForTypeAndCode };
* @return {boolean} True, if enabled; false otherwise.
*/
export const isLinkEnabled = ( paymentMethodsConfig ) => {
+ paymentMethodsConfig =
+ paymentMethodsConfig || getStripeServerData()?.paymentMethodsConfig;
return (
paymentMethodsConfig?.link !== undefined &&
paymentMethodsConfig?.card !== undefined
diff --git a/includes/abstracts/abstract-wc-stripe-payment-gateway.php b/includes/abstracts/abstract-wc-stripe-payment-gateway.php
index 251506e9d..ecf9778a8 100644
--- a/includes/abstracts/abstract-wc-stripe-payment-gateway.php
+++ b/includes/abstracts/abstract-wc-stripe-payment-gateway.php
@@ -481,8 +481,9 @@ public function generate_payment_request( $order, $prepared_payment_method ) {
$metadata = [
__( 'customer_name', 'woocommerce-gateway-stripe' ) => sanitize_text_field( $billing_first_name ) . ' ' . sanitize_text_field( $billing_last_name ),
__( 'customer_email', 'woocommerce-gateway-stripe' ) => sanitize_email( $billing_email ),
- 'order_id' => $order->get_order_number(),
- 'site_url' => esc_url( get_site_url() ),
+ 'order_id' => $order->get_order_number(),
+ 'site_url' => esc_url( get_site_url() ),
+ 'signature' => $this->get_order_signature( $order ),
];
if ( $this->has_subscription( $order->get_id() ) ) {
@@ -2324,4 +2325,23 @@ public function get_balance_transaction_id_from_charge( $charge ) {
return $balance_transaction_id;
}
+
+ /**
+ * Generates a unique signature for an order.
+ *
+ * This signature is included as metadata in Stripe requests and used to identify the order when webhooks are received.
+ *
+ * @param WC_Order $order The Order object.
+ * @return string The order's unique signature. Format: order_id:md5(order_id-order_key-customer_id-order_total).
+ */
+ protected function get_order_signature( $order ) {
+ $signature = [
+ absint( $order->get_id() ),
+ $order->get_order_key(),
+ $order->get_customer_id() ?? '',
+ WC_Stripe_Helper::get_stripe_amount( $order->get_total(), $order->get_currency() ),
+ ];
+
+ return sprintf( '%d:%s', $order->get_id(), md5( implode( '-', $signature ) ) );
+ }
}
diff --git a/includes/admin/class-wc-stripe-payment-requests-controller.php b/includes/admin/class-wc-stripe-payment-requests-controller.php
index 83f0bba71..beb2da974 100644
--- a/includes/admin/class-wc-stripe-payment-requests-controller.php
+++ b/includes/admin/class-wc-stripe-payment-requests-controller.php
@@ -39,6 +39,18 @@ public function admin_scripts() {
);
wp_enqueue_script( 'wc-stripe-payment-request-settings' );
+ $stripe_settings = WC_Stripe_Helper::get_stripe_settings();
+ $params = [
+ 'key' => 'yes' === $stripe_settings['testmode'] ? $stripe_settings['test_publishable_key'] : $stripe_settings['publishable_key'],
+ 'locale' => WC_Stripe_Helper::convert_wc_locale_to_stripe_locale( get_locale() ),
+ 'is_ece_enabled' => WC_Stripe_Feature_Flags::is_stripe_ece_enabled(),
+ ];
+ wp_localize_script(
+ 'wc-stripe-payment-request-settings',
+ 'wc_stripe_payment_request_settings_params',
+ $params
+ );
+
wp_register_style(
'wc-stripe-payment-request-settings',
plugins_url( 'build/payment_requests_settings.css', WC_STRIPE_MAIN_FILE ),
diff --git a/includes/admin/stripe-settings.php b/includes/admin/stripe-settings.php
index 5d80a4637..f13e45852 100644
--- a/includes/admin/stripe-settings.php
+++ b/includes/admin/stripe-settings.php
@@ -138,7 +138,7 @@
'label' => __( 'Button Type', 'woocommerce-gateway-stripe' ),
'type' => 'select',
'description' => __( 'Select the button type you would like to show.', 'woocommerce-gateway-stripe' ),
- 'default' => 'buy',
+ 'default' => 'default',
'desc_tip' => true,
'options' => [
'default' => __( 'Default', 'woocommerce-gateway-stripe' ),
diff --git a/includes/class-wc-gateway-stripe.php b/includes/class-wc-gateway-stripe.php
index ad59c91d2..3c0cd39d9 100644
--- a/includes/class-wc-gateway-stripe.php
+++ b/includes/class-wc-gateway-stripe.php
@@ -142,7 +142,7 @@ public function __construct() {
*/
public function get_title() {
// Change the title on the payment methods settings page to include the number of enabled payment methods.
- if ( isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] ) {
+ if ( ! WC_Stripe_Feature_Flags::is_upe_checkout_enabled() && isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] ) {
$enabled_payment_methods_count = count( WC_Stripe_Helper::get_legacy_enabled_payment_method_ids() );
$this->title = $enabled_payment_methods_count ?
/* translators: $1. Count of enabled payment methods. */
diff --git a/includes/class-wc-stripe-blocks-support.php b/includes/class-wc-stripe-blocks-support.php
index fa93dcdda..0fd0a49bb 100644
--- a/includes/class-wc-stripe-blocks-support.php
+++ b/includes/class-wc-stripe-blocks-support.php
@@ -188,13 +188,14 @@ public function get_payment_method_data() {
$js_params,
// Blocks-specific options
[
- 'icons' => $this->get_icons(),
- 'supports' => $this->get_supported_features(),
- 'showSavedCards' => $this->get_show_saved_cards(),
- 'showSaveOption' => $this->get_show_save_option(),
- 'isAdmin' => is_admin(),
- 'shouldShowPaymentRequestButton' => $this->should_show_payment_request_button(),
- 'button' => [
+ 'icons' => $this->get_icons(),
+ 'supports' => $this->get_supported_features(),
+ 'showSavedCards' => $this->get_show_saved_cards(),
+ 'showSaveOption' => $this->get_show_save_option(),
+ 'isAdmin' => is_admin(),
+ 'shouldShowPaymentRequestButton' => $this->should_show_payment_request_button(),
+ 'shouldShowExpressCheckoutButton' => $this->should_show_express_checkout_button(),
+ 'button' => [
'customLabel' => $this->payment_request_configuration->get_button_label(),
],
]
@@ -255,10 +256,15 @@ private function should_show_payment_request_button() {
* @return boolean True if ECEs should be displayed, false otherwise.
*/
private function should_show_express_checkout_button() {
+ // Don't show if ECEs are turned off in settings.
+ if ( ! $this->express_checkout_configuration->express_checkout_helper->is_express_checkout_enabled() ) {
+ return false;
+ }
+
// Don't show if ECEs are supposed to be hidden on the cart page.
if (
has_block( 'woocommerce/cart' )
- && ! $this->express_checkout_configuration->express_checkout_helper->should_show_ece_on_cart_page()()
+ && ! $this->express_checkout_configuration->express_checkout_helper->should_show_ece_on_cart_page()
) {
return false;
}
diff --git a/includes/class-wc-stripe-helper.php b/includes/class-wc-stripe-helper.php
index ef70c557e..c36d5faf3 100644
--- a/includes/class-wc-stripe-helper.php
+++ b/includes/class-wc-stripe-helper.php
@@ -238,32 +238,33 @@ public static function get_localized_messages() {
return apply_filters(
'wc_stripe_localized_messages',
[
- 'invalid_number' => __( 'The card number is not a valid credit card number.', 'woocommerce-gateway-stripe' ),
- 'invalid_expiry_month' => __( 'The card\'s expiration month is invalid.', 'woocommerce-gateway-stripe' ),
- 'invalid_expiry_year' => __( 'The card\'s expiration year is invalid.', 'woocommerce-gateway-stripe' ),
- 'invalid_cvc' => __( 'The card\'s security code is invalid.', 'woocommerce-gateway-stripe' ),
- 'incorrect_number' => __( 'The card number is incorrect.', 'woocommerce-gateway-stripe' ),
- 'incomplete_number' => __( 'The card number is incomplete.', 'woocommerce-gateway-stripe' ),
- 'incomplete_cvc' => __( 'The card\'s security code is incomplete.', 'woocommerce-gateway-stripe' ),
- 'incomplete_expiry' => __( 'The card\'s expiration date is incomplete.', 'woocommerce-gateway-stripe' ),
- 'expired_card' => __( 'The card has expired.', 'woocommerce-gateway-stripe' ),
- 'incorrect_cvc' => __( 'The card\'s security code is incorrect.', 'woocommerce-gateway-stripe' ),
- 'incorrect_zip' => __( 'The card\'s zip code failed validation.', 'woocommerce-gateway-stripe' ),
- 'postal_code_invalid' => __( 'Invalid zip code, please correct and try again', 'woocommerce-gateway-stripe' ),
- 'invalid_expiry_year_past' => __( 'The card\'s expiration year is in the past', 'woocommerce-gateway-stripe' ),
- 'card_declined' => __( 'The card was declined.', 'woocommerce-gateway-stripe' ),
- 'missing' => __( 'There is no card on a customer that is being charged.', 'woocommerce-gateway-stripe' ),
- 'processing_error' => __( 'An error occurred while processing the card.', 'woocommerce-gateway-stripe' ),
- 'invalid_sofort_country' => __( 'The billing country is not accepted by Sofort. Please try another country.', 'woocommerce-gateway-stripe' ),
- 'email_invalid' => __( 'Invalid email address, please correct and try again.', 'woocommerce-gateway-stripe' ),
- 'invalid_request_error' => is_add_payment_method_page()
+ 'invalid_number' => __( 'The card number is not a valid credit card number.', 'woocommerce-gateway-stripe' ),
+ 'invalid_expiry_month' => __( 'The card\'s expiration month is invalid.', 'woocommerce-gateway-stripe' ),
+ 'invalid_expiry_year' => __( 'The card\'s expiration year is invalid.', 'woocommerce-gateway-stripe' ),
+ 'invalid_cvc' => __( 'The card\'s security code is invalid.', 'woocommerce-gateway-stripe' ),
+ 'incorrect_number' => __( 'The card number is incorrect.', 'woocommerce-gateway-stripe' ),
+ 'incomplete_number' => __( 'The card number is incomplete.', 'woocommerce-gateway-stripe' ),
+ 'incomplete_cvc' => __( 'The card\'s security code is incomplete.', 'woocommerce-gateway-stripe' ),
+ 'incomplete_expiry' => __( 'The card\'s expiration date is incomplete.', 'woocommerce-gateway-stripe' ),
+ 'expired_card' => __( 'The card has expired.', 'woocommerce-gateway-stripe' ),
+ 'incorrect_cvc' => __( 'The card\'s security code is incorrect.', 'woocommerce-gateway-stripe' ),
+ 'incorrect_zip' => __( 'The card\'s zip code failed validation.', 'woocommerce-gateway-stripe' ),
+ 'postal_code_invalid' => __( 'Invalid zip code, please correct and try again', 'woocommerce-gateway-stripe' ),
+ 'invalid_expiry_year_past' => __( 'The card\'s expiration year is in the past', 'woocommerce-gateway-stripe' ),
+ 'card_declined' => __( 'The card was declined.', 'woocommerce-gateway-stripe' ),
+ 'missing' => __( 'There is no card on a customer that is being charged.', 'woocommerce-gateway-stripe' ),
+ 'processing_error' => __( 'An error occurred while processing the card.', 'woocommerce-gateway-stripe' ),
+ 'invalid_sofort_country' => __( 'The billing country is not accepted by Sofort. Please try another country.', 'woocommerce-gateway-stripe' ),
+ 'email_invalid' => __( 'Invalid email address, please correct and try again.', 'woocommerce-gateway-stripe' ),
+ 'invalid_request_error' => is_add_payment_method_page()
? __( 'Unable to save this payment method, please try again or use alternative method.', 'woocommerce-gateway-stripe' )
: __( 'Unable to process this payment, please try again or use alternative method.', 'woocommerce-gateway-stripe' ),
- 'amount_too_large' => __( 'The order total is too high for this payment method', 'woocommerce-gateway-stripe' ),
- 'amount_too_small' => __( 'The order total is too low for this payment method', 'woocommerce-gateway-stripe' ),
- 'country_code_invalid' => __( 'Invalid country code, please try again with a valid country code', 'woocommerce-gateway-stripe' ),
- 'tax_id_invalid' => __( 'Invalid Tax Id, please try again with a valid tax id', 'woocommerce-gateway-stripe' ),
- 'invalid_wallet_type' => __( 'Invalid wallet payment type, please try again or use an alternative method.', 'woocommerce-gateway-stripe' ),
+ 'amount_too_large' => __( 'The order total is too high for this payment method', 'woocommerce-gateway-stripe' ),
+ 'amount_too_small' => __( 'The order total is too low for this payment method', 'woocommerce-gateway-stripe' ),
+ 'country_code_invalid' => __( 'Invalid country code, please try again with a valid country code', 'woocommerce-gateway-stripe' ),
+ 'tax_id_invalid' => __( 'Invalid Tax Id, please try again with a valid tax id', 'woocommerce-gateway-stripe' ),
+ 'invalid_wallet_type' => __( 'Invalid wallet payment type, please try again or use an alternative method.', 'woocommerce-gateway-stripe' ),
+ 'payment_intent_authentication_failure' => __( 'We are unable to authenticate your payment method. Please choose a different payment method and try again.', 'woocommerce-gateway-stripe' ),
]
);
}
diff --git a/includes/class-wc-stripe-payment-tokens.php b/includes/class-wc-stripe-payment-tokens.php
index 0368de9ee..fb8c8f1be 100644
--- a/includes/class-wc-stripe-payment-tokens.php
+++ b/includes/class-wc-stripe-payment-tokens.php
@@ -412,9 +412,28 @@ public function woocommerce_payment_token_deleted( $token_id, $token ) {
$stripe_customer = new WC_Stripe_Customer( $token->get_user_id() );
try {
if ( WC_Stripe_Feature_Flags::is_upe_checkout_enabled() ) {
- if ( in_array( $token->get_gateway_id(), self::UPE_REUSABLE_GATEWAYS_BY_PAYMENT_METHOD, true ) ) {
- $stripe_customer->detach_payment_method( $token->get_token() );
+ // If it's not reusable payment method, we don't need to perform any additional checks.
+ if ( ! in_array( $token->get_gateway_id(), self::UPE_REUSABLE_GATEWAYS_BY_PAYMENT_METHOD, true ) ) {
+ return;
+ }
+
+ /**
+ * 1. Check if it's live mode.
+ * 2. Check if it's admin.
+ * 3. Check if it's not production environment.
+ * When all conditions are met, we don't want to delete the payment method from Stripe.
+ * This is to avoid detaching the payment method from the live stripe account on non production environments.
+ */
+ $settings = WC_Stripe_Helper::get_stripe_settings();
+ if (
+ 'no' === $settings['testmode'] &&
+ is_admin() &&
+ 'production' !== wp_get_environment_type()
+ ) {
+ return;
}
+
+ $stripe_customer->detach_payment_method( $token->get_token() );
} else {
if ( 'stripe' === $token->get_gateway_id() || 'stripe_sepa' === $token->get_gateway_id() ) {
$stripe_customer->delete_source( $token->get_token() );
diff --git a/includes/class-wc-stripe-webhook-handler.php b/includes/class-wc-stripe-webhook-handler.php
index 078b019a9..84e31fd1c 100644
--- a/includes/class-wc-stripe-webhook-handler.php
+++ b/includes/class-wc-stripe-webhook-handler.php
@@ -892,9 +892,14 @@ public function get_partial_amount_to_charge( $notification ) {
return false;
}
+ /**
+ * Handles the processing of a payment intent webhook.
+ *
+ * @param stdClass $notification The webhook notification from Stripe.
+ */
public function process_payment_intent_success( $notification ) {
$intent = $notification->data->object;
- $order = WC_Stripe_Helper::get_order_by_intent_id( $intent->id );
+ $order = $this->get_order_from_intent( $intent );
if ( ! $order ) {
WC_Stripe_Logger::log( 'Could not find order via intent ID: ' . $intent->id );
@@ -1198,6 +1203,43 @@ public function process_webhook( $request_body ) {
}
}
+
+ /**
+ * Fetches an order from a payment intent.
+ *
+ * @param stdClass $intent The Stripe PaymentIntent object.
+ * @return WC_Order|false The order object, or false if not found.
+ */
+ private function get_order_from_intent( $intent ) {
+ // Attempt to get the order from the intent metadata.
+ if ( isset( $intent->metadata->signature ) ) {
+ $signature = wc_clean( $intent->metadata->signature );
+ $data = explode( ':', $signature );
+
+ // Verify we received the order ID and signature (hash).
+ $order = isset( $data[0], $data[1] ) ? wc_get_order( absint( $data[0] ) ) : false;
+
+ if ( $order ) {
+ $intent_id = WC_Stripe_Helper::get_intent_id_from_order( $order );
+
+ // Return the order if the intent ID matches.
+ if ( $intent->id === $intent_id ) {
+ return $order;
+ }
+
+ /**
+ * If the order has no intent ID stored, we may have failed to store it during the initial payment request.
+ * Confirm that the signature matches the order, otherwise fall back to finding the order via the intent ID.
+ */
+ if ( empty( $intent_id ) && $this->get_order_signature( $order ) === $signature ) {
+ return $order;
+ }
+ }
+ }
+
+ // Fall back to finding the order via the intent ID.
+ return WC_Stripe_Helper::get_order_by_intent_id( $intent->id );
+ }
}
new WC_Stripe_Webhook_Handler();
diff --git a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php
index b294942ae..c6cc78ad0 100644
--- a/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php
+++ b/includes/compat/class-wc-stripe-subscriptions-legacy-sepa-token-update.php
@@ -70,14 +70,10 @@ public function maybe_update_subscription_legacy_payment_method( $subscription_i
public function maybe_update_subscription_source( WC_Subscription $subscription ) {
try {
$this->set_subscription_updated_payment_method( $subscription );
-
- $order_note = __( 'Stripe Gateway: The payment method used for renewals was updated from Sources to PaymentMethods.', 'woocommerce-gateway-stripe' );
+ $subscription->add_order_note( __( 'Stripe Gateway: The payment method used for renewals was updated from Sources to PaymentMethods.', 'woocommerce-gateway-stripe' ) );
} catch ( \Exception $e ) {
- /* translators: Reason why the subscription payment method wasn't updated */
- $order_note = sprintf( __( 'Stripe Gateway: A Source is used for renewals but could not be updated to PaymentMethods. Reason: %s', 'woocommerce-gateway-stripe' ), $e->getMessage() );
+ WC_Stripe_Logger::log( $e->getMessage() );
}
-
- $subscription->add_order_note( $order_note );
}
/**
@@ -131,6 +127,11 @@ private function set_subscription_updated_payment_method( WC_Subscription $subsc
// Retrieve the source object from the API.
$source_object = WC_Stripe_API::get_payment_method( $source_id );
+ // Bail out, if the source object isn't expected to be migrated. eg Card sources are not migrated.
+ if ( isset( $source_object->type ) && 'card' === $source_object->type ) {
+ throw new \Exception( sprintf( 'Skipping migration of Source for subscription #%d. Source is a card.', $subscription->get_id() ) );
+ }
+
// Bail out if the src_ hasn't been migrated to pm_ yet.
if ( ! isset( $source_object->metadata->migrated_payment_method ) ) {
throw new \Exception( sprintf( 'The Source has not been migrated to PaymentMethods on the Stripe account.', $subscription->get_id() ) );
diff --git a/includes/compat/trait-wc-stripe-subscriptions.php b/includes/compat/trait-wc-stripe-subscriptions.php
index 4811e834b..91bc3bc54 100644
--- a/includes/compat/trait-wc-stripe-subscriptions.php
+++ b/includes/compat/trait-wc-stripe-subscriptions.php
@@ -682,7 +682,7 @@ public function add_subscription_payment_meta( $payment_meta, $subscription ) {
],
'_stripe_source_id' => [
'value' => $source_id,
- 'label' => 'Stripe Source ID',
+ 'label' => 'Stripe Payment Method ID',
],
],
];
@@ -720,7 +720,7 @@ public function validate_subscription_payment_meta( $payment_method_id, $payment
&& 0 !== strpos( $payment_meta['post_meta']['_stripe_source_id']['value'], 'pm_' )
)
) {
- throw new Exception( __( 'Invalid source ID. A valid source "Stripe Source ID" must begin with "src_", "pm_", or "card_".', 'woocommerce-gateway-stripe' ) );
+ throw new Exception( __( 'Invalid payment method ID. A valid "Stripe Payment Method ID" must begin with "src_", "pm_", or "card_".', 'woocommerce-gateway-stripe' ) );
}
}
}
@@ -965,14 +965,14 @@ public function maybe_render_subscription_payment_method( $payment_method_to_dis
// Legacy handling for Stripe Card objects. ref: https://docs.stripe.com/api/cards/object
if ( isset( $source->object ) && WC_Stripe_Payment_Methods::CARD === $source->object ) {
/* translators: 1) card brand 2) last 4 digits */
- $payment_method_to_display = sprintf( __( 'Via %1$s card ending in %2$s', 'woocommerce-gateway-stripe' ), ( isset( $source->brand ) ? $source->brand : __( 'N/A', 'woocommerce-gateway-stripe' ) ), $source->last4 );
+ $payment_method_to_display = sprintf( __( 'Via %1$s card ending in %2$s', 'woocommerce-gateway-stripe' ), ( isset( $source->brand ) ? wc_get_credit_card_type_label( $source->brand ) : __( 'N/A', 'woocommerce-gateway-stripe' ) ), $source->last4 );
break 2;
}
switch ( $source->type ) {
case WC_Stripe_Payment_Methods::CARD:
/* translators: 1) card brand 2) last 4 digits */
- $payment_method_to_display = sprintf( __( 'Via %1$s card ending in %2$s', 'woocommerce-gateway-stripe' ), ( isset( $source->card->brand ) ? $source->card->brand : __( 'N/A', 'woocommerce-gateway-stripe' ) ), $source->card->last4 );
+ $payment_method_to_display = sprintf( __( 'Via %1$s card ending in %2$s', 'woocommerce-gateway-stripe' ), ( isset( $source->card->brand ) ? wc_get_credit_card_type_label( $source->card->brand ) : __( 'N/A', 'woocommerce-gateway-stripe' ) ), $source->card->last4 );
break 3;
case WC_Stripe_Payment_Methods::SEPA_DEBIT:
/* translators: 1) last 4 digits of SEPA Direct Debit */
diff --git a/includes/connect/class-wc-stripe-connect.php b/includes/connect/class-wc-stripe-connect.php
index e1c88d136..74d3e1f39 100644
--- a/includes/connect/class-wc-stripe-connect.php
+++ b/includes/connect/class-wc-stripe-connect.php
@@ -173,6 +173,9 @@ private function save_stripe_keys( $result, $type = 'connect', $mode = 'live' )
unset( $options['account_id'] );
unset( $options['test_account_id'] );
+ // Enable ECE for new connections.
+ $this->enable_ece_in_new_accounts();
+
WC_Stripe_Helper::update_main_stripe_settings( $options );
// Similar to what we do for webhooks, we save some stats to help debug oauth problems.
@@ -213,6 +216,17 @@ private function get_upe_checkout_experience_enabled() {
return 'yes';
}
+ /**
+ * Enable Stripe express checkout element for new connections.
+ */
+ private function enable_ece_in_new_accounts() {
+ $existing_stripe_settings = WC_Stripe_Helper::get_stripe_settings();
+
+ if ( empty( $existing_stripe_settings ) ) {
+ update_option( WC_Stripe_Feature_Flags::ECE_FEATURE_FLAG_NAME, 'yes' );
+ }
+ }
+
/**
* Gets default Stripe settings
*/
diff --git a/includes/payment-methods/class-wc-stripe-express-checkout-ajax-handler.php b/includes/payment-methods/class-wc-stripe-express-checkout-ajax-handler.php
index eb79ef3d8..fae03b94d 100644
--- a/includes/payment-methods/class-wc-stripe-express-checkout-ajax-handler.php
+++ b/includes/payment-methods/class-wc-stripe-express-checkout-ajax-handler.php
@@ -294,6 +294,7 @@ public function ajax_get_selected_product_data() {
wp_send_json( $data );
} catch ( Exception $e ) {
+ WC_Stripe_Logger::log( 'Product data error in express checkout: ' . $e->getMessage() );
wp_send_json( [ 'error' => wp_strip_all_tags( $e->getMessage() ) ] );
}
}
@@ -302,23 +303,33 @@ public function ajax_get_selected_product_data() {
* Create order. Security is handled by WC.
*/
public function ajax_create_order() {
- if ( WC()->cart->is_empty() ) {
- wp_send_json_error( __( 'Empty cart', 'woocommerce-gateway-stripe' ) );
- }
+ try {
+ if ( WC()->cart->is_empty() ) {
+ wp_send_json_error( __( 'Empty cart', 'woocommerce-gateway-stripe' ) );
+ }
- if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
- define( 'WOOCOMMERCE_CHECKOUT', true );
- }
+ if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
+ define( 'WOOCOMMERCE_CHECKOUT', true );
+ }
+
+ $this->express_checkout_helper->fix_address_fields_mapping();
- $this->express_checkout_helper->fix_address_fields_mapping();
+ // Normalizes billing and shipping state values.
+ $this->express_checkout_helper->normalize_state();
- // Normalizes billing and shipping state values.
- $this->express_checkout_helper->normalize_state();
+ // In case the state is required, but is missing, add a more descriptive error notice.
+ $this->express_checkout_helper->validate_state();
- // In case the state is required, but is missing, add a more descriptive error notice.
- $this->express_checkout_helper->validate_state();
+ WC()->checkout()->process_checkout();
+ } catch ( Exception $e ) {
+ WC_Stripe_Logger::log( 'Failed to create order for express checkout payment: ' . $e );
- WC()->checkout()->process_checkout();
+ $response = [
+ 'result' => 'error',
+ 'messages' => $e->getMessage(),
+ ];
+ wp_send_json( $response, 400 );
+ }
die( 0 );
}
@@ -356,14 +367,14 @@ public function ajax_pay_for_order() {
return;
}
+ $order_id = intval( $_POST['order'] );
try {
// Set up an environment, similar to core checkout.
wc_maybe_define_constant( 'WOOCOMMERCE_CHECKOUT', true );
wc_set_time_limit( 0 );
// Load the order.
- $order_id = intval( $_POST['order'] );
- $order = wc_get_order( $order_id );
+ $order = wc_get_order( $order_id );
if ( ! is_a( $order, WC_Order::class ) ) {
throw new Exception( __( 'Invalid order!', 'woocommerce-gateway-stripe' ) );
@@ -386,6 +397,8 @@ public function ajax_pay_for_order() {
$result = apply_filters( 'woocommerce_payment_successful_result', $result, $order_id );
} catch ( Exception $e ) {
+ WC_Stripe_Logger::log( 'Pay for order failed for order ' . $order_id . ' with express checkout: ' . $e );
+
$result = [
'result' => 'error',
'messages' => $e->getMessage(),
diff --git a/includes/payment-methods/class-wc-stripe-express-checkout-element.php b/includes/payment-methods/class-wc-stripe-express-checkout-element.php
index e760b4a90..f9286866d 100644
--- a/includes/payment-methods/class-wc-stripe-express-checkout-element.php
+++ b/includes/payment-methods/class-wc-stripe-express-checkout-element.php
@@ -91,7 +91,7 @@ public function init() {
add_action( 'wp_enqueue_scripts', [ $this, 'scripts' ] );
add_action( 'woocommerce_after_add_to_cart_form', [ $this, 'display_express_checkout_button_html' ], 1 );
- add_action( 'woocommerce_proceed_to_checkout', [ $this, 'display_express_checkout_button_html' ] );
+ add_action( 'woocommerce_proceed_to_checkout', [ $this, 'display_express_checkout_button_html' ], 20 );
add_action( 'woocommerce_checkout_before_customer_details', [ $this, 'display_express_checkout_button_html' ], 1 );
add_action( 'woocommerce_pay_order_before_payment', [ $this, 'display_express_checkout_button_html' ], 1 );
@@ -99,6 +99,7 @@ public function init() {
add_action( 'woocommerce_checkout_order_processed', [ $this, 'add_order_meta' ], 10, 2 );
add_filter( 'woocommerce_login_redirect', [ $this, 'get_login_redirect_url' ], 10, 3 );
add_filter( 'woocommerce_registration_redirect', [ $this, 'get_login_redirect_url' ], 10, 3 );
+ add_filter( 'woocommerce_cart_needs_shipping_address', [ $this, 'filter_cart_needs_shipping_address' ], 11, 1 );
add_action( 'before_woocommerce_pay_form', [ $this, 'localize_pay_for_order_page_scripts' ] );
}
@@ -168,21 +169,16 @@ public function get_login_redirect_url( $redirect ) {
* @return array The settings used for the Stripe express checkout element in JavaScript.
*/
public function javascript_params() {
- $needs_shipping = 'no';
- if ( ! is_null( WC()->cart ) && WC()->cart->needs_shipping() ) {
- $needs_shipping = 'yes';
- }
-
return [
- 'ajax_url' => WC_AJAX::get_endpoint( '%%endpoint%%' ),
- 'stripe' => [
+ 'ajax_url' => WC_AJAX::get_endpoint( '%%endpoint%%' ),
+ 'stripe' => [
'publishable_key' => 'yes' === $this->stripe_settings['testmode'] ? $this->stripe_settings['test_publishable_key'] : $this->stripe_settings['publishable_key'],
'allow_prepaid_card' => apply_filters( 'wc_stripe_allow_prepaid_card', true ) ? 'yes' : 'no',
'locale' => WC_Stripe_Helper::convert_wc_locale_to_stripe_locale( get_locale() ),
'is_link_enabled' => WC_Stripe_UPE_Payment_Method_Link::is_link_enabled(),
'is_express_checkout_enabled' => $this->express_checkout_helper->is_express_checkout_enabled(),
],
- 'nonce' => [
+ 'nonce' => [
'payment' => wp_create_nonce( 'wc-stripe-express-checkout' ),
'shipping' => wp_create_nonce( 'wc-stripe-express-checkout-shipping' ),
'get_cart_details' => wp_create_nonce( 'wc-stripe-get-cart-details' ),
@@ -194,26 +190,21 @@ public function javascript_params() {
'clear_cart' => wp_create_nonce( 'wc-stripe-clear-cart' ),
'pay_for_order' => wp_create_nonce( 'wc-stripe-pay-for-order' ),
],
- 'i18n' => [
+ 'i18n' => [
'no_prepaid_card' => __( 'Sorry, we\'re not accepting prepaid cards at this time.', 'woocommerce-gateway-stripe' ),
/* translators: Do not translate the [option] placeholder */
'unknown_shipping' => __( 'Unknown shipping option "[option]".', 'woocommerce-gateway-stripe' ),
],
- 'checkout' => [
- 'url' => wc_get_checkout_url(),
- 'currency_code' => strtolower( get_woocommerce_currency() ),
- 'country_code' => substr( get_option( 'woocommerce_default_country' ), 0, 2 ),
- 'needs_shipping' => $needs_shipping,
- // Defaults to 'required' to match how core initializes this option.
- 'needs_payer_phone' => 'required' === get_option( 'woocommerce_checkout_phone_field', 'required' ),
- ],
- 'button' => $this->express_checkout_helper->get_button_settings(),
- 'is_pay_for_order' => $this->express_checkout_helper->is_pay_for_order_page(),
- 'has_block' => has_block( 'woocommerce/cart' ) || has_block( 'woocommerce/checkout' ),
- 'login_confirmation' => $this->express_checkout_helper->get_login_confirmation_settings(),
- 'is_product_page' => $this->express_checkout_helper->is_product(),
- 'is_checkout_page' => $this->express_checkout_helper->is_checkout(),
- 'product' => $this->express_checkout_helper->get_product_data(),
+ 'checkout' => $this->express_checkout_helper->get_checkout_data(),
+ 'button' => $this->express_checkout_helper->get_button_settings(),
+ 'is_pay_for_order' => $this->express_checkout_helper->is_pay_for_order_page(),
+ 'has_block' => has_block( 'woocommerce/cart' ) || has_block( 'woocommerce/checkout' ),
+ 'login_confirmation' => $this->express_checkout_helper->get_login_confirmation_settings(),
+ 'is_product_page' => $this->express_checkout_helper->is_product(),
+ 'is_checkout_page' => $this->express_checkout_helper->is_checkout(),
+ 'product' => $this->express_checkout_helper->get_product_data(),
+ 'is_cart_page' => is_cart(),
+ 'taxes_based_on_billing' => wc_tax_enabled() && get_option( 'woocommerce_tax_based_on' ) === 'billing',
];
}
@@ -413,7 +404,7 @@ public function display_express_checkout_button_html() {
* Display express checkout button separator.
*/
public function display_express_checkout_button_separator_html() {
- if ( ! is_checkout() && ! is_cart() && ! is_wc_endpoint_url( 'order-pay' ) ) {
+ if ( ! is_checkout() && ! is_wc_endpoint_url( 'order-pay' ) ) {
return;
}
@@ -421,12 +412,21 @@ public function display_express_checkout_button_separator_html() {
return;
}
- if ( is_cart() && ! in_array( 'cart', $this->express_checkout_helper->get_button_locations(), true ) ) {
- return;
- }
-
?>
— —
express_checkout_helper->has_subscription_product() && wc_get_shipping_method_count( true, true ) === 0 ) {
+ return false;
+ }
+
+ return $needs_shipping_address;
+ }
}
diff --git a/includes/payment-methods/class-wc-stripe-express-checkout-helper.php b/includes/payment-methods/class-wc-stripe-express-checkout-helper.php
index eea5e1426..973e5c985 100644
--- a/includes/payment-methods/class-wc-stripe-express-checkout-helper.php
+++ b/includes/payment-methods/class-wc-stripe-express-checkout-helper.php
@@ -252,12 +252,7 @@ public function get_product_data() {
'pending' => true,
];
- $data['shippingOptions'] = [
- 'id' => 'pending',
- 'label' => __( 'Pending', 'woocommerce-gateway-stripe' ),
- 'detail' => '',
- 'amount' => 0,
- ];
+ $data['shippingOptions'] = [ $this->get_default_shipping_option() ];
}
$data['displayItems'] = $items;
@@ -277,6 +272,45 @@ public function get_product_data() {
return apply_filters( 'wc_stripe_payment_request_product_data', $data, $product );
}
+ /**
+ * JS params data used by cart and checkout pages.
+ *
+ * @param array $data
+ */
+ public function get_checkout_data() {
+ $data = [
+ 'url' => wc_get_checkout_url(),
+ 'currency_code' => strtolower( get_woocommerce_currency() ),
+ 'country_code' => substr( get_option( 'woocommerce_default_country' ), 0, 2 ),
+ 'needs_shipping' => 'no',
+ 'needs_payer_phone' => 'required' === get_option( 'woocommerce_checkout_phone_field', 'required' ),
+ 'default_shipping_option' => $this->get_default_shipping_option(),
+ ];
+
+ if ( ! is_null( WC()->cart ) && WC()->cart->needs_shipping() ) {
+ $data['needs_shipping'] = 'yes';
+ }
+
+ return $data;
+ }
+
+ /**
+ * Default shipping option, used by product, cart and checkout pages.
+ *
+ * @return void|array
+ */
+ private function get_default_shipping_option() {
+ if ( wc_get_shipping_method_count( true, true ) === 0 ) {
+ return null;
+ }
+
+ return [
+ 'id' => 'pending',
+ 'displayName' => __( 'Pending', 'woocommerce-gateway-stripe' ),
+ 'amount' => 0,
+ ];
+ }
+
/**
* Normalizes postal code in case of redacted data from Apple Pay.
*
@@ -514,6 +548,11 @@ public function should_show_express_checkout_button() {
return false;
}
+ $available_gateways = WC()->payment_gateways->get_available_payment_gateways();
+ if ( ! isset( $available_gateways['stripe'] ) ) {
+ return false;
+ }
+
// Don't show if on the cart or checkout page, or if page contains the cart or checkout
// shortcodes, with items in the cart that aren't supported.
if (
@@ -533,7 +572,7 @@ public function should_show_express_checkout_button() {
return false;
}
- // Don't show if product page PRB is disabled.
+ // Don't show if product page ECE is disabled.
if ( $this->is_product() && ! $this->should_show_ece_on_product_pages() ) {
return false;
}
@@ -543,6 +582,14 @@ public function should_show_express_checkout_button() {
return false;
}
+ // Don't show if the total price is 0.
+ // ToDo: support free trials. Free trials should be supported if the product does not require shipping.
+ if ( ( ! ( $this->is_pay_for_order_page() || $this->is_product() ) && isset( WC()->cart ) && 0.0 === (float) WC()->cart->get_total( false ) )
+ || ( $this->is_product() && 0.0 === (float) $this->get_product()->get_price() )
+ ) {
+ return false;
+ }
+
if ( $this->is_product() && in_array( $this->get_product()->get_type(), [ 'variable', 'variable-subscription' ], true ) ) {
$stock_availability = array_column( $this->get_product()->get_available_variations(), 'is_in_stock' );
// Don't show if all product variations are out-of-stock.
@@ -553,10 +600,12 @@ public function should_show_express_checkout_button() {
// Hide if cart/product doesn't require shipping and tax is based on billing or shipping address.
if (
+ ! $this->is_pay_for_order_page() &&
(
( is_product() && ! $this->product_needs_shipping( $this->get_product() ) ) ||
( ( is_cart() || is_checkout() ) && ! WC()->cart->needs_shipping() )
) &&
+ wc_tax_enabled() &&
in_array( get_option( 'woocommerce_tax_based_on' ), [ 'billing', 'shipping' ], true )
) {
return false;
diff --git a/includes/payment-methods/class-wc-stripe-payment-request.php b/includes/payment-methods/class-wc-stripe-payment-request.php
index 660e6ea40..7bb83d579 100644
--- a/includes/payment-methods/class-wc-stripe-payment-request.php
+++ b/includes/payment-methods/class-wc-stripe-payment-request.php
@@ -967,6 +967,11 @@ public function should_show_payment_request_button() {
return false;
}
+ $available_gateways = WC()->payment_gateways->get_available_payment_gateways();
+ if ( ! isset( $available_gateways['stripe'] ) ) {
+ return false;
+ }
+
// Don't show if on the cart or checkout page, or if page contains the cart or checkout
// shortcodes, with items in the cart that aren't supported.
if (
diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php
index 5fcb221d5..92867deaa 100644
--- a/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php
+++ b/includes/payment-methods/class-wc-stripe-upe-payment-gateway.php
@@ -561,7 +561,7 @@ public function get_upe_available_payment_methods() {
*/
public function payment_fields() {
try {
- $display_tokenization = $this->supports( 'tokenization' ) && is_checkout();
+ $display_tokenization = $this->supports( 'tokenization' ) && is_checkout() && $this->saved_cards;
// Output the form HTML.
?>
@@ -587,7 +587,9 @@ public function payment_fields() {
tokenization_script();
- $this->saved_payment_methods();
+ if ( is_user_logged_in() ) {
+ $this->saved_payment_methods();
+ }
}
?>
@@ -1609,9 +1611,17 @@ public function set_payment_method_title_for_order( $order, $payment_method_type
if ( ! isset( $this->payment_methods[ $payment_method_type ] ) ) {
return;
}
- $payment_method = $this->payment_methods[ $payment_method_type ];
- $payment_method_title = $payment_method->get_title( $stripe_payment_method );
- $payment_method_id = $payment_method instanceof WC_Stripe_UPE_Payment_Method_CC ? $this->id : $payment_method->id;
+
+ $payment_method = $this->payment_methods[ $payment_method_type ];
+ $payment_method_id = $payment_method instanceof WC_Stripe_UPE_Payment_Method_CC ? $this->id : $payment_method->id;
+ $is_stripe_link = isset( $stripe_payment_method->type ) && WC_Stripe_Payment_Methods::LINK === $stripe_payment_method->type;
+
+ // Stripe Link uses the main gateway to process payments, however Link payments should use the title of the Link payment method.
+ if ( $is_stripe_link && isset( $this->payment_methods[ WC_Stripe_Payment_Methods::LINK ] ) ) {
+ $payment_method_title = $this->payment_methods[ WC_Stripe_Payment_Methods::LINK ]->get_title( $stripe_payment_method );
+ } else {
+ $payment_method_title = $payment_method->get_title( $stripe_payment_method );
+ }
$order->set_payment_method( $payment_method_id );
$order->set_payment_method_title( $payment_method_title );
@@ -1740,6 +1750,7 @@ public function get_metadata_from_order( $order ) {
'order_id' => $order->get_order_number(),
'order_key' => $order->get_order_key(),
'payment_type' => $payment_type,
+ 'signature' => $this->get_order_signature( $order ),
];
return apply_filters( 'wc_stripe_intent_metadata', $metadata, $order );
diff --git a/includes/payment-methods/class-wc-stripe-upe-payment-method-link.php b/includes/payment-methods/class-wc-stripe-upe-payment-method-link.php
index c34a52ba5..b1d6721e4 100644
--- a/includes/payment-methods/class-wc-stripe-upe-payment-method-link.php
+++ b/includes/payment-methods/class-wc-stripe-upe-payment-method-link.php
@@ -32,7 +32,6 @@ public function __construct() {
* @return bool
*/
public static function is_link_enabled() {
-
// Assume Link is disabled if UPE is disabled.
if ( ! WC_Stripe_Feature_Flags::is_upe_checkout_enabled() ) {
return false;
diff --git a/package-lock.json b/package-lock.json
index 38929984e..80a07b112 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "woocommerce-gateway-stripe",
- "version": "8.8.2",
+ "version": "8.9.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
diff --git a/package.json b/package.json
index a98bd1896..3206a09c6 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "woocommerce-gateway-stripe",
"title": "WooCommerce Gateway Stripe",
- "version": "8.8.2",
+ "version": "8.9.0",
"license": "GPL-3.0",
"homepage": "http://wordpress.org/plugins/woocommerce-gateway-stripe/",
"repository": {
diff --git a/readme.txt b/readme.txt
index 4abd707c8..2c6304340 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,10 +1,10 @@
=== WooCommerce Stripe Payment Gateway ===
Contributors: woocommerce, automattic, royho, akeda, mattyza, bor0, woothemes
Tags: credit card, stripe, apple pay, payment request, google pay, sepa, bancontact, alipay, giropay, ideal, p24, woocommerce, automattic
-Requires at least: 6.4
-Tested up to: 6.6
+Requires at least: 6.5
+Tested up to: 6.7
Requires PHP: 7.4
-Stable tag: 8.8.2
+Stable tag: 8.9.0
License: GPLv3
License URI: https://www.gnu.org/licenses/gpl-3.0.html
Attributions: thorsten-stripe
@@ -110,9 +110,33 @@ If you get stuck, you can ask for help in the [Plugin Forum](https://wordpress.o
== Changelog ==
-= 8.8.2 - 2024-11-07 =
-* Fix - Prevent marking renewal orders as processing/completed multiple times due to handling the Stripe webhook in parallel.
-* Dev - Refactor lock_order_payment() to use order meta instead of transients.
-* Update - Process successful payment intent webhooks asynchronously.
+= 8.9.0 - 2024-11-14 =
+* Update - Enhance webhook processing to enable retrieving orders using payment_intent metadata.
+* Dev - Minor updates to the webhook handler class related to payment method names constants.
+* Tweak - Improve error message displayed when payment method creation fails in classic checkout.
+* Dev - Replace two occurrences of payment method names with their constant equivalents.
+* Fix - Hide express checkout when credit card payments are not enabled.
+* Fix - Fix issues when detaching payment methods on staging sites (with the new checkout experience enabled).
+* Fix - Display a notice if taxes vary by customer's billing address when checking out using the Stripe Express Checkout Element.
+* Tweak - Makes the new Stripe Express Checkout Element enabled by default in new accounts.
+* Dev - Add multiple unit tests for the Stripe Express Checkout Element implementation (for both frontend and backend).
+* Fix - Check if taxes are enabled when applying ECE tax compatibility check.
+* Fix - Fix ECE error when initial address on load is not defined as a shipping zone.
+* Fix - Corrected card brand capitalization on the My Account → Subscription page.
+* Fix - Displays a specific message when an authentication error occurs during checkout for 3DS cards (shortcode version).
+* Fix - Show 'Use a New Payment Method' radio button for logged in users only when card saving is enabled.
+* Fix - Fix the display and usage of the Link payment method on the shortcode checkout page with the Stripe Express Checkout Element.
+* Fix - Fix payment methods count on settings page.
+* Update - Improve Express Payment button previews on the edit Block Checkout and Cart pages for Google Pay and Apple Pay.
+* Tweak - Add error logging in ECE critical Ajax requests.
+* Add - Add support for Stripe Link payments via the new Stripe Checkout Element on the block cart and block checkout pages.
+* Add - Add support for Stripe Link payments via the new Stripe Checkout Element on the product, cart, checkout and pay for order pages.
+* Tweak - Do not load ECE button if the total amount is 0.
+* Add - Show ECE button preview on settings page.
+* Tweak - Remove the subscription order notes added each time a source wasn't migrated.
+* Tweak - Update ECE default button type.
+* Fix - Fix position of ECE button on shortcode cart page.
+* Fix - Call ECE specific 'paymentFailed' function only when payment request fails.
+* Fix - Fix issue in purchasing subscriptions when the store has no shipping options.
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-stripe/trunk/changelog.txt).
diff --git a/tests/phpunit/admin/migrations/test-class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php b/tests/phpunit/admin/migrations/test-class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php
index 498248b8e..90475e17b 100644
--- a/tests/phpunit/admin/migrations/test-class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php
+++ b/tests/phpunit/admin/migrations/test-class-wc-stripe-subscriptions-repairer-legacy-sepa-tokens.php
@@ -328,18 +328,9 @@ function ( $id ) {
$this->updater->maybe_migrate_before_renewal( $subscription_id );
$subscription = new WC_Subscription( $subscription_id );
- $notes = wc_get_order_notes(
- [ 'order_id' => $subscription_id ]
- );
// Confirm the subscription's payment method remains the same.
$this->assertEquals( $pm_id, $subscription->get_meta( self::SOURCE_ID_META_KEY ) );
-
- // Confirm a note is added when the Source wasn't migrated to PaymentMethods.
- $this->assertEquals(
- 'Stripe Gateway: A Source is used for renewals but could not be updated to PaymentMethods. Reason: The subscription is not using a Stripe Source for renewals.',
- $notes[0]->content
- );
}
public function test_maybe_update_subscription_legacy_payment_method_adds_note_when_source_not_migrated() {
@@ -361,18 +352,9 @@ function ( $id ) {
$this->updater->maybe_migrate_before_renewal( $subscription_id );
$subscription = new WC_Subscription( $subscription_id );
- $notes = wc_get_order_notes(
- [ 'order_id' => $subscription_id ]
- );
// Confirm the subscription's payment method remains the same.
$this->assertEquals( $source_id, $subscription->get_meta( self::SOURCE_ID_META_KEY ) );
-
- // Confirm a note is added when the Source wasn't migrated to PaymentMethods.
- $this->assertEquals(
- 'Stripe Gateway: A Source is used for renewals but could not be updated to PaymentMethods. Reason: The Source has not been migrated to PaymentMethods on the Stripe account.',
- $notes[0]->content
- );
}
/**
diff --git a/tests/phpunit/test-class-wc-stripe-upe-payment-gateway.php b/tests/phpunit/test-class-wc-stripe-upe-payment-gateway.php
index 7a33639a1..e3598461d 100644
--- a/tests/phpunit/test-class-wc-stripe-upe-payment-gateway.php
+++ b/tests/phpunit/test-class-wc-stripe-upe-payment-gateway.php
@@ -218,9 +218,10 @@ private function get_order_details( $order ) {
'customer_name' => 'Jeroen Sormani',
'customer_email' => 'admin@example.org',
'site_url' => 'http://example.org',
- 'order_id' => $order_id,
+ 'order_id' => $order_number,
'order_key' => $order_key,
'payment_type' => 'single',
+ 'signature' => sprintf( '%d:%s', $order->get_id(), md5( implode( '-', [ absint( $order->get_id() ), $order->get_order_key(), $order->get_customer_id(), $amount ] ) ) ),
];
return [ $amount, $description, $metadata ];
}
diff --git a/tests/phpunit/test-wc-stripe-express-checkout-element.php b/tests/phpunit/test-wc-stripe-express-checkout-element.php
new file mode 100644
index 000000000..2b14f3e8d
--- /dev/null
+++ b/tests/phpunit/test-wc-stripe-express-checkout-element.php
@@ -0,0 +1,365 @@
+getMockBuilder( WC_Stripe_Express_Checkout_Ajax_Handler::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $helper = $this->getMockBuilder( WC_Stripe_Express_Checkout_Helper::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->element = new WC_Stripe_Express_Checkout_Element( $ajax_handler, $helper );
+ }
+
+ /**
+ * Test for `get_login_redirect_url`.
+ *
+ * @return void
+ */
+ public function test_get_login_redirect_url() {
+ $actual = $this->element->get_login_redirect_url( 'http://example.com/redirect' );
+
+ $this->assertSame( 'http://example.com/redirect', $actual );
+ }
+
+ /**
+ * Test for `javascript_params`.
+ *
+ * @return void
+ */
+ public function test_javascript_params() {
+ $stripe_settings['testmode'] = 'yes';
+ $stripe_settings['test_publishable_key'] = 'pk_test_123';
+
+ WC_Stripe_Helper::update_main_stripe_settings( $stripe_settings );
+
+ $ajax_handler = $this->getMockBuilder( WC_Stripe_Express_Checkout_Ajax_Handler::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $helper = $this->getMockBuilder( WC_Stripe_Express_Checkout_Helper::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $element = new WC_Stripe_Express_Checkout_Element( $ajax_handler, $helper );
+
+ $actual = $element->javascript_params();
+
+ $this->assertSame( $stripe_settings['test_publishable_key'], $actual['stripe']['publishable_key'] );
+ }
+
+ /**
+ * Test for `scripts`.
+ *
+ * @return void
+ * @dataProvider provide_test_scripts
+ */
+ public function test_scripts( $page_supported, $should_show, $expected ) {
+ $stripe_settings['testmode'] = 'yes';
+ $stripe_settings['test_publishable_key'] = 'pk_test_123';
+
+ WC_Stripe_Helper::update_main_stripe_settings( $stripe_settings );
+
+ $ajax_handler = $this->getMockBuilder( WC_Stripe_Express_Checkout_Ajax_Handler::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $helper = $this->getMockBuilder( WC_Stripe_Express_Checkout_Helper::class )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'is_page_supported', 'should_show_express_checkout_button' ] )
+ ->getMock();
+
+ $helper->expects( $this->any() )
+ ->method( 'is_page_supported' )
+ ->willReturn( $page_supported );
+
+ $helper->expects( $this->any() )
+ ->method( 'should_show_express_checkout_button' )
+ ->willReturn( $should_show );
+
+ $element = new WC_Stripe_Express_Checkout_Element( $ajax_handler, $helper );
+
+ $element->scripts();
+ $actual = wp_script_is( 'wc_stripe_express_checkout', 'enqueued' );
+ $this->assertSame( $expected, $actual );
+ }
+
+ /**
+ * Provider for `test_scripts`.
+ *
+ * @return string[]
+ */
+ public function provide_test_scripts() {
+ return [
+ 'page not supported' => [
+ 'page supported' => false,
+ 'should show' => false,
+ 'expected' => false,
+ ],
+ 'should not show' => [
+ 'page supported' => true,
+ 'should show' => false,
+ 'expected' => false,
+ ],
+ 'successfully rendered' => [
+ 'page supported' => true,
+ 'should show' => true,
+ 'expected' => true,
+ ],
+ ];
+ }
+
+ /**
+ * Test for `add_order_meta`.
+ *
+ * @param string $checkout_type The checkout type.
+ * @param string $expected The expected payment method title.
+ * @return void
+ * @dataProvider provide_test_add_order_meta
+ */
+ public function test_add_order_meta( $checkout_type, $expected ) {
+ $order = wc_create_order();
+
+ $_POST['express_checkout_type'] = $checkout_type;
+ $_POST['payment_method'] = 'stripe';
+
+ $this->element->add_order_meta( $order->get_id(), [] );
+ $order = wc_get_order( $order->get_id() );
+
+ $this->assertSame( $expected, $order->get_payment_method_title() );
+ }
+
+ /**
+ * Provider for `test_add_order_meta`.
+ *
+ * @return array[]
+ */
+ public function provide_test_add_order_meta() {
+ return [
+ 'apple pay' => [
+ 'checkout type' => 'apple_pay',
+ 'expected' => 'Apple Pay (Stripe)',
+ ],
+ 'google pay' => [
+ 'checkout type' => 'google_pay',
+ 'expected' => 'Google Pay (Stripe)',
+ ],
+ ];
+ }
+
+ /**
+ * Test for `filter_gateway_title`.
+ *
+ * @return void
+ */
+ public function test_filter_gateway_title() {
+ $actual = $this->element->filter_gateway_title( 'test', 'stripe' );
+ $this->assertSame( 'test', $actual );
+ }
+
+ /**
+ * Test for `display_express_checkout_button_html`.
+ *
+ * @param bool $stripe_is_enabled Whether Stripe is enabled.
+ * @param bool $page_supported Whether the current page is supported.
+ * @param bool $should_show Whether the button should be shown.
+ * @param string $expected The expected output.
+ * @return void
+ * @dataProvider provide_test_display_express_checkout_button_html
+ */
+ public function test_display_express_checkout_button_html( $stripe_is_enabled, $page_supported, $should_show, $expected ) {
+ if ( $stripe_is_enabled ) {
+ add_filter(
+ 'woocommerce_available_payment_gateways',
+ function () use ( $stripe_is_enabled ) {
+ return [
+ 'stripe' => new class() extends WC_Payment_Gateway {
+ public function __construct() {
+ $this->id = 'stripe';
+ }
+ },
+ ];
+ }
+ );
+ }
+
+ $ajax_handler = $this->getMockBuilder( WC_Stripe_Express_Checkout_Ajax_Handler::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $helper = $this->getMockBuilder( WC_Stripe_Express_Checkout_Helper::class )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'is_page_supported', 'should_show_express_checkout_button' ] )
+ ->getMock();
+
+ $helper->expects( $this->any() )
+ ->method( 'is_page_supported' )
+ ->willReturn( $page_supported );
+
+ $helper->expects( $this->any() )
+ ->method( 'should_show_express_checkout_button' )
+ ->willReturn( $should_show );
+
+ $element = new WC_Stripe_Express_Checkout_Element( $ajax_handler, $helper );
+
+ ob_start();
+
+ $element->display_express_checkout_button_html();
+ $output = ob_get_clean();
+ $this->assertStringMatchesFormat( $expected, $output );
+ }
+
+ /**
+ * Provider for `test_display_express_checkout_button_html`.
+ *
+ * @return array
+ */
+ public function provide_test_display_express_checkout_button_html() {
+ return [
+ 'stripe disabled' => [
+ 'stripe is enabled' => false,
+ 'page supported' => false,
+ 'should show ECE' => false,
+ 'expected' => '',
+ ],
+ 'page not supported' => [
+ 'stripe is enabled' => true,
+ 'page supported' => false,
+ 'should show ECE' => false,
+ 'expected' => '',
+ ],
+ 'should not show ECE' => [
+ 'stripe is enabled' => true,
+ 'page supported' => true,
+ 'should show ECE' => false,
+ 'expected' => '',
+ ],
+ 'render successfully' => [
+ 'stripe is enabled' => true,
+ 'page supported' => true,
+ 'should show ECE' => true,
+ 'expected' => '%aid="wc-stripe-express-checkout-element"%a',
+ ],
+ ];
+ }
+
+ /**
+ * Test for `display_express_checkout_button_separator_html`.
+ *
+ * @param bool $is_checkout Whether the current page is checkout.
+ * @param bool $is_cart Whether the current page is cart.
+ * @param bool $is_order_pay Whether the current page is order pay.
+ * @param string $button_location The location of the button.
+ * @param string $expected The expected output.
+ * @return void
+ * @dataProvider provide_test_display_express_checkout_button_separator_html
+ */
+ public function test_display_express_checkout_button_separator_html( $is_checkout, $is_cart, $is_order_pay, $button_location, $expected ) {
+ add_filter(
+ 'woocommerce_is_checkout',
+ function () use ( $is_checkout ) {
+ return $is_checkout;
+ }
+ );
+
+ if ( $is_cart ) {
+ \Automattic\Jetpack\Constants::set_constant( 'WOOCOMMERCE_CART', true );
+ } else {
+ \Automattic\Jetpack\Constants::clear_single_constant( 'WOOCOMMERCE_CART' );
+ }
+
+ add_filter(
+ 'woocommerce_get_query_vars',
+ function () use ( $is_order_pay ) {
+ if ( ! $is_order_pay ) {
+ return [];
+ }
+
+ return [
+ 'is_order_pay' => $is_order_pay,
+ ];
+ }
+ );
+
+ $ajax_handler = $this->getMockBuilder( WC_Stripe_Express_Checkout_Ajax_Handler::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $helper = $this->getMockBuilder( WC_Stripe_Express_Checkout_Helper::class )
+ ->disableOriginalConstructor()
+ ->setMethods( [ 'get_button_locations' ] )
+ ->getMock();
+
+ $helper->expects( $this->any() )
+ ->method( 'get_button_locations' )
+ ->willReturn( [ $button_location ] );
+
+ $element = new WC_Stripe_Express_Checkout_Element( $ajax_handler, $helper );
+
+ ob_start();
+ $element->display_express_checkout_button_separator_html();
+ $output = ob_get_clean();
+ $this->assertStringMatchesFormat( $expected, $output );
+ }
+
+ /**
+ * Provider for `test_display_express_checkout_button_separator_html`.
+ *
+ * @return array
+ */
+ public function provide_test_display_express_checkout_button_separator_html() {
+ return [
+ 'not checkout, not cart, not order pay' => [
+ 'is checkout' => false,
+ 'is cart' => false,
+ 'is order pay' => false,
+ 'button location' => null,
+ 'expected' => '',
+ ],
+ 'checkout, button not in checkout' => [
+ 'is checkout' => true,
+ 'is cart' => false,
+ 'is order pay' => false,
+ 'button location' => 'cart',
+ 'expected' => '',
+ ],
+ 'cart, button not in cart' => [
+ 'is checkout' => false,
+ 'is cart' => true,
+ 'is order pay' => false,
+ 'button location' => 'checkout',
+ 'expected' => '',
+ ],
+ 'checkout, button in checkout' => [
+ 'is checkout' => true,
+ 'is cart' => false,
+ 'is order pay' => false,
+ 'button location' => 'checkout',
+ 'expected' => '%aid="wc-stripe-express-checkout-button-separator"%a',
+ ],
+ ];
+ }
+}
diff --git a/tests/phpunit/test-wc-stripe-express-checkout-helper.php b/tests/phpunit/test-wc-stripe-express-checkout-helper.php
index 87afc7a08..eee0adb36 100644
--- a/tests/phpunit/test-wc-stripe-express-checkout-helper.php
+++ b/tests/phpunit/test-wc-stripe-express-checkout-helper.php
@@ -3,13 +3,16 @@
/**
* These tests make assertions against class WC_Stripe_Express_Checkout_Helper.
*
- * @package WooCommerce_Stripe/Tests/WC_Stripe_Express_Checkout_Helper
+ * @package WooCommerce_Stripe/Tests/WC_Stripe_Express_Checkout_Helper_Test
*/
/**
- * WC_Stripe_Express_Checkout_Helper class.
+ * WC_Stripe_Express_Checkout_Helper_Test class.
*/
class WC_Stripe_Express_Checkout_Helper_Test extends WP_UnitTestCase {
+ private $shipping_zone;
+ private $shipping_method;
+
public function set_up() {
parent::set_up();
@@ -21,10 +24,35 @@ public function set_up() {
WC_Stripe_Helper::update_main_stripe_settings( $stripe_settings );
}
+ public function tear_down() {
+ if ( $this->shipping_zone ) {
+ delete_option( $this->shipping_method->get_instance_option_key() );
+ $this->shipping_zone->delete();
+ }
+
+ parent::tear_down();
+ }
+
+ public function set_up_shipping_methods() {
+ // Add a shipping zone.
+ $this->shipping_zone = new WC_Shipping_Zone();
+ $this->shipping_zone->set_zone_name( 'Worldwide' );
+ $this->shipping_zone->set_zone_order( 1 );
+ $this->shipping_zone->save();
+
+ $flat_rate_id = $this->shipping_zone->add_shipping_method( 'flat_rate' );
+ $this->shipping_method = WC_Shipping_Zones::get_shipping_method( $flat_rate_id );
+ $option_key = $this->shipping_method->get_instance_option_key();
+ $options['cost'] = '5';
+ update_option( $option_key, $options );
+ }
+
/**
* Test should_show_express_checkout_button, tax logic.
*/
public function test_hides_ece_if_cannot_compute_taxes() {
+ $this->set_up_shipping_methods();
+
$wc_stripe_ece_helper_mock = $this->createPartialMock(
WC_Stripe_Express_Checkout_Helper::class,
[
@@ -32,16 +60,22 @@ public function test_hides_ece_if_cannot_compute_taxes() {
'allowed_items_in_cart',
'should_show_ece_on_cart_page',
'should_show_ece_on_checkout_page',
+ 'is_pay_for_order_page',
]
);
$wc_stripe_ece_helper_mock->expects( $this->any() )->method( 'is_product' )->willReturn( false );
$wc_stripe_ece_helper_mock->expects( $this->any() )->method( 'allowed_items_in_cart' )->willReturn( true );
$wc_stripe_ece_helper_mock->expects( $this->any() )->method( 'should_show_ece_on_cart_page' )->willReturn( true );
$wc_stripe_ece_helper_mock->expects( $this->any() )->method( 'should_show_ece_on_checkout_page' )->willReturn( true );
+ $wc_stripe_ece_helper_mock->expects( $this->any() )->method( 'is_pay_for_order_page' )->willReturnOnConsecutiveCalls( true, false );
$wc_stripe_ece_helper_mock->testmode = true;
if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
define( 'WOOCOMMERCE_CHECKOUT', true );
}
+ $original_gateways = WC()->payment_gateways()->payment_gateways;
+ WC()->payment_gateways()->payment_gateways = [
+ 'stripe' => new WC_Gateway_Stripe(),
+ ];
// Create virtual product and add to cart.
$virtual_product = WC_Helper_Product::create_simple_product();
@@ -51,20 +85,111 @@ public function test_hides_ece_if_cannot_compute_taxes() {
WC()->session->init();
WC()->cart->add_to_cart( $virtual_product->get_id(), 1 );
+ // Do not hide if Pay for Order page.
+ update_option( 'woocommerce_tax_based_on', 'shipping' );
+ $this->assertTrue( $wc_stripe_ece_helper_mock->should_show_express_checkout_button() );
+
// Hide if cart has virtual product and tax is based on shipping or billing address.
+ update_option( 'woocommerce_calc_taxes', 'yes' );
update_option( 'woocommerce_tax_based_on', 'billing' );
$this->assertFalse( $wc_stripe_ece_helper_mock->should_show_express_checkout_button() );
update_option( 'woocommerce_tax_based_on', 'shipping' );
$this->assertFalse( $wc_stripe_ece_helper_mock->should_show_express_checkout_button() );
+ // Do not hide if taxes are not enabled.
+ update_option( 'woocommerce_calc_taxes', 'no' );
+ $this->assertTrue( $wc_stripe_ece_helper_mock->should_show_express_checkout_button() );
+
// Do not hide if taxes are not based on customer billing or shipping address.
+ update_option( 'woocommerce_calc_taxes', 'yes' );
update_option( 'woocommerce_tax_based_on', 'base' );
$this->assertTrue( $wc_stripe_ece_helper_mock->should_show_express_checkout_button() );
// Do not hide if cart requires shipping.
+ update_option( 'woocommerce_tax_based_on', 'billing' );
$shippable_product = WC_Helper_Product::create_simple_product();
WC()->cart->add_to_cart( $shippable_product->get_id(), 1 );
$this->assertTrue( $wc_stripe_ece_helper_mock->should_show_express_checkout_button() );
+
+ // Restore original gateways.
+ WC()->payment_gateways()->payment_gateways = $original_gateways;
+ }
+
+ /**
+ * Test should_show_express_checkout_button, gateway logic.
+ */
+ public function test_hides_ece_if_stripe_gateway_unavailable() {
+ $this->set_up_shipping_methods();
+
+ $wc_stripe_ece_helper_mock = $this->createPartialMock(
+ WC_Stripe_Express_Checkout_Helper::class,
+ [
+ 'is_product',
+ 'allowed_items_in_cart',
+ 'should_show_ece_on_cart_page',
+ 'should_show_ece_on_checkout_page',
+ ]
+ );
+ $wc_stripe_ece_helper_mock->expects( $this->any() )->method( 'is_product' )->willReturn( false );
+ $wc_stripe_ece_helper_mock->expects( $this->any() )->method( 'allowed_items_in_cart' )->willReturn( true );
+ $wc_stripe_ece_helper_mock->expects( $this->any() )->method( 'should_show_ece_on_cart_page' )->willReturn( true );
+ $wc_stripe_ece_helper_mock->expects( $this->any() )->method( 'should_show_ece_on_checkout_page' )->willReturn( true );
+ $wc_stripe_ece_helper_mock->testmode = true;
+ if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
+ define( 'WOOCOMMERCE_CHECKOUT', true );
+ }
+ $original_gateways = WC()->payment_gateways()->payment_gateways;
+
+ // Hide if 'stripe' gateway is unavailable.
+ update_option( 'woocommerce_calc_taxes', 'no' );
+ WC()->payment_gateways()->payment_gateways = [
+ 'stripe' => new WC_Gateway_Stripe(),
+ 'stripe_alipay' => new WC_Gateway_Stripe_Alipay(),
+ ];
+ $this->assertTrue( $wc_stripe_ece_helper_mock->should_show_express_checkout_button() );
+
+ unset( WC()->payment_gateways()->payment_gateways['stripe'] );
+ $this->assertFalse( $wc_stripe_ece_helper_mock->should_show_express_checkout_button() );
+
+ // Restore original gateways.
+ WC()->payment_gateways()->payment_gateways = $original_gateways;
+ }
+
+ /**
+ * Test for get_checkout_data().
+ */
+ public function test_get_checkout_data() {
+ // Local setup
+ update_option( 'woocommerce_checkout_phone_field', 'optional' );
+ update_option( 'woocommerce_default_country', 'US' );
+ update_option( 'woocommerce_currency', 'USD' );
+ WC()->cart->empty_cart();
+
+ $this->set_up_shipping_methods();
+
+ $wc_stripe_ece_helper = new WC_Stripe_Express_Checkout_Helper();
+ $checkout_data = $wc_stripe_ece_helper->get_checkout_data();
+
+ $this->assertNotEmpty( $checkout_data['url'] );
+ $this->assertEquals( 'usd', $checkout_data['currency_code'] );
+ $this->assertEquals( 'US', $checkout_data['country_code'] );
+ $this->assertEquals( 'no', $checkout_data['needs_shipping'] );
+ $this->assertFalse( $checkout_data['needs_payer_phone'] );
+ $this->assertArrayHasKey( 'id', $checkout_data['default_shipping_option'] );
+ $this->assertArrayHasKey( 'displayName', $checkout_data['default_shipping_option'] );
+ $this->assertArrayHasKey( 'amount', $checkout_data['default_shipping_option'] );
+ }
+
+ /**
+ * Test for get_checkout_data(), no shipping zones.
+ *
+ * This is in a separate test, to avoid problems with cached data.
+ */
+ public function test_get_checkout_data_no_shipping_zones() {
+ // When no shipping zones are set up, the default shipping option should be empty.
+ $wc_stripe_ece_helper = new WC_Stripe_Express_Checkout_Helper();
+ $checkout_data = $wc_stripe_ece_helper->get_checkout_data();
+ $this->assertEmpty( $checkout_data['default_shipping_option'] );
}
}
diff --git a/tests/phpunit/test-wc-stripe-payment-gateway.php b/tests/phpunit/test-wc-stripe-payment-gateway.php
index b37e28337..86f4be565 100644
--- a/tests/phpunit/test-wc-stripe-payment-gateway.php
+++ b/tests/phpunit/test-wc-stripe-payment-gateway.php
@@ -569,6 +569,63 @@ public function test_get_balance_transaction_id_from_charge() {
$this->assertEquals( null, $this->gateway->get_balance_transaction_id_from_charge( null ) );
}
+ /**
+ * Tests for Card brand and last 4 digits are displayed correctly for subscription.
+ *
+ * @see WC_Stripe_Subscriptions_Trait::maybe_render_subscription_payment_method()
+ */
+ public function test_render_subscription_payment_method() {
+ $mock_subscription = WC_Helper_Order::create_order(); // We can use an order as a subscription.
+ $mock_subscription->set_payment_method( 'stripe' );
+
+ $mock_subscription->update_meta_data( '_stripe_source_id', 'src_mock' );
+ $mock_subscription->update_meta_data( '_stripe_customer_id', 'cus_mock' );
+ $mock_subscription->save();
+
+ // This is the key the customer's payment methods are stored under in the transient.
+ $transient_key = WC_Stripe_Customer::PAYMENT_METHODS_TRANSIENT_KEY . 'cardcus_mock';
+
+ $mock_payment_method = new stdClass();
+ $mock_payment_method->id = 'src_mock';
+ $mock_payment_method->type = 'card';
+ $mock_payment_method->card = new stdClass();
+
+ // VISA ending in 4242
+ $mock_payment_method->card->brand = 'visa';
+ $mock_payment_method->card->last4 = '4242';
+
+ set_transient( $transient_key, [ $mock_payment_method ], DAY_IN_SECONDS );
+ $this->assertEquals( 'Via Visa card ending in 4242', $this->gateway->maybe_render_subscription_payment_method( 'N/A', $mock_subscription ) );
+
+ // MasterCard ending in 1234
+ $mock_payment_method->card->brand = 'mastercard';
+ $mock_payment_method->card->last4 = '1234';
+
+ set_transient( $transient_key, [ $mock_payment_method ], DAY_IN_SECONDS );
+ $this->assertEquals( 'Via MasterCard card ending in 1234', $this->gateway->maybe_render_subscription_payment_method( 'N/A', $mock_subscription ) );
+
+ // American Express ending in 5678
+ $mock_payment_method->card->brand = 'amex';
+ $mock_payment_method->card->last4 = '5678';
+
+ set_transient( $transient_key, [ $mock_payment_method ], DAY_IN_SECONDS );
+ $this->assertEquals( 'Via Amex card ending in 5678', $this->gateway->maybe_render_subscription_payment_method( 'N/A', $mock_subscription ) );
+
+ // JCB ending in 9012'
+ $mock_payment_method->card->brand = 'jcb';
+ $mock_payment_method->card->last4 = '9012';
+
+ set_transient( $transient_key, [ $mock_payment_method ], DAY_IN_SECONDS );
+
+ // Unknown card type
+ $mock_payment_method->card->brand = 'dummy';
+ $mock_payment_method->card->last4 = '0000';
+
+ set_transient( $transient_key, [ $mock_payment_method ], DAY_IN_SECONDS );
+ // Card brands that WC core doesn't recognize will be displayed as ucwords.
+ $this->assertEquals( 'Via Dummy card ending in 0000', $this->gateway->maybe_render_subscription_payment_method( 'N/A', $mock_subscription ) );
+ }
+
/**
* Tests for `lock_order_payment` method.
*/
diff --git a/uninstall.php b/uninstall.php
index 49409e678..9340d5b0c 100644
--- a/uninstall.php
+++ b/uninstall.php
@@ -117,4 +117,5 @@
// Feature flags
delete_option( '_wcstripe_feature_upe' );
delete_option( 'upe_checkout_experience_accepted_payments' );
+ delete_option( '_wcstripe_feature_ece' );
}
diff --git a/woocommerce-gateway-stripe.php b/woocommerce-gateway-stripe.php
index f01d45689..8b7ce3660 100644
--- a/woocommerce-gateway-stripe.php
+++ b/woocommerce-gateway-stripe.php
@@ -5,12 +5,12 @@
* Description: Take credit card payments on your store using Stripe.
* Author: Stripe
* Author URI: https://stripe.com/
- * Version: 8.8.2
+ * Version: 8.9.0
* Requires Plugins: woocommerce
- * Requires at least: 6.4
- * Tested up to: 6.6
- * WC requires at least: 8.9
- * WC tested up to: 9.3
+ * Requires at least: 6.5
+ * Tested up to: 6.7
+ * WC requires at least: 9.2
+ * WC tested up to: 9.4
* Text Domain: woocommerce-gateway-stripe
* Domain Path: /languages
*/
@@ -22,7 +22,7 @@
/**
* Required minimums and constants
*/
-define( 'WC_STRIPE_VERSION', '8.8.2' ); // WRCS: DEFINED_VERSION.
+define( 'WC_STRIPE_VERSION', '8.9.0' ); // WRCS: DEFINED_VERSION.
define( 'WC_STRIPE_MIN_PHP_VER', '7.3.0' );
define( 'WC_STRIPE_MIN_WC_VER', '7.4' );
define( 'WC_STRIPE_FUTURE_MIN_WC_VER', '7.5' );
@@ -262,10 +262,10 @@ public function init() {
require_once dirname( __FILE__ ) . '/includes/class-wc-stripe-account.php';
new Allowed_Payment_Request_Button_Types_Update();
- $this->api = new WC_Stripe_Connect_API();
- $this->connect = new WC_Stripe_Connect( $this->api );
- $this->payment_request_configuration = new WC_Stripe_Payment_Request();
- $this->account = new WC_Stripe_Account( $this->connect, 'WC_Stripe_API' );
+ $this->api = new WC_Stripe_Connect_API();
+ $this->connect = new WC_Stripe_Connect( $this->api );
+ $this->payment_request_configuration = new WC_Stripe_Payment_Request();
+ $this->account = new WC_Stripe_Account( $this->connect, 'WC_Stripe_API' );
// Express checkout configurations.
$express_checkout_helper = new WC_Stripe_Express_Checkout_Helper();
@@ -379,8 +379,8 @@ public function install() {
public function update_prb_location_settings() {
$stripe_settings = WC_Stripe_Helper::get_stripe_settings();
$prb_locations = isset( $stripe_settings['payment_request_button_locations'] )
- ? $stripe_settings['payment_request_button_locations']
- : [];
+ ? $stripe_settings['payment_request_button_locations']
+ : [];
if ( ! empty( $stripe_settings ) && empty( $prb_locations ) ) {
global $post;
@@ -483,6 +483,16 @@ public function add_gateways( $methods ) {
}
}
+ // Don't include Link as an enabled method if we're in the admin so it doesn't show up in the checkout editor page.
+ if ( is_admin() ) {
+ $methods = array_filter(
+ $methods,
+ function( $method ) {
+ return ! is_a( $method, WC_Stripe_UPE_Payment_Method_Link::class );
+ }
+ );
+ }
+
return $methods;
}