Skip to content

Handling saving of methods when SPE is enabled #4126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 49 commits into from
Apr 7, 2025
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
30b59b7
Changing icon to Stripe's
wjrosa Mar 20, 2025
c323ffd
Disabling method instructions when SPE is enabled
wjrosa Mar 20, 2025
1e0e717
Disabling saving of methods when SPE is enabled
wjrosa Mar 20, 2025
78ea9ab
Merge branch 'develop' into tweak/visual-changes-to-spe-in-classic-ch…
wjrosa Mar 20, 2025
ea2378d
Hiding Stripe label when it is the only method available
wjrosa Mar 20, 2025
c4a0541
Fix blocks checkout test
wjrosa Mar 20, 2025
ee6a6cc
Adding specific unit tests
wjrosa Mar 20, 2025
97f688e
Changelog and readme entries
wjrosa Mar 20, 2025
c1e4c4e
Re-enabling saving of methods
wjrosa Mar 21, 2025
da8c8b4
Minor style change
wjrosa Mar 21, 2025
2e3b7e1
Removing unnecessary styling
wjrosa Mar 21, 2025
5b2c93b
Toggling visibility of the saving method checkbox depending whether t…
wjrosa Mar 21, 2025
4b7c782
Adding constant + renaming selector
wjrosa Mar 21, 2025
1f22ae0
Minor logic improvement
wjrosa Mar 21, 2025
6e4668f
Merge branch 'develop' into fix/handling-saving-of-methods-with-spe-e…
wjrosa Mar 21, 2025
f87b4a4
Moving SPE files
wjrosa Mar 21, 2025
059d02e
Renaming constant
wjrosa Mar 21, 2025
83164a7
Merge branch 'develop' into fix/handling-saving-of-methods-with-spe-e…
wjrosa Mar 24, 2025
ea5d2b4
Merge branch 'fix/handling-saving-of-methods-with-spe-enabled' of htt…
wjrosa Mar 24, 2025
cc7f828
Fix payment method instatiation
wjrosa Mar 24, 2025
8f99920
Fix fingerprint error
wjrosa Mar 24, 2025
a746281
Fix saving of methods on the my account page
wjrosa Mar 24, 2025
2c4c641
Fix saving of methods in the checkout
wjrosa Mar 24, 2025
5a52073
Reverting unnecessary changes
wjrosa Mar 24, 2025
1121533
Fix display of saving checkbox on the block checkout + moving some files
wjrosa Mar 24, 2025
ccaac50
Revering unnecessary changes
wjrosa Mar 24, 2025
2a94ea1
Merge branch 'develop' into fix/handling-saving-of-methods-with-spe-e…
wjrosa Mar 25, 2025
2fcbe12
Reverting unnecessary change
wjrosa Mar 25, 2025
fa10e53
Changelog and readme entries
wjrosa Mar 25, 2025
442c0f4
Merge branch 'develop' into fix/handling-saving-of-methods-with-spe-e…
Mayisha Mar 26, 2025
583838a
Merge branch 'develop' into fix/handling-saving-of-methods-with-spe-e…
wjrosa Mar 26, 2025
74da162
Merge branch 'fix/handling-saving-of-methods-with-spe-enabled' of htt…
wjrosa Mar 26, 2025
977b38c
Additional check for missing payment method instance
wjrosa Mar 26, 2025
a8f4c97
Fix hiding of Stripe label when there are saved tokens
wjrosa Mar 26, 2025
c3a62ea
Fix saving checkbox display for logged out users
wjrosa Mar 26, 2025
a695e94
Merge branch 'develop' into fix/handling-saving-of-methods-with-spe-e…
Mayisha Mar 27, 2025
b32cdaf
Checking for absence of the saving checkbox
wjrosa Mar 27, 2025
449afdf
Merge branch 'develop' into fix/handling-saving-of-methods-with-spe-e…
wjrosa Mar 27, 2025
e8b8d08
Reverse logic for checkbox display
wjrosa Mar 27, 2025
2670f59
Merge branch 'develop' into fix/handling-saving-of-methods-with-spe-e…
wjrosa Mar 28, 2025
80618ca
Merge branch 'develop' into fix/handling-saving-of-methods-with-spe-e…
wjrosa Mar 31, 2025
7a8afd2
Fix create account checkbox behavior
wjrosa Mar 31, 2025
e4e6202
Merge branch 'develop' into fix/handling-saving-of-methods-with-spe-e…
wjrosa Apr 1, 2025
4b09ce3
Merge branch 'develop' into fix/handling-saving-of-methods-with-spe-e…
wjrosa Apr 2, 2025
d0db66b
Merge branch 'develop' into fix/handling-saving-of-methods-with-spe-e…
wjrosa Apr 4, 2025
7f9d3dd
Merge branch 'develop' into fix/handling-saving-of-methods-with-spe-e…
wjrosa Apr 7, 2025
13f4cb3
Merge branch 'develop' into fix/handling-saving-of-methods-with-spe-e…
wjrosa Apr 7, 2025
d410310
Merge on charge event handlers for SPE on the classic checkout
wjrosa Apr 7, 2025
fed2e12
Fix bad merge
wjrosa Apr 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { applySinglePaymentElementStyles } from 'wcstripe/blocks/upe/apply-single-payment-element-styles';
import { applySinglePaymentElementStyles } from 'wcstripe/blocks/upe/spe/apply-single-payment-element-styles';

