Skip to content

Commit 7d11926

Browse files
Avoid processing webhooks meant for different sites (#11010)
1 parent 3e29f13 commit 7d11926

4 files changed

+74
-5
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: fix
3+
4+
Ignore webhooks whenever the order key in their body does not match the local order.

includes/class-wc-payments-webhook-processing-service.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,7 @@ private function get_order_from_event_body( $event_body ) {
722722
$event_data = $this->read_webhook_property( $event_body, 'data' );
723723
$event_object = $this->read_webhook_property( $event_data, 'object' );
724724
$intent_id = $this->read_webhook_property( $event_object, 'id' );
725+
$order_key = $this->read_webhook_property( $event_object, 'metadata' )['order_key'] ?? null;
725726

726727
// Look up the order related to this intent.
727728
$order = $this->wcpay_db->order_from_intent_id( $intent_id );
@@ -746,6 +747,24 @@ private function get_order_from_event_body( $event_body ) {
746747
}
747748
}
748749

750+
/**
751+
* If the order has been found, but there is an order key mismatch, it
752+
* could be caused by another site creating orders with the same IDs
753+
* while this site remains the primary webhook receiver.
754+
*/
755+
if ( null !== $order_key && $order instanceof WC_Order && $order->get_order_key() !== $order_key ) {
756+
Logger::debug(
757+
'Mismatching order key found while retrieving an order for webhook processing',
758+
[
759+
'intent_id' => $intent_id,
760+
'order_id' => $order->get_id(),
761+
'webhook_order_key' => $order_key,
762+
'local_order_key' => $order->get_order_key(),
763+
]
764+
);
765+
return null;
766+
}
767+
749768
if ( ! $order instanceof \WC_Order ) {
750769
throw new Invalid_Payment_Method_Exception(
751770
sprintf(

includes/wc-payment-api/class-wc-payments-api-client.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2289,7 +2289,7 @@ public function deserialize_payment_intention_object_from_array( array $intentio
22892289
$payment_method_options = $intention_array['payment_method_options'] ?? [];
22902290

22912291
$charge = ! empty( $charge_array ) ? self::deserialize_charge_object_from_array( $charge_array ) : null;
2292-
$order = $this->get_order_info_from_intention_object( $intention_array['id'] );
2292+
$order = $this->get_order_info_from_intention_object( $intention_array['id'], $intention_array['metadata']['order_key'] ?? null );
22932293

22942294
$intent = new WC_Payments_API_Payment_Intention(
22952295
$intention_array['id'],
@@ -2756,13 +2756,16 @@ private function maybe_act_on_fraud_prevention( string $error_code ) {
27562756
* Adds additional info to intention object.
27572757
*
27582758
* @param string $intention_id Intention ID.
2759-
*
2759+
* @param string $order_key Order key.
27602760
* @return array
27612761
*/
2762-
private function get_order_info_from_intention_object( $intention_id ) {
2763-
$order = $this->wcpay_db->order_from_intent_id( $intention_id );
2764-
$object = $this->add_order_info_to_object( $order, [] );
2762+
private function get_order_info_from_intention_object( $intention_id, $order_key = null ) {
2763+
$order = $this->wcpay_db->order_from_intent_id( $intention_id );
2764+
if ( $order instanceof WC_Order && null !== $order_key && $order_key !== $order->get_order_key() ) {
2765+
return [];
2766+
}
27652767

2768+
$object = $this->add_order_info_to_object( $order, [] );
27662769
return $object['order'];
27672770
}
27682771

tests/unit/test-class-wc-payments-webhook-processing-service.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,46 @@ public function test_payment_intent_successful_and_completes_order() {
748748
$this->webhook_processing_service->process( $this->event_body );
749749
}
750750

751+
/**
752+
* Tests that a payment_intent.succeeded event will ignore the order due to key mismatch.
753+
*/
754+
public function test_payment_intent_successful_ignores_order_due_to_key_mismatch() {
755+
$this->event_body['type'] = 'payment_intent.succeeded';
756+
$this->event_body['livemode'] = true;
757+
$this->event_body['data']['object'] = [
758+
'id' => 'pi_123123123123123', // payment_intent's ID.
759+
'amount' => 1500,
760+
'charges' => [
761+
'data' => [
762+
[
763+
'id' => 'py_123123123123123',
764+
],
765+
],
766+
],
767+
'currency' => 'eur',
768+
'metadata' => [
769+
'order_key' => 'abcd',
770+
],
771+
];
772+
773+
$this->mock_order->expects( $this->any() )
774+
->method( 'get_order_key' )
775+
->willReturn( 'xyz' );
776+
777+
$this->mock_order->expects( $this->never() )->method( 'update_meta_data' );
778+
$this->mock_order->expects( $this->never() )->method( 'save' );
779+
$this->mock_order->expects( $this->never() )->method( 'payment_complete' );
780+
781+
$this->mock_db_wrapper
782+
->expects( $this->once() )
783+
->method( 'order_from_intent_id' )
784+
->with( 'pi_123123123123123' )
785+
->willReturn( $this->mock_order );
786+
787+
// Run the test.
788+
$this->webhook_processing_service->process( $this->event_body );
789+
}
790+
751791
/**
752792
* Tests that a payment_intent.succeeded event will add relevant metadata.
753793
*/
@@ -1275,6 +1315,7 @@ public function test_payment_intent_fails_and_fails_order() {
12751315
],
12761316
],
12771317
],
1318+
'metadata' => [],
12781319
'last_payment_error' => [
12791320
'message' => 'error message',
12801321
'payment_method' => [
@@ -1345,6 +1386,7 @@ public function test_payment_intent_without_charges_fails_and_fails_order() {
13451386
'object' => 'payment_intent',
13461387
'amount' => 1500,
13471388
'charges' => [],
1389+
'metadata' => [],
13481390
'last_payment_error' => [
13491391
'code' => 'card_declined',
13501392
'decline_code' => 'debit_notification_undelivered',
@@ -1988,6 +2030,7 @@ public function test_payment_intent_failed_handles_terminal_payment() {
19882030
],
19892031
],
19902032
],
2033+
'metadata' => [],
19912034
'last_payment_error' => [
19922035
'message' => 'Card declined',
19932036
'payment_method' => [

0 commit comments

Comments
 (0)