Skip to content

Commit

Permalink
Allow payment_intent_succeeded webhook to handle orders without inten…
Browse files Browse the repository at this point in the history
…t_id attached (#3118)

* Allow payment_intent_succeeded webhook to handle orders without intent_id attached

Co-authored-by: Daniel Mallory <daniel.mallory@automattic.com>
  • Loading branch information
daquinons and dmallory42 authored Oct 21, 2021
1 parent 0c6f396 commit 67fc075
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 6 deletions.
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* Add - Add compatibility between Multi-Currency and WooCommerce UPS shipping extension.
* Add - Add compatibility between Multi-Currency and WooCommerce FedEx shipping extension.
* Fix - Fix decimal error with shipping calculations with Multi-Currency.
* Fix - Allow payment_intent_succeeded webhook to handle orders without intent_id attached.

= 3.1.0 - 2021-10-06 =
* Fix - Issue affecting analytics for Multi-Currency orders made with a zero-decimal to non-zero decimal conversion.
Expand Down
9 changes: 9 additions & 0 deletions includes/admin/class-wc-rest-payments-webhook-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,15 @@ private function process_webhook_payment_intent_succeeded( $event_body ) {

// Look up the order related to this charge.
$order = $this->wcpay_db->order_from_intent_id( $intent_id );

if ( ! $order ) {
// Retrieving order with order_id in case intent_id was not properly set.
Logger::debug( 'intent_id not found, using order_id to retrieve order' );
$metadata = $this->read_rest_property( $event_object, 'metadata' );
$order_id = $metadata['order_id'];
$order = $this->wcpay_db->order_from_order_id( $order_id );
}

if ( ! $order ) {
throw new Invalid_Payment_Method_Exception(
sprintf(
Expand Down
6 changes: 3 additions & 3 deletions includes/class-wc-payment-gateway-wcpay.php
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,9 @@ public function process_payment_for_order( $cart, $payment_information, $additio
$next_action = $intent['next_action'];
}

$this->attach_intent_info_to_order( $order, $intent_id, $status, $payment_method, $customer_id, $charge_id, $currency );
$this->attach_exchange_info_to_order( $order, $charge_id );

if ( ! empty( $intent ) ) {
if ( ! in_array( $status, self::SUCCESSFUL_INTENT_STATUS, true ) ) {
$intent_failed = true;
Expand Down Expand Up @@ -1099,9 +1102,6 @@ public function process_payment_for_order( $cart, $payment_information, $additio
}
}

$this->attach_intent_info_to_order( $order, $intent_id, $status, $payment_method, $customer_id, $charge_id, $currency );
$this->attach_exchange_info_to_order( $order, $charge_id );

if ( isset( $response ) ) {
return $response;
}
Expand Down
15 changes: 13 additions & 2 deletions includes/class-wc-payments-db.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function order_from_charge_id( $charge_id ) {
$order_id = $this->order_id_from_meta_key_value( self::META_KEY_CHARGE_ID, $charge_id );

if ( $order_id ) {
return wc_get_order( $order_id );
return $this->order_from_order_id( $order_id );
}
return false;
}
Expand All @@ -41,7 +41,7 @@ public function order_from_intent_id( $intent_id ) {
$order_id = $this->order_id_from_meta_key_value( self::META_KEY_INTENT_ID, $intent_id );

if ( $order_id ) {
return wc_get_order( $order_id );
return $this->order_from_order_id( $order_id );
}
return false;
}
Expand All @@ -67,4 +67,15 @@ private function order_id_from_meta_key_value( $meta_key, $meta_value ) {
);
return $order_id;
}

/**
* Retrieve an order using order ID.
*
* @param string $order_id WC Order Id.
*
* @return null|WC_Order
*/
public function order_from_order_id( $order_id ) {
return wc_get_order( ( $order_id ) );
}
}
1 change: 1 addition & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ Please note that our support for the checkout block is still experimental and th
* Add - Add compatibility between Multi-Currency and WooCommerce UPS shipping extension.
* Add - Add compatibility between Multi-Currency and WooCommerce FedEx shipping extension.
* Fix - Fix decimal error with shipping calculations with Multi-Currency.
* Fix - Allow payment_intent_succeeded webhook to handle orders without intent_id attached.

= 3.1.0 - 2021-10-06 =
* Fix - Issue affecting analytics for Multi-Currency orders made with a zero-decimal to non-zero decimal conversion.
Expand Down
52 changes: 51 additions & 1 deletion tests/unit/admin/test-class-wc-rest-payments-webhook.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function setUp() {

$this->mock_db_wrapper = $this->getMockBuilder( WC_Payments_DB::class )
->disableOriginalConstructor()
->setMethods( [ 'order_from_charge_id', 'order_from_intent_id' ] )
->setMethods( [ 'order_from_charge_id', 'order_from_intent_id', 'order_from_order_id' ] )
->getMock();

$this->mock_remote_note_service = $this->createMock( WC_Payments_Remote_Note_Service::class );
Expand Down Expand Up @@ -562,6 +562,56 @@ public function test_payment_intent_successful_and_completes_order() {
$this->assertEquals( [ 'result' => 'success' ], $response_data );
}

/**
* Tests that a payment_intent.succeeded event will complete the order even if the intent was not properly attached into the order.
*/
public function test_payment_intent_successful_and_completes_order_without_intent_id() {
$this->request_body['type'] = 'payment_intent.succeeded';
$this->request_body['data']['object'] = [
'id' => 'pi_123123123123123', // payment_intent's ID.
'object' => 'payment_intent',
'amount' => 1500,
'charges' => [],
'currency' => 'eur',
'metadata' => [ 'order_id' => 'id_1323' ], // Using order_id inside of the intent metadata to find the order.
];

$this->request->set_body( wp_json_encode( $this->request_body ) );

$mock_order = $this->createMock( WC_Order::class );

$mock_order
->expects( $this->once() )
->method( 'has_status' )
->with( [ 'processing', 'completed' ] )
->willReturn( false );

$mock_order
->expects( $this->once() )
->method( 'payment_complete' );

$this->mock_db_wrapper
->expects( $this->once() )
->method( 'order_from_intent_id' )
->with( 'pi_123123123123123' )
->willReturn( null );

$this->mock_db_wrapper
->expects( $this->once() )
->method( 'order_from_order_id' )
->with( 'id_1323' )
->willReturn( $mock_order );

// Run the test.
$response = $this->controller->handle_webhook( $this->request );

// Check the response.
$response_data = $response->get_data();

$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( [ 'result' => 'success' ], $response_data );
}

/**
* Tests that a payment_intent.succeeded event will not complete the order
* if it is already completed/processed.
Expand Down

0 comments on commit 67fc075

Please sign in to comment.