From 6a67203525f66a592f284d5e05849b455f99799d Mon Sep 17 00:00:00 2001 From: Zvonimir Maglica Date: Fri, 13 Dec 2024 13:44:44 +0100 Subject: [PATCH] Fix issue where order status and data are not updated when an order is in the processing state and being captured. (#9922) --- ...ndle-error-on-refund-during-manual-capture | 4 +++ includes/class-wc-payment-gateway-wcpay.php | 2 +- includes/class-wc-payments-order-service.php | 15 ++++++++++ .../test-class-wc-payments-order-service.php | 29 +++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 changelog/fix-5671-handle-error-on-refund-during-manual-capture diff --git a/changelog/fix-5671-handle-error-on-refund-during-manual-capture b/changelog/fix-5671-handle-error-on-refund-during-manual-capture new file mode 100644 index 00000000000..016c68f13aa --- /dev/null +++ b/changelog/fix-5671-handle-error-on-refund-during-manual-capture @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fixed an issue where order metadata was not updated when capturing an order in the processing state. diff --git a/includes/class-wc-payment-gateway-wcpay.php b/includes/class-wc-payment-gateway-wcpay.php index 0f53c3dde42..cff7022a1b9 100644 --- a/includes/class-wc-payment-gateway-wcpay.php +++ b/includes/class-wc-payment-gateway-wcpay.php @@ -3373,7 +3373,7 @@ public function capture_charge( $order, $include_level3 = true, $intent_metadata $this->attach_exchange_info_to_order( $order, $charge_id ); if ( Intent_Status::SUCCEEDED === $status ) { - $this->order_service->update_order_status_from_intent( $order, $intent ); + $this->order_service->process_captured_payment( $order, $intent ); } elseif ( $is_authorization_expired ) { $this->order_service->mark_payment_capture_expired( $order, $intent_id, Intent_Status::CANCELED, $charge_id ); } else { diff --git a/includes/class-wc-payments-order-service.php b/includes/class-wc-payments-order-service.php index c563877e830..f685b50debf 100644 --- a/includes/class-wc-payments-order-service.php +++ b/includes/class-wc-payments-order-service.php @@ -190,6 +190,21 @@ public function update_order_status_from_intent( $order, $intent ) { $this->complete_order_processing( $order ); } + /** + * Handles the order state when a payment is captured successfully. + * Unlike `update_order_status_from_intent`, this method does not check the current order status or skip processing + * if the order is already in the "processing" state. This ensures the order status is updated correctly upon a + * successful capture, preventing issues where the capture is not reflected in the order details or transaction screens + * due to the order status being in the processing state. + * + * @param WC_Order $order The order to update. + * @param WC_Payments_API_Abstract_Intention $intent The intent object containing payment or setup data. + */ + public function process_captured_payment( $order, $intent ) { + $this->mark_payment_capture_completed( $order, $intent ); + $this->complete_order_processing( $order, $intent->get_status() ); + } + /** * Updates an order to failed status, while adding a note with a link to the transaction. * diff --git a/tests/unit/test-class-wc-payments-order-service.php b/tests/unit/test-class-wc-payments-order-service.php index 2eeaa50864e..d3fef27ea1f 100644 --- a/tests/unit/test-class-wc-payments-order-service.php +++ b/tests/unit/test-class-wc-payments-order-service.php @@ -1382,4 +1382,33 @@ public function test_add_note_and_metadata_for_refund_partially_refunded(): void WC_Helper_Order::delete_order( $order->get_id() ); } + + public function test_process_captured_payment() { + $order = WC_Helper_Order::create_order(); + $order->save(); + + $intent = WC_Helper_Intention::create_intention( [ 'status' => Intent_Status::SUCCEEDED ] ); + $this->order_service->set_intention_status_for_order( $this->order, Intent_Status::REQUIRES_CAPTURE ); + $this->order_service->set_intent_id_for_order( $order, $intent->get_id() ); + $order->set_status( Order_Status::PROCESSING ); // Let's simulate that order is set to processing, so order status should not interfere with the process. + $order->save(); + + $this->order_service->process_captured_payment( $order, $intent ); + + $this->assertEquals( $intent->get_status(), $this->order_service->get_intention_status_for_order( $order ) ); + + $this->assertTrue( $order->has_status( wc_get_is_paid_statuses() ) ); + + $notes = wc_get_order_notes( [ 'order_id' => $order->get_id() ] ); + $this->assertStringContainsString( 'successfully captured using WooPayments', $notes[0]->content ); + $this->assertStringContainsString( '/payments/transactions/details&id=pi_mock" target="_blank" rel="noopener noreferrer">pi_mock', $notes[0]->content ); + + // Assert: Check that the order was unlocked. + $this->assertFalse( get_transient( 'wcpay_processing_intent_' . $order->get_id() ) ); + + // Assert: Applying the same data multiple times does not cause duplicate actions. + $this->order_service->update_order_status_from_intent( $order, $intent ); + $notes_2 = wc_get_order_notes( [ 'order_id' => $order->get_id() ] ); + $this->assertEquals( count( $notes ), count( $notes_2 ) ); + } }