diff --git a/includes/class-payment-information.php b/includes/class-payment-information.php index 4b4f8a13f04..8548c671443 100644 --- a/includes/class-payment-information.php +++ b/includes/class-payment-information.php @@ -11,6 +11,7 @@ exit; // Exit if accessed directly. } +use WCPay\Constants\Payment_Method; use WCPay\Constants\Payment_Type; use WCPay\Constants\Payment_Initiated_By; use WCPay\Constants\Payment_Capture_Type; @@ -498,4 +499,13 @@ public function set_error( \WP_Error $error ) { public function get_error() { return $this->error; } + + /** + * Returns true if the payment method is an offline payment method, false otherwise. + * + * @return bool True if the payment method is an offline payment method, false otherwise. + */ + public function is_offline_payment_method(): bool { + return in_array( $this->payment_method_stripe_id, Payment_Method::OFFLINE_PAYMENT_METHODS, true ); + } } diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php index cff0e8eedf4..e68ce8b1091 100644 --- a/includes/class-wc-payment-gateway-wcpay.php +++ b/includes/class-wc-payment-gateway-wcpay.php @@ -1786,6 +1786,7 @@ public function process_payment_for_order( $cart, $payment_information, $schedul $processing = []; } + $is_offline_payment_method = $payment_information->is_offline_payment_method(); if ( ! empty( $intent ) ) { if ( ! $intent->is_authorized() ) { $intent_failed = true; @@ -1836,11 +1837,14 @@ public function process_payment_for_order( $cart, $payment_information, $schedul } if ( Intent_Status::REQUIRES_ACTION === $status ) { - if ( isset( $next_action['type'] ) && 'redirect_to_url' === $next_action['type'] && ! empty( $next_action['redirect_to_url']['url'] ) ) { + $next_action_type = $next_action['type'] ?? null; + if ( 'redirect_to_url' === $next_action_type && ! empty( $next_action[ $next_action_type ]['url'] ) ) { $response = [ 'result' => 'success', - 'redirect' => $next_action['redirect_to_url']['url'], + 'redirect' => $next_action[ $next_action_type ]['url'], ]; + } elseif ( 'multibanco_display_details' === $next_action_type && ! empty( $next_action[ $next_action_type ]['hosted_voucher_url'] ) ) { + $order->add_meta_data( 'hosted_voucer_url', $next_action[ $next_action_type ]['hosted_voucher_url'], true ); } else { $response = [ 'result' => 'success', @@ -1862,7 +1866,7 @@ public function process_payment_for_order( $cart, $payment_information, $schedul $this->order_service->attach_intent_info_to_order( $order, $intent ); $this->attach_exchange_info_to_order( $order, $charge_id ); - if ( Intent_Status::SUCCEEDED === $status ) { + if ( Intent_Status::SUCCEEDED === $status || ( Intent_Status::REQUIRES_ACTION === $status && $is_offline_payment_method ) ) { $this->duplicate_payment_prevention_service->remove_session_processing_order( $order->get_id() ); } $this->order_service->update_order_status_from_intent( $order, $intent ); @@ -1870,7 +1874,7 @@ public function process_payment_for_order( $cart, $payment_information, $schedul $this->maybe_add_customer_notification_note( $order, $processing ); - if ( isset( $response ) ) { + if ( isset( $response ) && ! $is_offline_payment_method ) { return $response; } diff --git a/includes/class-wc-payments-order-service.php b/includes/class-wc-payments-order-service.php index eb74cde866f..d3b823dbc5f 100644 --- a/includes/class-wc-payments-order-service.php +++ b/includes/class-wc-payments-order-service.php @@ -8,6 +8,7 @@ use WCPay\Constants\Fraud_Meta_Box_Type; use WCPay\Constants\Order_Status; use WCPay\Constants\Intent_Status; +use WCPay\Constants\Payment_Method; use WCPay\Exceptions\Order_Not_Found_Exception; use WCPay\Fraud_Prevention\Models\Rule; use WCPay\Logger; @@ -180,7 +181,11 @@ 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 ( in_array( $intent->get_payment_method_type(), Payment_Method::OFFLINE_PAYMENT_METHODS, true ) ) { + $this->mark_payment_on_hold( $order, $intent_data ); + } 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() ); @@ -1114,6 +1119,30 @@ private function mark_payment_authorized( $order, $intent_data ) { $this->set_intention_status_for_order( $order, $intent_data['intent_status'] ); } + /** + * Updates an order to on-hold status, while adding a note with a link to the transaction. + * + * @param WC_Order $order Order object. + * @param array $intent_data The intent data associated with this order. + * + * @return void + */ + private function mark_payment_on_hold( $order, $intent_data ) { + $note = $this->generate_payment_started_note( $order, $intent_data['intent_id'] ); + if ( $this->order_note_exists( $order, $note ) ) { + return; + } + + if ( Rule::FRAUD_OUTCOME_ALLOW === $intent_data['fraud_outcome'] ) { + $this->set_fraud_outcome_status_for_order( $order, Rule::FRAUD_OUTCOME_ALLOW ); + $this->set_fraud_meta_box_type_for_order( $order, Fraud_Meta_Box_Type::ALLOW ); + } + + $this->update_order_status( $order, Order_Status::ON_HOLD ); + $order->add_order_note( $note ); + $this->set_intention_status_for_order( $order, $intent_data['intent_status'] ); + } + /** * Updates an order to processing/completed status, while adding a note with a link to the transaction. * diff --git a/includes/constants/class-payment-method.php b/includes/constants/class-payment-method.php index 64848372862..775c4c5c203 100644 --- a/includes/constants/class-payment-method.php +++ b/includes/constants/class-payment-method.php @@ -47,4 +47,8 @@ class Payment_Method extends Base_Constant { self::AFTERPAY, self::KLARNA, ]; + + const OFFLINE_PAYMENT_METHODS = [ + self::MULTIBANCO, + ]; }