diff --git a/changelog/add-wechat-pay-response-handling b/changelog/add-wechat-pay-response-handling new file mode 100644 index 00000000000..0ae410c1e3c --- /dev/null +++ b/changelog/add-wechat-pay-response-handling @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add WeChat Pay response handling. diff --git a/client/checkout/api/index.js b/client/checkout/api/index.js index e70765c551b..2f8551eef7e 100644 --- a/client/checkout/api/index.js +++ b/client/checkout/api/index.js @@ -142,7 +142,7 @@ export default class WCPayAPI { */ confirmIntent( redirectUrl, shouldSavePaymentMethod = false ) { const partials = redirectUrl.match( - /#wcpay-confirm-(pi|si):(.+):(.+):(.+)$/ + /#wcpay-confirm-(pi|si):(.+):(.+):(.+):(.+)$/ ); if ( ! partials ) { @@ -153,7 +153,7 @@ export default class WCPayAPI { let orderId = partials[ 2 ]; const clientSecret = partials[ 3 ]; const nonce = partials[ 4 ]; - + const paymentMethodType = partials[ 5 ]; const orderPayIndex = redirectUrl.indexOf( 'order-pay' ); const isOrderPage = orderPayIndex > -1; @@ -199,6 +199,18 @@ export default class WCPayAPI { ).confirmCardPayment( clientSecret ); } + if ( paymentMethodType === 'wechat_pay' ) { + const confirmPayment = await stripe.confirmWechatPayPayment( + clientSecret, + { + payment_method_options: { + wechat_pay: { client: 'web' }, + }, + } + ); + return confirmPayment; + } + // When not dealing with a setup intent or woopay we need to force an account // specific request in Stripe. const stripeWithForcedAccountRequest = await this.getStripe( true ); @@ -211,6 +223,20 @@ export default class WCPayAPI { confirmPaymentOrSetup() // ToDo: Switch to an async function once it works with webpack. .then( ( result ) => { + let paymentError = null; + if ( result.paymentIntent?.last_payment_error ) { + paymentError = { + message: + result.paymentIntent.last_payment_error.message, + }; + } + // If a wallet iframe is closed, Stripe doesn't throw an error, but the intent status will be requires_action. + if ( result.paymentIntent?.status === 'requires_action' ) { + paymentError = { + message: 'Payment requires additional action.', + }; + } + const intentId = ( result.paymentIntent && result.paymentIntent.id ) || ( result.setupIntent && result.setupIntent.id ) || @@ -238,11 +264,11 @@ export default class WCPayAPI { : 'false', } ); - return [ ajaxCall, result.error ]; + return [ ajaxCall, paymentError, result.error ]; } ) - .then( ( [ verificationCall, originalError ] ) => { - if ( originalError ) { - throw originalError; + .then( ( [ verificationCall, paymentError, resultError ] ) => { + if ( resultError ) { + throw resultError; } return verificationCall.then( ( response ) => { @@ -255,6 +281,10 @@ export default class WCPayAPI { throw result.error; } + if ( paymentError ) { + throw paymentError; + } + return result.return_url; } ); } ) diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php index 357c6323d51..02d6924bc55 100644 --- a/includes/class-wc-payment-gateway-wcpay.php +++ b/includes/class-wc-payment-gateway-wcpay.php @@ -1687,11 +1687,12 @@ public function process_payment_for_order( $cart, $payment_information, $schedul // Include a new nonce for update_order_status to ensure the update order // status call works when a guest user creates an account during checkout. 'redirect' => sprintf( - '#wcpay-confirm-%s:%s:%s:%s', + '#wcpay-confirm-%s:%s:%s:%s:%s', $payment_needed ? 'pi' : 'si', $order_id, $client_secret, - wp_create_nonce( 'wcpay_update_order_status_nonce' ) + wp_create_nonce( 'wcpay_update_order_status_nonce' ), + $intent->get_payment_method_type(), ), // Include the payment method ID so the Blocks integration can save cards. 'payment_method' => $payment_information->get_payment_method(), diff --git a/includes/class-wc-payments-order-service.php b/includes/class-wc-payments-order-service.php index 395b430fb43..fcc395413b6 100644 --- a/includes/class-wc-payments-order-service.php +++ b/includes/class-wc-payments-order-service.php @@ -180,7 +180,12 @@ public function update_order_status_from_intent( $order, $intent ) { break; case Intent_Status::REQUIRES_ACTION: case Intent_Status::REQUIRES_PAYMENT_METHOD: - $this->mark_payment_started( $order, $intent_data ); + if ( ! empty( $intent_data['error'] ) ) { + $this->unlock_order_payment( $order ); + $this->mark_payment_failed( $order, $intent_data['intent_id'], $intent_data['intent_status'], $intent_data['charge_id'], $intent_data['error']['message'] ); + } else { + $this->mark_payment_started( $order, $intent_data ); + } break; default: Logger::error( 'Uncaught payment intent status of ' . $intent_data['intent_status'] . ' passed for order id: ' . $order->get_id() ); @@ -2056,6 +2061,7 @@ private function get_intent_data( WC_Payments_API_Abstract_Intention $intent ): if ( $intent instanceof WC_Payments_API_Payment_Intention ) { $charge = $intent->get_charge(); $intent_data['charge_id'] = $charge ? $charge->get_id() : null; + $intent_data['error'] = $intent->get_last_payment_error(); } return $intent_data; diff --git a/includes/class-wc-payments-webhook-processing-service.php b/includes/class-wc-payments-webhook-processing-service.php index 492c4da79be..404c7c6d9cc 100644 --- a/includes/class-wc-payments-webhook-processing-service.php +++ b/includes/class-wc-payments-webhook-processing-service.php @@ -426,6 +426,7 @@ private function process_webhook_payment_intent_failed( $event_body ) { Payment_Method::CARD_PRESENT, Payment_Method::US_BANK_ACCOUNT, Payment_Method::BECS, + Payment_Method::WECHAT_PAY, ]; if ( empty( $payment_method_type ) || ! in_array( $payment_method_type, $actionable_methods, true ) ) { diff --git a/tests/unit/test-class-wc-payment-gateway-wcpay-process-payment.php b/tests/unit/test-class-wc-payment-gateway-wcpay-process-payment.php index 39e94cba660..49b320b6b0d 100644 --- a/tests/unit/test-class-wc-payment-gateway-wcpay-process-payment.php +++ b/tests/unit/test-class-wc-payment-gateway-wcpay-process-payment.php @@ -970,7 +970,7 @@ public function test_intent_status_requires_action() { // Assert: Returning correct array. $this->assertEquals( 'success', $result['result'] ); $this->assertEquals( - '#wcpay-confirm-pi:' . $order_id . ':' . $secret . ':' . wp_create_nonce( 'wcpay_update_order_status_nonce' ), + '#wcpay-confirm-pi:' . $order_id . ':' . $secret . ':' . wp_create_nonce( 'wcpay_update_order_status_nonce' ) . ':' . $intent->get_payment_method_type(), $result['redirect'] ); } @@ -1088,7 +1088,7 @@ public function test_setup_intent_status_requires_action() { // Assert: Returning correct array. $this->assertEquals( 'success', $result['result'] ); $this->assertEquals( - '#wcpay-confirm-si:' . $order_id . ':' . $secret . ':' . wp_create_nonce( 'wcpay_update_order_status_nonce' ), + '#wcpay-confirm-si:' . $order_id . ':' . $secret . ':' . wp_create_nonce( 'wcpay_update_order_status_nonce' ) . ':' . $intent->get_payment_method_type(), $result['redirect'] ); }