@@ -49,6 +49,13 @@ class WC_Stripe_Webhook_Handler extends WC_Stripe_Payment_Gateway {
49
49
*/
50
50
protected $ deferred_webhook_action = 'wc_stripe_deferred_webhook ' ;
51
51
52
+ /**
53
+ * The order object being processed.
54
+ *
55
+ * @var WC_Order|null
56
+ */
57
+ protected $ resolved_order = null ;
58
+
52
59
/**
53
60
* Constructor.
54
61
*
@@ -71,7 +78,7 @@ public function __construct() {
71
78
// plugin when this code first appears.
72
79
WC_Stripe_Webhook_State::get_monitoring_began_at ();
73
80
74
- add_action ( $ this ->deferred_webhook_action , [ $ this , 'process_deferred_webhook ' ], 10 , 2 );
81
+ add_action ( $ this ->deferred_webhook_action , [ $ this , 'process_deferred_webhook ' ], 10 , 3 );
75
82
}
76
83
77
84
/**
@@ -269,6 +276,9 @@ public function process_webhook_payment( $notification, $retry = true ) {
269
276
return ;
270
277
}
271
278
279
+ // Set the order being processed for the `wc_stripe_webhook_received` action later.
280
+ $ this ->resolved_order = $ order ;
281
+
272
282
$ order_id = $ order ->get_id ();
273
283
274
284
$ is_pending_receiver = ( 'receiver ' === $ notification ->data ->object ->flow );
@@ -402,6 +412,9 @@ public function process_webhook_dispute( $notification ) {
402
412
return ;
403
413
}
404
414
415
+ // Set the order being processed for the `wc_stripe_webhook_received` action later.
416
+ $ this ->resolved_order = $ order ;
417
+
405
418
$ this ->set_stripe_order_status_before_hold ( $ order , $ order ->get_status () );
406
419
407
420
$ needs_response = in_array ( $ notification ->data ->object ->status , [ 'needs_response ' , 'warning_needs_response ' ], true );
@@ -444,6 +457,9 @@ public function process_webhook_dispute_closed( $notification ) {
444
457
return ;
445
458
}
446
459
460
+ // Set the order being processed for the `wc_stripe_webhook_received` action later.
461
+ $ this ->resolved_order = $ order ;
462
+
447
463
if ( 'lost ' === $ status ) {
448
464
$ message = __ ( 'The dispute was lost or accepted. ' , 'woocommerce-gateway-stripe ' );
449
465
} elseif ( 'won ' === $ status ) {
@@ -495,6 +511,9 @@ public function process_webhook_capture( $notification ) {
495
511
return ;
496
512
}
497
513
514
+ // Set the order being processed for the `wc_stripe_webhook_received` action later.
515
+ $ this ->resolved_order = $ order ;
516
+
498
517
if ( WC_Stripe_Helper::payment_method_allows_manual_capture ( $ order ->get_payment_method () ) ) {
499
518
$ charge = $ order ->get_transaction_id ();
500
519
$ captured = $ order ->get_meta ( '_stripe_charge_captured ' , true );
@@ -567,6 +586,9 @@ public function process_webhook_charge_succeeded( $notification ) {
567
586
return ;
568
587
}
569
588
589
+ // Set the order being processed for the `wc_stripe_webhook_received` action later.
590
+ $ this ->resolved_order = $ order ;
591
+
570
592
if ( ! $ order ->has_status ( OrderStatus::ON_HOLD ) ) {
571
593
return ;
572
594
}
@@ -625,6 +647,9 @@ public function process_webhook_charge_failed( $notification ) {
625
647
return ;
626
648
}
627
649
650
+ // Set the order being processed for the `wc_stripe_webhook_received` action later.
651
+ $ this ->resolved_order = $ order ;
652
+
628
653
// If order status is already in failed status don't continue.
629
654
if ( $ order ->has_status ( OrderStatus::FAILED ) ) {
630
655
return ;
@@ -665,6 +690,9 @@ public function process_webhook_source_canceled( $notification ) {
665
690
}
666
691
}
667
692
693
+ // Set the order being processed for the `wc_stripe_webhook_received` action later.
694
+ $ this ->resolved_order = $ order ;
695
+
668
696
// Don't proceed if payment method isn't Stripe.
669
697
if ( 'stripe ' !== $ order ->get_payment_method () ) {
670
698
WC_Stripe_Logger::log ( 'Canceled webhook abort: Order was not processed by Stripe: ' . $ order ->get_id () );
@@ -697,6 +725,9 @@ public function process_webhook_refund( $notification ) {
697
725
$ order = WC_Stripe_Helper::get_order_by_charge_id ( $ notification ->data ->object ->id );
698
726
}
699
727
728
+ // Set the order being processed for the `wc_stripe_webhook_received` action later.
729
+ $ this ->resolved_order = $ order ;
730
+
700
731
if ( ! $ order ) {
701
732
WC_Stripe_Logger::log ( 'Could not find order via charge ID: ' . $ notification ->data ->object ->id );
702
733
return ;
@@ -785,6 +816,9 @@ public function process_webhook_refund_updated( $notification ) {
785
816
return ;
786
817
}
787
818
819
+ // Set the order being processed for the `wc_stripe_webhook_received` action later.
820
+ $ this ->resolved_order = $ order ;
821
+
788
822
$ order_id = $ order ->get_id ();
789
823
790
824
if ( 'stripe ' === substr ( (string ) $ order ->get_payment_method (), 0 , 6 ) ) {
@@ -888,6 +922,9 @@ public function process_review_opened( $notification ) {
888
922
}
889
923
}
890
924
925
+ // Set the order being processed for the `wc_stripe_webhook_received` action later.
926
+ $ this ->resolved_order = $ order ;
927
+
891
928
$ this ->set_stripe_order_status_before_hold ( $ order , $ order ->get_status () );
892
929
893
930
$ message = sprintf (
@@ -929,6 +966,9 @@ public function process_review_closed( $notification ) {
929
966
}
930
967
}
931
968
969
+ // Set the order being processed for the `wc_stripe_webhook_received` action later.
970
+ $ this ->resolved_order = $ order ;
971
+
932
972
/* translators: 1) The reason type. */
933
973
$ message = sprintf ( __ ( 'The opened review for this order is now closed. Reason: (%s) ' , 'woocommerce-gateway-stripe ' ), $ notification ->data ->object ->reason );
934
974
@@ -1047,6 +1087,9 @@ public function process_payment_intent( $notification ) {
1047
1087
return ;
1048
1088
}
1049
1089
1090
+ // Set the order being processed for the `wc_stripe_webhook_received` action later.
1091
+ $ this ->resolved_order = $ order ;
1092
+
1050
1093
if ( $ this ->lock_order_payment ( $ order , $ intent ) ) {
1051
1094
return ;
1052
1095
}
@@ -1082,7 +1125,7 @@ public function process_payment_intent( $notification ) {
1082
1125
$ process_webhook_async = apply_filters ( 'wc_stripe_process_payment_intent_webhook_async ' , true , $ order , $ intent , $ notification );
1083
1126
$ is_awaiting_action = $ order ->get_meta ( '_stripe_upe_waiting_for_redirect ' ) ?? false ;
1084
1127
1085
- // Process the webhook now if it's for a voucher or wallet payment , or if filtered to process immediately and order is not awaiting action.
1128
+ // Process the webhook now if it's for a voucher or wallet payment, or if filtered to process immediately and order is not awaiting action.
1086
1129
if ( $ is_voucher_payment || $ is_wallet_payment || ( ! $ process_webhook_async && ! $ is_awaiting_action ) ) {
1087
1130
$ charge = $ this ->get_latest_charge_from_intent ( $ intent );
1088
1131
@@ -1096,6 +1139,8 @@ public function process_payment_intent( $notification ) {
1096
1139
1097
1140
$ charge ->is_webhook_response = true ;
1098
1141
$ this ->process_response ( $ charge , $ order );
1142
+
1143
+ $ this ->run_webhook_received_action ( (string ) $ notification ->type , $ notification , $ this ->resolved_order );
1099
1144
} else {
1100
1145
WC_Stripe_Logger::log ( "Processing $ notification ->type ( $ intent ->id ) asynchronously for order $ order_id. " );
1101
1146
@@ -1112,7 +1157,6 @@ public function process_payment_intent( $notification ) {
1112
1157
do_action ( 'wc_gateway_stripe_process_payment_intent_incomplete ' , $ order );
1113
1158
}
1114
1159
}
1115
-
1116
1160
break ;
1117
1161
default :
1118
1162
if ( $ is_voucher_payment && 'payment_intent.payment_failed ' === $ notification ->type ) {
@@ -1153,6 +1197,9 @@ public function process_setup_intent( $notification ) {
1153
1197
return ;
1154
1198
}
1155
1199
1200
+ // Set the order being processed for the `wc_stripe_webhook_received` action later.
1201
+ $ this ->resolved_order = $ order ;
1202
+
1156
1203
$ allowed_payment_processing_statuses = [ OrderStatus::PENDING , OrderStatus::FAILED ];
1157
1204
1158
1205
$ allowed_payment_processing_statuses = apply_filters_deprecated (
@@ -1227,8 +1274,9 @@ protected function defer_webhook_processing( $webhook_notification, $additional_
1227
1274
time () + $ this ->deferred_webhook_delay ,
1228
1275
$ this ->deferred_webhook_action ,
1229
1276
[
1230
- 'type ' => $ webhook_notification ->type ,
1231
- 'data ' => $ additional_data ,
1277
+ 'type ' => $ webhook_notification ->type ,
1278
+ 'data ' => $ additional_data ,
1279
+ 'notification ' => $ webhook_notification ,
1232
1280
]
1233
1281
);
1234
1282
}
@@ -1240,8 +1288,9 @@ protected function defer_webhook_processing( $webhook_notification, $additional_
1240
1288
*
1241
1289
* @param string $webhook_type The webhook event name/type.
1242
1290
* @param array $additional_data Additional data passed to the scheduled job.
1291
+ * @param stdClass $notification The webhook notification payload.
1243
1292
*/
1244
- public function process_deferred_webhook ( $ webhook_type , $ additional_data ) {
1293
+ public function process_deferred_webhook ( $ webhook_type , $ additional_data, $ notification = null ) {
1245
1294
try {
1246
1295
switch ( $ webhook_type ) {
1247
1296
case 'payment_intent.succeeded ' :
@@ -1253,6 +1302,9 @@ public function process_deferred_webhook( $webhook_type, $additional_data ) {
1253
1302
throw new Exception ( "Missing required data. 'order_id' is invalid or not found for the deferred ' {$ webhook_type }' event. " );
1254
1303
}
1255
1304
1305
+ // Set the order being processed for the `wc_stripe_webhook_received` action later.
1306
+ $ this ->resolved_order = $ order ;
1307
+
1256
1308
if ( empty ( $ intent_id ) ) {
1257
1309
throw new Exception ( "Missing required data. 'intent_id' is missing for the deferred ' {$ webhook_type }' event. " );
1258
1310
}
@@ -1269,6 +1321,8 @@ public function process_deferred_webhook( $webhook_type, $additional_data ) {
1269
1321
throw new Exception ( "Unsupported webhook type: {$ webhook_type }" );
1270
1322
break ;
1271
1323
}
1324
+
1325
+ $ this ->run_webhook_received_action ( (string ) $ webhook_type , $ notification , $ this ->resolved_order );
1272
1326
} catch ( Exception $ e ) {
1273
1327
WC_Stripe_Logger::log ( 'Error processing deferred webhook: ' . $ e ->getMessage () );
1274
1328
@@ -1334,6 +1388,8 @@ public function process_account_updated( $notification ) {
1334
1388
public function process_webhook ( $ request_body ) {
1335
1389
$ notification = json_decode ( $ request_body );
1336
1390
1391
+ $ this ->resolved_order = null ;
1392
+
1337
1393
switch ( $ notification ->type ) {
1338
1394
case 'account.updated ' :
1339
1395
$ this ->process_account_updated ( $ notification );
@@ -1397,8 +1453,41 @@ public function process_webhook( $request_body ) {
1397
1453
$ this ->process_setup_intent ( $ notification );
1398
1454
1399
1455
}
1456
+
1457
+ // These events might be processed async. Skip the action trigger for them here. The trigger will be called inside the specific methods.
1458
+ if ( 'payment_intent.succeeded ' === $ notification ->type || 'payment_intent.amount_capturable_updated ' === $ notification ->type ) {
1459
+ return ;
1460
+ }
1461
+
1462
+ $ this ->run_webhook_received_action ( $ notification ->type , $ notification , $ this ->resolved_order );
1400
1463
}
1401
1464
1465
+ /**
1466
+ * Helper function to run the `wc_stripe_webhook_received` action consistently.
1467
+ *
1468
+ * @param string $webhook_type The type of webhook that was processed.
1469
+ * @param object $notification The webhook data sent from Stripe.
1470
+ * @param WC_Order|null $order The order being processed by the webhook.
1471
+ */
1472
+ private function run_webhook_received_action ( string $ webhook_type , object $ notification , ?WC_Order $ order = null ): void {
1473
+ try {
1474
+ /**
1475
+ * Fires after a webhook has been processed, but before we respond to Stripe.
1476
+ * This allows for custom processing of the webhook after it has been processed.
1477
+ * Note that the $order parameter may be null in various cases, especially when processing
1478
+ * webhooks unrelated to orders, such as account updates.
1479
+ *
1480
+ * @since 9.8.0
1481
+ *
1482
+ * @param string $webhook_type The type of webhook that was processed.
1483
+ * @param object $notification The webhook data sent from Stripe.
1484
+ * @param WC_Order|null $order The order being processed by the webhook.
1485
+ */
1486
+ do_action ( 'wc_stripe_webhook_received ' , $ webhook_type , $ notification , $ this ->resolved_order );
1487
+ } catch ( Throwable $ e ) {
1488
+ WC_Stripe_Logger::error ( 'Error in wc_stripe_webhook_received action: ' . $ e ->getMessage (), [ 'error ' => $ e ] );
1489
+ }
1490
+ }
1402
1491
/**
1403
1492
* Fetches an order from a payment intent.
1404
1493
*
0 commit comments