mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-08-30 05:00:51 +08:00
🧹 Remove reliance on the session in favor of passing the token directly
This commit is contained in:
parent
ba7b01dfd6
commit
99cd8e4aeb
3 changed files with 140 additions and 238 deletions
|
@ -153,26 +153,3 @@ class WP_HTML_Tag_Processor {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* WooCommerce Session stubs for Psalm
|
|
||||||
*/
|
|
||||||
class WC_Session {
|
|
||||||
/**
|
|
||||||
* Get session cookie.
|
|
||||||
*
|
|
||||||
* @return string|false
|
|
||||||
*/
|
|
||||||
public function get_session_cookie() {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set customer session cookie.
|
|
||||||
*
|
|
||||||
* @param bool $set Whether to set the cookie.
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function set_customer_session_cookie( $set ) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -245,59 +245,16 @@ class AxoGateway extends WC_Payment_Gateway {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for stored 3DS errors.
|
// Check for tokens to determine if this is a 3DS return or initial payment.
|
||||||
$stored_error = $this->get_and_clear_stored_error();
|
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||||
if ( $stored_error ) {
|
$axo_nonce = wc_clean( wp_unslash( $_POST['axo_nonce'] ?? '' ) );
|
||||||
return array(
|
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||||
'result' => 'failure',
|
$token_param = wc_clean( wp_unslash( $_GET['token'] ?? '' ) );
|
||||||
'message' => $stored_error['message'],
|
|
||||||
);
|
if ( empty( $axo_nonce ) && ! empty( $token_param ) ) {
|
||||||
|
return $this->process_3ds_return( $wc_order, $token_param );
|
||||||
}
|
}
|
||||||
|
|
||||||
$existing_order = $this->session_handler->order();
|
|
||||||
|
|
||||||
if ( $existing_order ) {
|
|
||||||
// Check if this session order belongs to current WC order and we're in 3DS context.
|
|
||||||
$session_order_belongs_to_current_wc_order = $this->session_order_matches_wc_order( $existing_order, $wc_order );
|
|
||||||
$is_3ds_context = $this->is_3ds_context();
|
|
||||||
|
|
||||||
if ( $session_order_belongs_to_current_wc_order && $is_3ds_context ) {
|
|
||||||
// This is a legitimate 3DS return for the current order.
|
|
||||||
try {
|
|
||||||
/**
|
|
||||||
* This filter controls if the method 'process()' from OrderProcessor will be called.
|
|
||||||
*/
|
|
||||||
$process = apply_filters( 'woocommerce_paypal_payments_before_order_process', true, $this, $wc_order );
|
|
||||||
if ( $process ) {
|
|
||||||
$this->order_processor->process_captured_and_authorized( $wc_order, $existing_order );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear session after successful processing.
|
|
||||||
$this->session_handler->destroy_session_data();
|
|
||||||
WC()->cart->empty_cart();
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'result' => 'success',
|
|
||||||
'redirect' => $this->get_return_url( $wc_order ),
|
|
||||||
);
|
|
||||||
|
|
||||||
} catch ( Exception $exception ) {
|
|
||||||
// Handle 3DS processing failures with universal error approach.
|
|
||||||
// Clear session data since payment failed.
|
|
||||||
$this->session_handler->destroy_session_data();
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'result' => 'failure',
|
|
||||||
'message' => $this->get_user_friendly_error_message( $exception ),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Session order doesn't belong to current WC order OR not 3DS context.
|
|
||||||
$this->session_handler->destroy_session_data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No existing order or cleared session - this is an initial payment.
|
|
||||||
try {
|
try {
|
||||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||||
$fastlane_member = wc_clean( wp_unslash( $_POST['fastlane_member'] ?? '' ) );
|
$fastlane_member = wc_clean( wp_unslash( $_POST['fastlane_member'] ?? '' ) );
|
||||||
|
@ -308,31 +265,25 @@ class AxoGateway extends WC_Payment_Gateway {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The `axo_nonce` is not a WP nonce, but a card-token generated by the JS SDK.
|
// The `axo_nonce` is not a WP nonce, but a card-token generated by the JS SDK.
|
||||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
if ( empty( $axo_nonce ) ) {
|
||||||
$token = wc_clean( wp_unslash( $_POST['axo_nonce'] ?? '' ) );
|
|
||||||
|
|
||||||
// Enhanced token validation with universal error handling.
|
|
||||||
if ( empty( $token ) ) {
|
|
||||||
// Universal error return.
|
|
||||||
return array(
|
return array(
|
||||||
'result' => 'failure',
|
'result' => 'failure',
|
||||||
'message' => $this->is_3ds_context()
|
'message' => __( 'No payment token provided. Please try again.', 'woocommerce-paypal-payments' ),
|
||||||
? __( 'Payment session expired. Please try your payment again.', 'woocommerce-paypal-payments' )
|
|
||||||
: __( 'No payment token provided. Please try again.', 'woocommerce-paypal-payments' ),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$order = $this->create_paypal_order( $wc_order, $token );
|
$order = $this->create_paypal_order( $wc_order, $axo_nonce );
|
||||||
|
|
||||||
// Check if 3DS verification is required.
|
// Check if 3DS verification is required.
|
||||||
$payer_action = $this->get_payer_action_url( $order );
|
$payer_action = $this->get_payer_action_url( $order );
|
||||||
|
|
||||||
// If 3DS verification is required, store order and redirect.
|
// If 3DS verification is required, redirect with token in return URL.
|
||||||
if ( $payer_action ) {
|
if ( $payer_action ) {
|
||||||
// Store the order in session before 3DS redirect.
|
$return_url = add_query_arg(
|
||||||
$this->session_handler->replace_order( $order );
|
'token',
|
||||||
|
$order->id(),
|
||||||
$return_url = home_url( WC_AJAX::get_endpoint( ReturnUrlEndpoint::ENDPOINT ) );
|
home_url( WC_AJAX::get_endpoint( ReturnUrlEndpoint::ENDPOINT ) )
|
||||||
|
);
|
||||||
|
|
||||||
$redirect_url = add_query_arg(
|
$redirect_url = add_query_arg(
|
||||||
'redirect_uri',
|
'redirect_uri',
|
||||||
|
@ -359,7 +310,8 @@ class AxoGateway extends WC_Payment_Gateway {
|
||||||
$this->order_processor->process_captured_and_authorized( $wc_order, $order );
|
$this->order_processor->process_captured_and_authorized( $wc_order, $order );
|
||||||
}
|
}
|
||||||
} catch ( Exception $exception ) {
|
} catch ( Exception $exception ) {
|
||||||
// Error handling for initial payment failures.
|
// Error handling for payment failures.
|
||||||
|
$this->logger->error( '[AXO] Payment processing failed: ' . $exception->getMessage() );
|
||||||
return array(
|
return array(
|
||||||
'result' => 'failure',
|
'result' => 'failure',
|
||||||
'message' => $this->get_user_friendly_error_message( $exception ),
|
'message' => $this->get_user_friendly_error_message( $exception ),
|
||||||
|
@ -375,20 +327,51 @@ class AxoGateway extends WC_Payment_Gateway {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get and clear stored payment errors.
|
* Process 3DS return scenario.
|
||||||
|
*
|
||||||
|
* @param WC_Order $wc_order The WooCommerce order.
|
||||||
|
* @param string $token The PayPal order token.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function get_and_clear_stored_error() {
|
protected function process_3ds_return( WC_Order $wc_order, string $token ) : array {
|
||||||
if ( ! WC()->session ) {
|
try {
|
||||||
return null;
|
$paypal_order = $this->order_endpoint->order( $token );
|
||||||
|
|
||||||
|
if ( ! $paypal_order->status()->is( OrderStatus::COMPLETED ) ) {
|
||||||
|
return array(
|
||||||
|
'result' => 'failure',
|
||||||
|
'message' => __( '3D Secure authentication was not completed successfully. Please try again.', 'woocommerce-paypal-payments' ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This filter controls if the method 'process()' from OrderProcessor will be called.
|
||||||
|
* So you can implement your own for example on subscriptions
|
||||||
|
*
|
||||||
|
* - true bool controls execution of 'OrderProcessor::process()'
|
||||||
|
* - $this \WC_Payment_Gateway
|
||||||
|
* - $wc_order \WC_Order
|
||||||
|
*/
|
||||||
|
$process = apply_filters( 'woocommerce_paypal_payments_before_order_process', true, $this, $wc_order );
|
||||||
|
if ( $process ) {
|
||||||
|
$this->order_processor->process_captured_and_authorized( $wc_order, $paypal_order );
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch ( Exception $exception ) {
|
||||||
|
$this->logger->error( '[AXO] 3DS return processing failed: ' . $exception->getMessage() );
|
||||||
|
return array(
|
||||||
|
'result' => 'failure',
|
||||||
|
'message' => $this->get_user_friendly_error_message( $exception ),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$stored_error = WC()->session->get( 'ppcp_payment_error' );
|
WC()->cart->empty_cart();
|
||||||
if ( $stored_error ) {
|
|
||||||
WC()->session->__unset( 'ppcp_payment_error' );
|
|
||||||
return $stored_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return array(
|
||||||
|
'result' => 'success',
|
||||||
|
'redirect' => $this->get_return_url( $wc_order ),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -423,56 +406,21 @@ class AxoGateway extends WC_Payment_Gateway {
|
||||||
* Extract payer action URL from PayPal order.
|
* Extract payer action URL from PayPal order.
|
||||||
*/
|
*/
|
||||||
private function get_payer_action_url( Order $order ) {
|
private function get_payer_action_url( Order $order ) {
|
||||||
foreach ( $order->links() as $link ) {
|
$links = $order->links();
|
||||||
if ( $link->rel === 'payer-action' ) {
|
|
||||||
return $link->href;
|
if ( ! $links ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ( $links as $link ) {
|
||||||
|
if ( isset( $link->rel ) && $link->rel === 'payer-action' ) {
|
||||||
|
return $link->href ?? '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if session order belongs to current WC order.
|
|
||||||
*
|
|
||||||
* @param Order $paypal_order The PayPal order from session.
|
|
||||||
* @param WC_Order $wc_order The current WooCommerce order.
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function session_order_matches_wc_order( Order $paypal_order, WC_Order $wc_order ): bool {
|
|
||||||
$paypal_custom_id = $paypal_order->purchase_units()[0]->custom_id() ?? '';
|
|
||||||
$wc_order_id = (string) $wc_order->get_id();
|
|
||||||
|
|
||||||
return $paypal_custom_id === $wc_order_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if we're in a 3DS return context.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function is_3ds_context(): bool {
|
|
||||||
// Check referer for PayPal.
|
|
||||||
$referer = $_SERVER['HTTP_REFERER'] ?? '';
|
|
||||||
if ( strpos( $referer, 'paypal.com' ) !== false ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for 3DS-specific parameters.
|
|
||||||
$three_ds_params = array( 'liability_shift', 'state', 'code', 'authentication_state' );
|
|
||||||
foreach ( $three_ds_params as $param ) {
|
|
||||||
if ( isset( $_GET[ $param ] ) ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we're being called by ReturnUrlEndpoint.
|
|
||||||
if ( isset( $_GET['wc-ajax'] ) && $_GET['wc-ajax'] === 'ppc-return-url' ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new PayPal order from the existing WC_Order instance.
|
* Create a new PayPal order from the existing WC_Order instance.
|
||||||
*
|
*
|
||||||
|
@ -505,7 +453,8 @@ class AxoGateway extends WC_Payment_Gateway {
|
||||||
null,
|
null,
|
||||||
self::ID,
|
self::ID,
|
||||||
array(),
|
array(),
|
||||||
$payment_source
|
$payment_source,
|
||||||
|
$wc_order
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,71 +88,37 @@ class ReturnUrlEndpoint {
|
||||||
WC()->session->set_customer_session_cookie( true );
|
WC()->session->set_customer_session_cookie( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we have a PayPal order in session (3DS return).
|
// Check for token parameter - required for all returns.
|
||||||
$order = $this->session_handler->order();
|
|
||||||
|
|
||||||
if ( $order ) {
|
|
||||||
try {
|
|
||||||
// Handle 3DS capture before processing.
|
|
||||||
$order = $this->handle_3ds_return( $order );
|
|
||||||
|
|
||||||
$wc_order_id = (int) $order->purchase_units()[0]->custom_id();
|
|
||||||
$wc_order = wc_get_order( $wc_order_id );
|
|
||||||
|
|
||||||
if ( ! $wc_order || ! is_a( $wc_order, \WC_Order::class ) ) {
|
|
||||||
wc_add_notice( __( 'Order information is missing. Please try placing your order again.', 'woocommerce-paypal-payments' ), 'error' );
|
|
||||||
wp_safe_redirect( wc_get_checkout_url() );
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
$payment_gateway = $this->get_payment_gateway( $wc_order->get_payment_method() );
|
|
||||||
if ( ! $payment_gateway ) {
|
|
||||||
wc_add_notice( __( 'Payment gateway is unavailable. Please try again or contact support.', 'woocommerce-paypal-payments' ), 'error' );
|
|
||||||
wp_safe_redirect( wc_get_checkout_url() );
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = $payment_gateway->process_payment( $wc_order_id );
|
|
||||||
|
|
||||||
if ( isset( $result['result'] ) && $result['result'] === 'success' ) {
|
|
||||||
wp_safe_redirect( $result['redirect'] );
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
wc_add_notice( __( 'Payment processing failed. Please try again or contact support.', 'woocommerce-paypal-payments' ), 'error' );
|
|
||||||
wp_safe_redirect( wc_get_checkout_url() );
|
|
||||||
exit();
|
|
||||||
|
|
||||||
} catch ( Exception $e ) {
|
|
||||||
wc_add_notice( __( 'There was an error processing your payment. Please try again or contact support.', 'woocommerce-paypal-payments' ), 'error' );
|
|
||||||
wp_safe_redirect( wc_get_checkout_url() );
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No order in session - handle regular PayPal returns.
|
|
||||||
if ( ! isset( $_GET['token'] ) ) {
|
if ( ! isset( $_GET['token'] ) ) {
|
||||||
wc_add_notice( __( 'Payment session expired. Please try placing your order again.', 'woocommerce-paypal-payments' ), 'error' );
|
wc_add_notice( __( 'Payment session expired. Please try placing your order again.', 'woocommerce-paypal-payments' ), 'error' );
|
||||||
wp_safe_redirect( wc_get_checkout_url() );
|
wp_safe_redirect( wc_get_checkout_url() );
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle regular PayPal returns (non-3DS).
|
|
||||||
// phpcs:enable WordPress.Security.NonceVerification.Recommended
|
|
||||||
$token = sanitize_text_field( wp_unslash( $_GET['token'] ) );
|
$token = sanitize_text_field( wp_unslash( $_GET['token'] ) );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$order = $this->order_endpoint->order( $token );
|
$order = $this->order_endpoint->order( $token );
|
||||||
} catch ( Exception $exception ) {
|
} catch ( Exception $exception ) {
|
||||||
|
$this->logger->warning( "Return URL endpoint failed to fetch order $token: " . $exception->getMessage() );
|
||||||
wc_add_notice( __( 'Could not retrieve payment information. Please try again.', 'woocommerce-paypal-payments' ), 'error' );
|
wc_add_notice( __( 'Could not retrieve payment information. Please try again.', 'woocommerce-paypal-payments' ), 'error' );
|
||||||
wp_safe_redirect( wc_get_checkout_url() );
|
wp_safe_redirect( wc_get_checkout_url() );
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $order->status()->is( OrderStatus::APPROVED ) || $order->status()->is( OrderStatus::COMPLETED ) ) {
|
// Handle 3DS completion if needed.
|
||||||
$this->session_handler->replace_order( $order );
|
if ( $this->needs_3ds_completion( $order ) ) {
|
||||||
|
try {
|
||||||
|
$order = $this->complete_3ds_verification( $order );
|
||||||
|
} catch ( Exception $e ) {
|
||||||
|
$this->logger->warning( "3DS completion failed for order $token: " . $e->getMessage() );
|
||||||
|
wc_add_notice( $this->get_3ds_error_message( $e ), 'error' );
|
||||||
|
wp_safe_redirect( wc_get_checkout_url() );
|
||||||
|
exit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get WooCommerce order ID.
|
||||||
$wc_order_id = (int) $order->purchase_units()[0]->custom_id();
|
$wc_order_id = (int) $order->purchase_units()[0]->custom_id();
|
||||||
if ( ! $wc_order_id ) {
|
if ( ! $wc_order_id ) {
|
||||||
// We cannot finish processing here without WC order, but at least go into the continuation mode.
|
// We cannot finish processing here without WC order, but at least go into the continuation mode.
|
||||||
|
@ -192,7 +158,9 @@ class ReturnUrlEndpoint {
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->logger->info( 'ReturnUrlEndpoint calling process_payment for gateway: ' . get_class( $payment_gateway ) );
|
||||||
$success = $payment_gateway->process_payment( $wc_order_id );
|
$success = $payment_gateway->process_payment( $wc_order_id );
|
||||||
|
$this->logger->info( 'ReturnUrlEndpoint process_payment result: ' . wp_json_encode( $success ) );
|
||||||
|
|
||||||
if ( isset( $success['result'] ) && 'success' === $success['result'] ) {
|
if ( isset( $success['result'] ) && 'success' === $success['result'] ) {
|
||||||
add_filter(
|
add_filter(
|
||||||
|
@ -212,62 +180,70 @@ class ReturnUrlEndpoint {
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if order needs 3DS completion.
|
||||||
|
*
|
||||||
|
* @param mixed $order The PayPal order.
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function needs_3ds_completion( $order ): bool {
|
||||||
|
// If order is still CREATED after 3DS redirect, it needs to be captured.
|
||||||
|
return $order->status()->is( OrderStatus::CREATED );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle 3DS return and capture order if needed.
|
* Complete 3DS verification by capturing the order.
|
||||||
*
|
*
|
||||||
* @param mixed $order The PayPal order.
|
* @param mixed $order The PayPal order.
|
||||||
* @return mixed The processed order.
|
* @return mixed The processed order.
|
||||||
|
* @throws Exception When 3DS completion fails.
|
||||||
*/
|
*/
|
||||||
private function handle_3ds_return( $order ) {
|
private function complete_3ds_verification( $order ) {
|
||||||
// If order is still CREATED after 3DS, it needs to be captured.
|
try {
|
||||||
if ( $order->status()->is( OrderStatus::CREATED ) ) {
|
// Capture the order.
|
||||||
try {
|
$captured_order = $this->order_endpoint->capture( $order );
|
||||||
// Capture the order.
|
|
||||||
$captured_order = $this->order_endpoint->capture( $order );
|
|
||||||
|
|
||||||
// Check if capture actually succeeded vs. payment declined.
|
// Check if capture actually succeeded vs. payment declined.
|
||||||
if ( $captured_order->status()->is( OrderStatus::COMPLETED ) ) {
|
if ( $captured_order->status()->is( OrderStatus::COMPLETED ) ) {
|
||||||
// Update session with captured order.
|
return $captured_order;
|
||||||
$this->session_handler->replace_order( $captured_order );
|
} else {
|
||||||
return $captured_order;
|
// Capture API succeeded but payment was declined.
|
||||||
} else {
|
throw new Exception( __( 'Payment was declined by the payment provider. Please try a different payment method.', 'woocommerce-paypal-payments' ) );
|
||||||
// Capture API succeeded but payment was declined.
|
|
||||||
throw new Exception( __( 'Payment was declined by the payment provider. Please try a different payment method.', 'woocommerce-paypal-payments' ) );
|
|
||||||
}
|
|
||||||
} catch ( DomainException $e ) {
|
|
||||||
// Handle 3DS authentication failures (Test Case 4: Unavailable).
|
|
||||||
// Clear session data since authentication failed.
|
|
||||||
$this->session_handler->destroy_session_data();
|
|
||||||
|
|
||||||
// Use native WooCommerce error handling.
|
|
||||||
wc_add_notice( __( '3D Secure authentication was unavailable or failed. Please try a different payment method or contact your bank.', 'woocommerce-paypal-payments' ), 'error' );
|
|
||||||
wp_safe_redirect( wc_get_checkout_url() );
|
|
||||||
exit();
|
|
||||||
|
|
||||||
} catch ( RuntimeException $e ) {
|
|
||||||
if ( strpos( $e->getMessage(), 'declined' ) !== false ||
|
|
||||||
strpos( $e->getMessage(), 'PAYMENT_DENIED' ) !== false ||
|
|
||||||
strpos( $e->getMessage(), 'INSTRUMENT_DECLINED' ) !== false ||
|
|
||||||
strpos( $e->getMessage(), 'Payment provider declined' ) !== false ) {
|
|
||||||
|
|
||||||
$this->session_handler->destroy_session_data();
|
|
||||||
|
|
||||||
wc_add_notice( __( 'Your payment was declined after 3D Secure verification. Please try a different payment method or contact your bank.', 'woocommerce-paypal-payments' ), 'error' );
|
|
||||||
wp_safe_redirect( wc_get_checkout_url() );
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
throw $e;
|
|
||||||
} catch ( Exception $e ) {
|
|
||||||
$this->session_handler->destroy_session_data();
|
|
||||||
wc_add_notice( __( 'There was an error processing your payment. Please try again or contact support.', 'woocommerce-paypal-payments' ), 'error' );
|
|
||||||
wp_safe_redirect( wc_get_checkout_url() );
|
|
||||||
exit();
|
|
||||||
}
|
}
|
||||||
|
} catch ( DomainException $e ) {
|
||||||
|
// Handle 3DS authentication failures.
|
||||||
|
throw new Exception( __( '3D Secure authentication was unavailable or failed. Please try a different payment method or contact your bank.', 'woocommerce-paypal-payments' ) );
|
||||||
|
} catch ( RuntimeException $e ) {
|
||||||
|
if ( strpos( $e->getMessage(), 'declined' ) !== false ||
|
||||||
|
strpos( $e->getMessage(), 'PAYMENT_DENIED' ) !== false ||
|
||||||
|
strpos( $e->getMessage(), 'INSTRUMENT_DECLINED' ) !== false ||
|
||||||
|
strpos( $e->getMessage(), 'Payment provider declined' ) !== false ) {
|
||||||
|
throw new Exception( __( 'Your payment was declined after 3D Secure verification. Please try a different payment method or contact your bank.', 'woocommerce-paypal-payments' ) );
|
||||||
|
}
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user-friendly error message for 3DS failures.
|
||||||
|
*
|
||||||
|
* @param Exception $exception The exception.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function get_3ds_error_message( Exception $exception ): string {
|
||||||
|
$error_message = $exception->getMessage();
|
||||||
|
|
||||||
|
if ( strpos( $error_message, '3D Secure' ) !== false ) {
|
||||||
|
return $error_message;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $order;
|
if ( strpos( $error_message, 'declined' ) !== false ) {
|
||||||
|
return __( 'Your payment was declined after 3D Secure verification. Please try a different payment method or contact your bank.', 'woocommerce-paypal-payments' );
|
||||||
|
}
|
||||||
|
|
||||||
|
return __( 'There was an error processing your payment. Please try again or contact support.', 'woocommerce-paypal-payments' );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the appropriate payment gateway for the given payment method.
|
* Gets the appropriate payment gateway for the given payment method.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue