Skip to content

Commit 3c9680b

Browse files
authored
Show number of pending webhooks in the warning section of the Account status (#4484)
* Show the number of pending webhooks in the warning section of the Account status
1 parent fc88a6e commit 3c9680b

File tree

5 files changed

+71
-8
lines changed

5 files changed

+71
-8
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* Update - Enhanced logging system with support for all log levels and improved context handling
2121
* Fix - Set default values for custom field options
2222
* Fix - Enforce rate limiter for failed add payment method attempts
23+
* Update - Add the number of pending webhooks to the Account status section
2324

2425
= 9.6.0 - 2025-07-07 =
2526
* Fix - Register Express Checkout script before use to restore buttons on “order-pay” pages

includes/class-wc-stripe-webhook-handler.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ public function check_for_webhook() {
9898
return;
9999
}
100100

101+
WC_Stripe_Webhook_State::set_pending_webhooks_count( $event->pending_webhooks );
102+
101103
// Validate it to make sure it is legit.
102104
$request_headers = array_change_key_case( $this->get_request_headers(), CASE_UPPER );
103105
$validation_result = $this->validate_request( $request_headers, $request_body );

includes/class-wc-stripe-webhook-state.php

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ class WC_Stripe_Webhook_State {
1515
const OPTION_LIVE_LAST_SUCCESS_AT = 'wc_stripe_wh_last_success_at';
1616
const OPTION_LIVE_LAST_FAILURE_AT = 'wc_stripe_wh_last_failure_at';
1717
const OPTION_LIVE_LAST_ERROR = 'wc_stripe_wh_last_error';
18+
const OPTION_LIVE_PENDING_WEBHOOKS = 'wc_stripe_wh_live_pending_webhooks';
1819

1920
const OPTION_TEST_MONITORING_BEGAN_AT = 'wc_stripe_wh_test_monitor_began_at';
2021
const OPTION_TEST_LAST_SUCCESS_AT = 'wc_stripe_wh_test_last_success_at';
2122
const OPTION_TEST_LAST_FAILURE_AT = 'wc_stripe_wh_test_last_failure_at';
2223
const OPTION_TEST_LAST_ERROR = 'wc_stripe_wh_test_last_error';
24+
const OPTION_TEST_PENDING_WEBHOOKS = 'wc_stripe_wh_test_pending_webhooks';
2325

2426
const VALIDATION_SUCCEEDED = 'validation_succeeded';
2527
const VALIDATION_FAILED_EMPTY_HEADERS = 'empty_headers';
@@ -57,13 +59,15 @@ public static function clear_state( $mode = 'all' ) {
5759
delete_option( self::OPTION_LIVE_LAST_SUCCESS_AT );
5860
delete_option( self::OPTION_LIVE_LAST_FAILURE_AT );
5961
delete_option( self::OPTION_LIVE_LAST_ERROR );
62+
delete_option( self::OPTION_LIVE_PENDING_WEBHOOKS );
6063
}
6164

6265
if ( 'all' === $mode || 'test' === $mode ) {
6366
delete_option( self::OPTION_TEST_MONITORING_BEGAN_AT );
6467
delete_option( self::OPTION_TEST_LAST_SUCCESS_AT );
6568
delete_option( self::OPTION_TEST_LAST_FAILURE_AT );
6669
delete_option( self::OPTION_TEST_LAST_ERROR );
70+
delete_option( self::OPTION_TEST_PENDING_WEBHOOKS );
6771
}
6872
}
6973

@@ -227,6 +231,30 @@ public static function get_webhook_status_code() {
227231
return 4;
228232
}
229233

234+
/**
235+
* Sets the number of pending webhooks.
236+
*
237+
* @since 9.7.0
238+
*
239+
* @param int $pending_webhooks The number of pending webhooks.
240+
*/
241+
public static function set_pending_webhooks_count( $pending_webhooks ) {
242+
$option = WC_Stripe_Mode::is_test() ? self::OPTION_TEST_PENDING_WEBHOOKS : self::OPTION_LIVE_PENDING_WEBHOOKS;
243+
update_option( $option, $pending_webhooks );
244+
}
245+
246+
/**
247+
* Gets the number of pending webhooks.
248+
*
249+
* @since 9.7.0
250+
*
251+
* @return int The number of pending webhooks.
252+
*/
253+
public static function get_pending_webhooks_count() {
254+
$option = WC_Stripe_Mode::is_test() ? self::OPTION_TEST_PENDING_WEBHOOKS : self::OPTION_LIVE_PENDING_WEBHOOKS;
255+
return get_option( $option, 0 );
256+
}
257+
230258
/**
231259
* Gets the state of webhook processing in a human readable format.
232260
*
@@ -240,30 +268,35 @@ public static function get_webhook_status_message() {
240268
$last_error = self::get_last_error_reason();
241269
$test_mode = WC_Stripe_Mode::is_test();
242270
$code = self::get_webhook_status_code();
271+
$pending_webhooks = self::get_pending_webhooks_count();
243272

244273
$date_format = 'Y-m-d H:i:s e';
245274

275+
$message = '';
276+
246277
switch ( $code ) {
247278
case 1: // Case 1 (Nominal case): Most recent = success
248-
return sprintf(
279+
$message = sprintf(
249280
$test_mode ?
250281
/* translators: 1) date and time of last webhook received, e.g. 2020-06-28 10:30:50 UTC */
251282
__( 'The most recent test webhook, timestamped %s, was processed successfully.', 'woocommerce-gateway-stripe' ) :
252283
/* translators: 1) date and time of last webhook received, e.g. 2020-06-28 10:30:50 UTC */
253284
__( 'The most recent live webhook, timestamped %s, was processed successfully.', 'woocommerce-gateway-stripe' ),
254285
gmdate( $date_format, $last_success_at )
255286
);
287+
break;
256288
case 2: // Case 2: No webhooks received yet
257-
return sprintf(
289+
$message = sprintf(
258290
$test_mode ?
259291
/* translators: 1) date and time webhook monitoring began, e.g. 2020-06-28 10:30:50 UTC */
260292
__( 'No test webhooks have been received since monitoring began at %s.', 'woocommerce-gateway-stripe' ) :
261293
/* translators: 1) date and time webhook monitoring began, e.g. 2020-06-28 10:30:50 UTC */
262294
__( 'No live webhooks have been received since monitoring began at %s.', 'woocommerce-gateway-stripe' ),
263295
gmdate( $date_format, $monitoring_began_at )
264296
);
297+
break;
265298
case 3: // Case 3: Failure after success
266-
return sprintf(
299+
$message = sprintf(
267300
$test_mode ?
268301
/*
269302
* translators: 1) date and time of last failed webhook e.g. 2020-06-28 10:30:50 UTC
@@ -279,10 +312,11 @@ public static function get_webhook_status_message() {
279312
__( 'Warning: The most recent live webhook, received at %1$s, could not be processed. Reason: %2$s. (The last live webhook to process successfully was timestamped %3$s.)', 'woocommerce-gateway-stripe' ),
280313
gmdate( $date_format, $last_failure_at ),
281314
$last_error,
282-
gmdate( $date_format, $last_success_at )
315+
gmdate( $date_format, $last_success_at ),
283316
);
317+
break;
284318
default: // Case 4: Failure with no prior success
285-
return sprintf(
319+
$message = sprintf(
286320
$test_mode ?
287321
/* translators: 1) date and time of last failed webhook e.g. 2020-06-28 10:30:50 UTC
288322
* translators: 2) reason webhook failed
@@ -296,9 +330,24 @@ public static function get_webhook_status_message() {
296330
__( 'Warning: The most recent live webhook, received at %1$s, could not be processed. Reason: %2$s. (No live webhooks have been processed successfully since monitoring began at %3$s.)', 'woocommerce-gateway-stripe' ),
297331
gmdate( $date_format, $last_failure_at ),
298332
$last_error,
299-
gmdate( $date_format, $monitoring_began_at )
333+
gmdate( $date_format, $monitoring_began_at ),
300334
);
301335
}
336+
337+
if ( $pending_webhooks > 0 ) {
338+
$message .= '. ' . sprintf(
339+
/* translators: 1) number of pending webhooks */
340+
_n(
341+
'There is at least %d webhook pending.',
342+
'There are approximately %d webhooks pending.',
343+
$pending_webhooks,
344+
'woocommerce-gateway-stripe'
345+
),
346+
$pending_webhooks
347+
);
348+
}
349+
350+
return $message;
302351
}
303352

