Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
@@ -0,0 +1,4 @@
Significance: patch
Type: update

refactor: ECE to use confirmation tokens instead of payment methods
4 changes: 2 additions & 2 deletions client/express-checkout/event-handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export const onConfirmHandler = async (
return abortPayment( submitError.message );
}

const { paymentMethod, error } = await stripe.createPaymentMethod( {
const { confirmationToken, error } = await stripe.createConfirmationToken( {
elements,
} );

Expand All @@ -123,7 +123,7 @@ export const onConfirmHandler = async (
// so that we make it harder for external plugins to modify or intercept checkout data.
...transformStripePaymentMethodForStoreApi(
event,
paymentMethod.id
confirmationToken.id
),
extensions: applyFilters(
'wcpay.express-checkout.cart-place-order-extension-data',
Expand Down
8 changes: 4 additions & 4 deletions client/express-checkout/transformers/stripe-to-wc.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ export const transformStripeShippingAddressForStoreApi = (
* Transform order data from Stripe's object to the expected format for WC.
*
* @param {Object} paymentData Stripe's order object.
* @param {string} paymentMethodId Stripe's payment method id.
* @param {string} confirmationTokenId Stripe's confirmation token id.
*
* @return {Object} Order object in the format WooCommerce expects.
*/
export const transformStripePaymentMethodForStoreApi = (
paymentData,
paymentMethodId
confirmationTokenId
) => {
const name = paymentData.billingDetails?.name || '';
const billing = paymentData.billingDetails?.address ?? {};
Expand Down Expand Up @@ -82,8 +82,8 @@ export const transformStripePaymentMethodForStoreApi = (
value: window.wcpayFraudPreventionToken ?? '',
},
{
key: 'wcpay-payment-method',
value: paymentMethodId,
key: 'wcpay-confirmation-token',
value: confirmationTokenId,
},
{
key: 'express_payment_type',
Expand Down
12 changes: 6 additions & 6 deletions client/express-checkout/utils/normalize.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ export const normalizeLineItems = ( displayItems ) => {
* Normalize order data from Stripe's object to the expected format for WC.
*
* @param {Object} event Stripe's event object.
* @param {string} paymentMethodId Stripe's payment method id.
* @param {string} confirmationTokenId Stripe's confirmation token id.
*
* @return {Object} Order object in the format WooCommerce expects.
*/
export const normalizeOrderData = ( event, paymentMethodId ) => {
export const normalizeOrderData = ( event, confirmationTokenId ) => {
const name = event?.billingDetails?.name;
const email = event?.billingDetails?.email ?? '';
const billing = event?.billingDetails?.address ?? {};
Expand Down Expand Up @@ -70,7 +70,7 @@ export const normalizeOrderData = ( event, paymentMethodId ) => {
payment_method: 'woocommerce_payments',
ship_to_different_address: 1,
terms: 1,
'wcpay-payment-method': paymentMethodId,
'wcpay-confirmation-token': confirmationTokenId,
express_payment_type: event?.expressPaymentType,
'wcpay-fraud-prevention-token': fraudPreventionTokenValue,
};
Expand All @@ -80,14 +80,14 @@ export const normalizeOrderData = ( event, paymentMethodId ) => {
* Normalize Pay for Order data from Stripe's object to the expected format for WC.
*
* @param {Object} event Stripe's event object.
* @param {string} paymentMethodId Stripe's payment method id.
* @param {string} confirmationTokenId Stripe's confirmation token id.
*
* @return {Object} Order object in the format WooCommerce expects.
*/
export const normalizePayForOrderData = ( event, paymentMethodId ) => {
export const normalizePayForOrderData = ( event, confirmationTokenId ) => {
return {
payment_method: 'woocommerce_payments',
'wcpay-payment-method': paymentMethodId,
'wcpay-confirmation-token': confirmationTokenId,
express_payment_type: event?.expressPaymentType,
'wcpay-fraud-prevention-token': window.wcpayFraudPreventionToken ?? '',
};
Expand Down
5 changes: 3 additions & 2 deletions includes/class-payment-information.php
Original file line number Diff line number Diff line change
Expand Up @@ -287,14 +287,15 @@ public static function from_payment_request(
}

/**
* Extracts the payment method from the provided request.
* Extracts the payment method or confirmation token from the provided request.
*
* @param array $request Associative array containing payment request information.
*
* @return string
*/
public static function get_payment_method_from_request( array $request ): string {
foreach ( [ 'wcpay-payment-method', 'wcpay-payment-method-sepa' ] as $key ) {
// Check for confirmation token first (new ECE flow), then fall back to payment method (legacy flow).
foreach ( [ 'wcpay-confirmation-token', 'wcpay-payment-method', 'wcpay-payment-method-sepa' ] as $key ) {
if ( ! empty( $request[ $key ] ) ) {
$normalized = wc_clean( $request[ $key ] );
return is_string( $normalized ) ? $normalized : '';
Expand Down
7 changes: 6 additions & 1 deletion includes/class-wc-payment-gateway-wcpay.php
Original file line number Diff line number Diff line change
Expand Up @@ -1549,7 +1549,12 @@ public function process_payment_for_order( $cart, $payment_information, $schedul
$request = Create_And_Confirm_Intention::create();
$request->set_amount( $converted_amount );
$request->set_currency_code( $currency );
$request->set_payment_method( $payment_information->get_payment_method() );
$payment_method_id = $payment_information->get_payment_method();
if ( str_starts_with( $payment_method_id, 'ctoken_' ) ) {
$request->set_confirmation_token( $payment_method_id );
} else {
$request->set_payment_method( $payment_method_id );
}
$request->set_customer( $customer_id );
$request->set_capture_method( $payment_information->is_using_manual_capture() );
$request->set_metadata( $metadata );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ class Create_And_Confirm_Intention extends Create_Intention {
'amount',
'currency',
'payment_method',
'confirmation_token',
'payment_method_update_data',
'return_url',
];

const REQUIRED_PARAMS = [
'amount',
'currency',
'payment_method',
'customer',
'metadata',
];
Comment on lines 28 to 33
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

Removing payment_method from REQUIRED_PARAMS means the request can now be sent without either a payment_method or a confirmation_token, which would likely fail at the Stripe API level. Consider adding custom validation in the get_params() method or before calling send() to ensure at least one of these parameters is set. For example, you could override get_params() to check that either payment_method or confirmation_token is present in the params before the request is sent.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

send() and get_params() are final

Expand Down Expand Up @@ -114,7 +114,7 @@ public function set_cvc_confirmation( $cvc_confirmation = null ) {
/**
* Return URL setter.
*
* @param string $return_url The URL to redirect the customer back to after they authenticate their payment on the payment methods site.
* @param string $return_url The URL to redirect the customer back to after they authenticate their payment on the payment method's site.
*/
public function set_return_url( $return_url ) {
$this->set_param( 'return_url', $return_url );
Expand Down
15 changes: 13 additions & 2 deletions includes/core/server/request/class-create-intention.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ public function get_method(): string {
}

/**
* Payment method setter.
* Payment method or confirmation token setter.
*
* @param string $payment_method_id ID of payment method to process charge with.
* @param string $payment_method_id ID of payment method or confirmation token to process charge with.
*
* @return void
* @throws Invalid_Request_Parameter_Exception
Expand All @@ -59,6 +59,17 @@ public function set_payment_method( string $payment_method_id ) {
$this->set_param( 'payment_method', $payment_method_id );
}

/**
* Confirmation token setter.
*
* @param string $confirmation_token The confirmation token.
*
* @return void
*/
public function set_confirmation_token( string $confirmation_token ) {
$this->set_param( 'confirmation_token', $confirmation_token );
}

/**
* Payment methods type setter.
*
Expand Down
Loading