describe( 'applySinglePaymentElementStyles', () => {
it( 'Correctly apply the required styles to HTML elements', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const SINGLE_PAYMENT_ELEMENT_CLASS = 'single-payment-element';

export const applySinglePaymentElementStyles = () => {
// Hide the Stripe radio button when it is the only available payment method.
const paymentMethodOptions = document.querySelectorAll(
Expand All @@ -14,12 +16,12 @@ export const applySinglePaymentElementStyles = () => {
.getElementById(
'radio-control-wc-payment-method-options-stripe__content'
)
.classList.add( 'single-payment-element' );
.classList.add( SINGLE_PAYMENT_ELEMENT_CLASS );
document
.getElementById(
'radio-control-wc-payment-method-options-stripe__label'
)
.classList.add( 'single-payment-element' );
.classList.add( SINGLE_PAYMENT_ELEMENT_CLASS );

// Style the Stripe iframe to remove the margin and set width to 100%.
const stripeIframe = document.querySelector(
Expand Down
14 changes: 14 additions & 0 deletions client/blocks/upe/spe/handle-display-of-saving-checkbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { NON_REUSABLE_METHODS } from 'wcstripe/stripe-utils/constants';
Copy link
Contributor Author

@wjrosa wjrosa Mar 25, 2025

Choose a reason for hiding this comment

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

I will merge both files on the next PR so this one doesn't get too big. I will also add tests for this there.


export const handleDisplayOfSavingCheckbox = ( method ) => {
const saveCardInfoContainer = document.querySelector(
'.wc-block-components-payment-methods__save-card-info'
);
if ( saveCardInfoContainer ) {
saveCardInfoContainer.style.display = NON_REUSABLE_METHODS.includes(
method
)
? 'none'
: 'block';
}
};
2 changes: 1 addition & 1 deletion client/blocks/upe/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ button.stripe-gateway-stripelink-modal-trigger:hover {
}
/* stylelint-disable selector-id-pattern */
#radio-control-wc-payment-method-options-stripe__content.single-payment-element {
padding: 0;
padding-top: 1.4em;

.content {
display: none;
Expand Down
11 changes: 0 additions & 11 deletions client/blocks/upe/upe-deferred-intent-creation/payment-elements.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,6 @@ const PaymentElements = ( {
};

if ( getBlocksConfiguration()?.isSPEEnabled ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removing the unnecessary styles since radio buttons will go away soon.

options.appearance.rules = {
...options.appearance?.rules,
...{
'.RadioIcon': {
width: '2.1em',
},
'.RadioIconOuter': {
strokeWidth: '2px',
},
},
};
options = {
...options,
...{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,9 @@ import {
removeCashAppLimitNotice,
} from 'wcstripe/stripe-utils/cash-app-limit-notice-handler';
import { isLinkEnabled } from 'wcstripe/stripe-utils';
import {
PAYMENT_METHOD_CARD,
PAYMENT_METHOD_CASHAPP,
} from 'wcstripe/stripe-utils/constants';
import { applySinglePaymentElementStyles } from 'wcstripe/blocks/upe/apply-single-payment-element-styles';
import { PAYMENT_METHOD_CASHAPP } from 'wcstripe/stripe-utils/constants';
import { applySinglePaymentElementStyles } from 'wcstripe/blocks/upe/spe/apply-single-payment-element-styles';
import { handleDisplayOfSavingCheckbox } from 'wcstripe/blocks/upe/spe/handle-display-of-saving-checkbox';

const noop = () => null;

Expand Down Expand Up @@ -309,10 +307,7 @@ const PaymentProcessor = ( {
removeCashAppLimitNotice();
}
// Apply single payment element styles if the selected payment method is card and SPE is enabled.
if (
selectedPaymentMethodType === PAYMENT_METHOD_CARD &&
getBlocksConfiguration()?.isSPEEnabled
) {
if ( getBlocksConfiguration()?.isSPEEnabled ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The selected payment method type here is always card, so it is redundant.

applySinglePaymentElementStyles();
}
}, [ selectedPaymentMethodType ] );
Expand All @@ -337,6 +332,9 @@ const PaymentProcessor = ( {
const onSelectedPaymentMethodChange = ( { value, complete } ) => {
setSelectedPaymentMethodType( value.type );
setIsPaymentElementComplete( complete );
if ( getBlocksConfiguration()?.isSPEEnabled ) {
handleDisplayOfSavingCheckbox( value.type );
}
};

return (
Expand Down
2 changes: 1 addition & 1 deletion client/classic/upe/deferred-intent.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
mountStripePaymentElement,
processPayment,
} from './payment-processing';
import { applySinglePaymentElementStyles } from 'wcstripe/classic/upe/apply-single-payment-element-styles';
import { applySinglePaymentElementStyles } from 'wcstripe/classic/upe/spe/apply-single-payment-element-styles';

jQuery( function ( $ ) {
// Create an API object, which will be used throughout the checkout.
Expand Down
8 changes: 8 additions & 0 deletions client/classic/upe/payment-processing.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
PAYMENT_METHOD_MULTIBANCO,
PAYMENT_METHOD_WECHAT_PAY,
} from 'wcstripe/stripe-utils/constants';
import { handleDisplayOfSavingCheckbox } from 'wcstripe/classic/upe/spe/handle-display-of-saving-checkbox';

const gatewayUPEComponents = {};
const paymentMethodsConfig = getStripeServerData()?.paymentMethodsConfig;
Expand Down Expand Up @@ -328,6 +329,13 @@ export async function mountStripePaymentElement( api, domElement ) {
gatewayUPEComponents[ paymentMethodType ].hasLoadError = true;
} );

// If the SPE is enabled, we need to handle the display of the saving checkbox.
if ( getStripeServerData()?.isSPEEnabled ) {
upeElement.on( 'change', ( { value } ) => {
handleDisplayOfSavingCheckbox( value.type );
} );
}

return gatewayUPEComponents[ paymentMethodType ];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { applySinglePaymentElementStyles } from 'wcstripe/classic/upe/apply-single-payment-element-styles';
import { applySinglePaymentElementStyles } from 'wcstripe/classic/upe/spe/apply-single-payment-element-styles';

describe( 'applySinglePaymentElementStyles', () => {
it( 'Correctly apply the required styles to HTML elements', () => {
Expand Down
14 changes: 14 additions & 0 deletions client/classic/upe/spe/handle-display-of-saving-checkbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { NON_REUSABLE_METHODS } from 'wcstripe/stripe-utils/constants';

export const handleDisplayOfSavingCheckbox = ( method ) => {
const saveCardInfoContainer = document.querySelector(
'.woocommerce-SavedPaymentMethods-saveNew'
);
if ( saveCardInfoContainer ) {
saveCardInfoContainer.style.display = NON_REUSABLE_METHODS.includes(
method
)
? 'none'
: 'block';
}
};
18 changes: 18 additions & 0 deletions client/stripe-utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,21 @@ export const EXPRESS_PAYMENT_METHOD_SETTING_AMAZON_PAY = 'amazonPay';
export const EXPRESS_PAYMENT_METHOD_SETTING_APPLE_PAY = 'applePay';
export const EXPRESS_PAYMENT_METHOD_SETTING_GOOGLE_PAY = 'googlePay';
export const EXPRESS_PAYMENT_METHOD_SETTING_LINK = 'link';

/**
* List of payment methods that are not recurring
*/
export const NON_REUSABLE_METHODS = [
PAYMENT_METHOD_ALIPAY,
PAYMENT_METHOD_AFFIRM,
PAYMENT_METHOD_AFTERPAY_CLEARPAY,
PAYMENT_METHOD_BECS,
PAYMENT_METHOD_BOLETO,
PAYMENT_METHOD_EPS,
PAYMENT_METHOD_GIROPAY,
PAYMENT_METHOD_KLARNA,
PAYMENT_METHOD_MULTIBANCO,
PAYMENT_METHOD_P24,
PAYMENT_METHOD_OXXO,
PAYMENT_METHOD_WECHAT_PAY,
];
6 changes: 5 additions & 1 deletion includes/class-wc-stripe-intent-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,11 @@ private function validate_payment_intent_required_params( $required_params, $non
*/
private function build_base_payment_intent_request_params( $payment_information ) {
$selected_payment_type = $payment_information['selected_payment_type'];
$payment_method_types = $payment_information['payment_method_types'];
if ( $this->get_upe_gateway()->is_spe_enabled() && isset( $payment_information['payment_method_details']->type ) ) {
$selected_payment_type = $payment_information['payment_method_details']->type;
}

$payment_method_types = $payment_information['payment_method_types'];

$request = [
'shipping' => $payment_information['shipping'],
Expand Down
41 changes: 26 additions & 15 deletions includes/payment-methods/class-wc-stripe-upe-payment-gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ public function __construct() {
$this->enabled = $this->get_option( 'enabled' );
$this->sepa_tokens_for_other_methods = 'yes' === $this->get_option( 'sepa_tokens_for_other_methods' );
$this->spe_enabled = WC_Stripe_Feature_Flags::is_spe_available() && 'yes' === $this->get_option( 'single_payment_element' );
$this->saved_cards = ! $this->spe_enabled && 'yes' === $this->get_option( 'saved_cards' ); // @todo Temporarily disabling saving of methods when SPE is enabled
$this->saved_cards = 'yes' === $this->get_option( 'saved_cards' );
$this->testmode = WC_Stripe_Mode::is_test();
$this->publishable_key = ! empty( $main_settings['publishable_key'] ) ? $main_settings['publishable_key'] : '';
$this->secret_key = ! empty( $main_settings['secret_key'] ) ? $main_settings['secret_key'] : '';
Expand Down Expand Up @@ -653,10 +653,7 @@ public function payment_fields() {
<p><?php echo wp_kses_post( $this->get_description() ); ?></p>
<?php endif; ?>

<?php
// @todo Temporarily disabling test instructions when SPE is enabled.
if ( $this->testmode && ! $this->spe_enabled ) :
?>
<?php if ( $this->testmode ) : ?>
<p class="testmode-info">
<?php
printf(
Expand Down Expand Up @@ -899,6 +896,15 @@ private function process_payment_with_payment_method( int $order_id ) {
$upe_payment_method = $this->payment_methods[ $selected_payment_type ] ?? null;
$response_args = [];

if ( $this->spe_enabled && isset( $payment_method_details->type ) ) {
foreach ( self::UPE_AVAILABLE_METHODS as $payment_method_class ) {
$payment_method = new $payment_method_class();
if ( $payment_method->get_id() === $payment_method_details->type ) {
$upe_payment_method = $payment_method;
}
}
}

// Make sure that we attach the payment method and the customer ID to the order meta data.
$this->set_payment_method_id_for_order( $order, $payment_method_id );
$this->set_customer_id_for_order( $order, $payment_information['customer'] );
Expand Down Expand Up @@ -2009,18 +2015,27 @@ public function create_token_from_setup_intent( $setup_intent_id, $user ) {

$payment_method_id = $setup_intent->payment_method;

if ( isset( $this->payment_methods[ $payment_method_type ] ) ) {
$payment_method = null;
if ( $this->spe_enabled ) {
$payment_method_type = $payment_method_details['type'] ?? $payment_method_details->type ?? null;
if ( ! empty( $payment_method_type ) ) {
foreach ( self::UPE_AVAILABLE_METHODS as $payment_method_class ) {
$payment_method_instance = new $payment_method_class();
if ( $payment_method_instance->get_id() === $payment_method_type ) {
$payment_method = $payment_method_instance;
}
}
}
} else {
$payment_method = $this->payment_methods[ $payment_method_type ];
}

if ( $payment_method->get_id() !== $payment_method->get_retrievable_type() ) {
$payment_method_id = $payment_method_details[ $payment_method_type ]->generated_sepa_debit;
}
if ( $payment_method->get_id() !== $payment_method->get_retrievable_type() ) {
$payment_method_id = $payment_method_details[ $payment_method_type ]->generated_sepa_debit;
}

$payment_method_object = $this->stripe_request( 'payment_methods/' . $payment_method_id );

$payment_method = $this->payment_methods[ $payment_method_type ];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was redundant.


$customer = new WC_Stripe_Customer( wp_get_current_user()->ID );
$customer->clear_cache();

Expand Down Expand Up @@ -2847,10 +2862,6 @@ private function get_payment_method_types_for_intent_creation(
* @return bool - True if the payment method is reusable and the saved cards feature is enabled for the gateway and there is no subscription item in the cart, false otherwise.
*/
private function should_upe_payment_method_show_save_option( $payment_method ) {
if ( $this->spe_enabled ) { // @todo Temporary disabling saving payment methods for SPE.
return false;
}

if ( $payment_method->is_reusable() ) {
// If a subscription in the cart, it will be saved by default so no need to show the option.
return $this->is_saved_cards_enabled() && ! $this->is_subscription_item_in_cart() && ! $this->is_pre_order_charged_upon_release_in_cart();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,14 +384,19 @@ public function get_retrievable_type() {
* @return WC_Payment_Token_SEPA
*/
public function create_payment_token_for_user( $user_id, $payment_method ) {
$method_details = $payment_method->{ $payment_method->type };

$token = new WC_Payment_Token_SEPA();
$token->set_last4( $payment_method->sepa_debit->last4 );
$token->set_last4( $method_details->last4 );
$token->set_gateway_id( $this->id );
$token->set_token( $payment_method->id );
$token->set_payment_method_type( $this->get_id() );
$token->set_user_id( $user_id );
$token->set_fingerprint( $payment_method->sepa_debit->fingerprint );
if ( ! empty( $method_details->fingerprint ) ) {
$token->set_fingerprint( $method_details->fingerprint );
}
$token->save();

return $token;
}

Expand Down
16 changes: 11 additions & 5 deletions includes/payment-tokens/class-wc-stripe-payment-tokens.php
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,16 @@ public function woocommerce_get_customer_upe_payment_tokens( $tokens, $user_id,
}

// Add SEPA if it is disabled and iDEAL or Bancontact are enabled. iDEAL and Bancontact tokens are saved as SEPA tokens.
// @todo Temporarily disabling the `is_sepa_tokens_for_other_methods_enabled` feature when SPE is enabled.
if ( ! $gateway->is_spe_enabled() && $gateway->is_sepa_tokens_for_other_methods_enabled() && ! $gateway->payment_methods[ WC_Stripe_UPE_Payment_Method_Sepa::STRIPE_ID ]->is_enabled()
&& ( $gateway->payment_methods[ WC_Stripe_UPE_Payment_Method_Ideal::STRIPE_ID ]->is_enabled()
|| $gateway->payment_methods[ WC_Stripe_UPE_Payment_Method_Bancontact::STRIPE_ID ]->is_enabled() ) ) {
if ( $gateway->is_sepa_tokens_for_other_methods_enabled() ) {
if ( $gateway->is_spe_enabled() ) {
$payment_methods[] = $customer->get_payment_methods( WC_Stripe_UPE_Payment_Method_Sepa::STRIPE_ID );
} else {
if ( ! $gateway->payment_methods[ WC_Stripe_UPE_Payment_Method_Sepa::STRIPE_ID ]->is_enabled()
&& ( $gateway->payment_methods[ WC_Stripe_UPE_Payment_Method_Ideal::STRIPE_ID ]->is_enabled()
|| $gateway->payment_methods[ WC_Stripe_UPE_Payment_Method_Bancontact::STRIPE_ID ]->is_enabled() ) ) {
$payment_methods[] = $customer->get_payment_methods( WC_Stripe_UPE_Payment_Method_Sepa::STRIPE_ID );
}
}
}

$payment_methods = array_merge( ...$payment_methods );
Expand All @@ -332,7 +337,7 @@ public function woocommerce_get_customer_upe_payment_tokens( $tokens, $user_id,
$payment_method_type = $this->get_original_payment_method_type( $payment_method );

// The corresponding method for the payment method type is not enabled, skipping.
if ( ! $gateway->payment_methods[ $payment_method_type ]->is_enabled() ) {
if ( ! $gateway->is_spe_enabled() && ! $gateway->payment_methods[ $payment_method_type ]->is_enabled() ) {
continue;
}

Expand All @@ -351,6 +356,7 @@ public function woocommerce_get_customer_upe_payment_tokens( $tokens, $user_id,
unset( $stored_tokens[ $payment_method->id ] );
}
}

add_action( 'woocommerce_get_customer_payment_tokens', [ $this, 'woocommerce_get_customer_payment_tokens' ], 10, 3 );

remove_action( 'woocommerce_payment_token_deleted', [ $this, 'woocommerce_payment_token_deleted' ], 10, 2 );
Expand Down
Loading