304353
/**

readme.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,5 +130,6 @@ If you get stuck, you can ask for help in the [Plugin Forum](https://wordpress.o
130130
* Update - Enhanced logging system with support for all log levels and improved context handling
131131
* Fix - Set default values for custom field options
132132
* Fix - Enforce rate limiter for failed add payment method attempts
133+
* Update - Add the number of pending webhooks to the Account status section
133134

134135
[See changelog for full details across versions](https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-stripe/trunk/changelog.txt).

tests/phpunit/WC_Stripe_Webhook_State_Test.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,13 @@ public function tear_down() {
7070
delete_option( WC_Stripe_Webhook_State::OPTION_LIVE_LAST_SUCCESS_AT );
7171
delete_option( WC_Stripe_Webhook_State::OPTION_LIVE_LAST_FAILURE_AT );
7272
delete_option( WC_Stripe_Webhook_State::OPTION_LIVE_LAST_ERROR );
73+
delete_option( WC_Stripe_Webhook_State::OPTION_LIVE_PENDING_WEBHOOKS );
7374

7475
delete_option( WC_Stripe_Webhook_State::OPTION_TEST_MONITORING_BEGAN_AT );
7576
delete_option( WC_Stripe_Webhook_State::OPTION_TEST_LAST_SUCCESS_AT );
7677
delete_option( WC_Stripe_Webhook_State::OPTION_TEST_LAST_FAILURE_AT );
7778
delete_option( WC_Stripe_Webhook_State::OPTION_TEST_LAST_ERROR );
79+
delete_option( WC_Stripe_Webhook_State::OPTION_TEST_PENDING_WEBHOOKS );
7880

7981
parent::tear_down();
8082
}
@@ -127,9 +129,11 @@ private function process_webhook() {
127129
if ( WC_Stripe_Webhook_State::VALIDATION_SUCCEEDED === $validation_result ) {
128130
$notification = json_decode( $this->request_body );
129131
WC_Stripe_Webhook_State::set_last_webhook_success_at( $notification->created );
132+
WC_Stripe_Webhook_State::set_pending_webhooks_count( 0 );
130133
} else {
131134
WC_Stripe_Webhook_State::set_last_webhook_failure_at( time() );
132135
WC_Stripe_Webhook_State::set_last_error_reason( $validation_result );
136+
WC_Stripe_Webhook_State::set_pending_webhooks_count( 3 );
133137
}
134138
}
135139

@@ -167,7 +171,7 @@ public function test_get_webhook_status_message_no_webhooks_received() {
167171
// Case 3: Failure after success.
168172
public function test_get_webhook_status_message_failure_after_success() {
169173
$this->set_valid_request_data();
170-
$expected_message = '/Warning: The most recent [mode] webhook, received at (.*), could not be processed. Reason: (.*) \(The last [mode] webhook to process successfully was timestamped/';
174+
$expected_message = '/Warning: The most recent [mode] webhook, received at (.*), could not be processed. Reason: (.*) \(The last [mode] webhook to process successfully was timestamped (.*).\). There are approximately (\d+) webhooks pending./';
171175
// Live
172176
$this->set_testmode( 'no' );
173177
// Process successful webhook.
@@ -193,7 +197,7 @@ public function test_get_webhook_status_message_failure_after_success() {
193197
// Case 4: Failure with no prior success.
194198
public function test_get_webhook_status_message_failure_with_no_prior_success() {
195199
$this->set_valid_request_data();
196-
$expected_message = '/Warning: The most recent [mode] webhook, received at (.*), could not be processed. Reason: (.*) \(No [mode] webhooks have been processed successfully since monitoring began at/';
200+
$expected_message = '/Warning: The most recent [mode] webhook, received at (.*), could not be processed. Reason: (.*) \(No [mode] webhooks have been processed successfully since monitoring began at (.*).\). There are approximately (\d+) webhooks pending./';
197201
// Live
198202
$this->set_testmode( 'no' );
199203
// Fail webhook.
@@ -208,6 +212,12 @@ public function test_get_webhook_status_message_failure_with_no_prior_success()
208212
$this->process_webhook();
209213
$message = WC_Stripe_Webhook_State::get_webhook_status_message();
210214
$this->assertMatchesRegularExpression( str_replace( '[mode]', 'test', $expected_message ), $message );
215+
216+
// Test that when pending webhooks count is 1 the message is singular.
217+
$expected_message = '/Warning: (.*).\). There is at least 1 webhook pending./';
218+
WC_Stripe_Webhook_State::set_pending_webhooks_count( 1 );
219+
$message = WC_Stripe_Webhook_State::get_webhook_status_message();
220+
$this->assertMatchesRegularExpression( $expected_message, $message );
211221
}
212222

213223
// Test failure reason: no error.

0 commit comments

Comments
 (0)