Skip to content

Commit 9a70a5a

Browse files
authored
Improve webhook status related messages on the settings page. (#3681)
* return 400 status code for webhook signature mismatch * check if duplicate webhook is set up in Stripe account * update webhook message * fix logic and update variable name
1 parent dc94d85 commit 9a70a5a

File tree

5 files changed

+46
-2
lines changed

5 files changed

+46
-2
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* Add - Correctly handles charge expired webhook events, setting the order status to failed and adding a note.
2626
* Fix - Allow account creation on checkout, if enabled, when purchasing subscriptions using ECE.
2727
* Tweak - Add empty check for cart when checking for allowed products for express checkout.
28+
* Tweak - Improve webhook status related messages on the settings page.
2829
* Update - Prevent editing of orders awaiting payment capture.
2930
* Add - Introduce locking and unlocking in refund flow to prevent double refund due to race condition.
3031
* Fix - Check order currency on pay for order page to display supported payment methods.

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,16 @@ public function check_for_webhook() {
115115
WC_Stripe_Logger::error( 'Webhook body: ' . print_r( $request_body, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
116116

117117
WC_Stripe_Webhook_State::set_last_webhook_failure_at( time() );
118+
119+
if ( WC_Stripe_Webhook_State::VALIDATION_FAILED_SIGNATURE_MISMATCH === $validation_result && $this->has_duplicate_webhooks_setup() ) {
120+
WC_Stripe_Webhook_State::set_last_error_reason( WC_Stripe_Webhook_State::VALIDATION_FAILED_DUPLICATE_WEBHOOKS );
121+
122+
// Return a 400 HTTP status code to notify Stripe about a misconfigured webhook when the signature does not match.
123+
// @see https://docs.stripe.com/webhooks#disable
124+
status_header( 400 );
125+
exit;
126+
}
127+
118128
WC_Stripe_Webhook_State::set_last_error_reason( $validation_result );
119129

120130
// A webhook endpoint must return a 2xx HTTP status code to prevent future webhook
@@ -125,6 +135,33 @@ public function check_for_webhook() {
125135
}
126136
}
127137

138+
/**
139+
* Check if the Stripe account has duplicate webhooks setup for this site.
140+
*
141+
* @since 9.1.0
142+
*/
143+
public function has_duplicate_webhooks_setup() {
144+
$webhook_url = WC_Stripe_Helper::get_webhook_url();
145+
$webhooks = WC_Stripe_API::retrieve( 'webhook_endpoints' );
146+
147+
if ( is_wp_error( $webhooks ) || ! isset( $webhooks->data ) || empty( $webhooks->data ) ) {
148+
return false;
149+
}
150+
151+
$number_of_webhooks = 0;
152+
foreach ( $webhooks->data as $webhook ) {
153+
if ( ! isset( $webhook->url ) ) {
154+
continue;
155+
}
156+
157+
if ( $webhook->url === $webhook_url ) {
158+
$number_of_webhooks++;
159+
}
160+
}
161+
162+
return $number_of_webhooks > 1;
163+
}
164+
128165
/**
129166
* Verify the incoming webhook notification to make sure it is legit.
130167
*

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class WC_Stripe_Webhook_State {
2727
const VALIDATION_FAILED_EMPTY_SECRET = 'empty_secret';
2828
const VALIDATION_FAILED_USER_AGENT_INVALID = 'user_agent_invalid';
2929
const VALIDATION_FAILED_SIGNATURE_INVALID = 'signature_invalid';
30+
const VALIDATION_FAILED_DUPLICATE_WEBHOOKS = 'duplicate_webhooks';
3031
const VALIDATION_FAILED_TIMESTAMP_MISMATCH = 'timestamp_out_of_range';
3132
const VALIDATION_FAILED_SIGNATURE_MISMATCH = 'signature_mismatch';
3233

@@ -170,7 +171,7 @@ public static function get_last_error_reason() {
170171
}
171172

172173
if ( self::VALIDATION_FAILED_EMPTY_SECRET === $last_error ) {
173-
return( __( 'The webhook secret is not set in the store', 'woocommerce-gateway-stripe' ) );
174+
return( __( 'The webhook secret is not set in the store. Please configure the webhooks', 'woocommerce-gateway-stripe' ) );
174175
}
175176

176177
// Legacy failure reason. Removed in 8.6.0.
@@ -182,6 +183,10 @@ public static function get_last_error_reason() {
182183
return( __( 'The webhook signature was missing or was incorrectly formatted', 'woocommerce-gateway-stripe' ) );
183184
}
184185

186+
if ( self::VALIDATION_FAILED_DUPLICATE_WEBHOOKS == $last_error ) {
187+
return( __( 'Multiple webhooks exist for this site. Please remove the duplicate webhooks or re-configure the webhooks', 'woocommerce-gateway-stripe' ) );
188+
}
189+
185190
if ( self::VALIDATION_FAILED_TIMESTAMP_MISMATCH == $last_error ) {
186191
return( __( 'The timestamp in the webhook differed more than five minutes from the site time', 'woocommerce-gateway-stripe' ) );
187192
}

readme.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ If you get stuck, you can ask for help in the [Plugin Forum](https://wordpress.o
135135
* Add - Correctly handles charge expired webhook events, setting the order status to failed and adding a note.
136136
* Fix - Allow account creation on checkout, if enabled, when purchasing subscriptions using ECE.
137137
* Tweak - Add empty check for cart when checking for allowed products for express checkout.
138+
* Tweak - Improve webhook status related messages on the settings page.
138139
* Update - Prevent editing of orders awaiting payment capture.
139140
* Add - Introduce locking and unlocking in refund flow to prevent double refund due to race condition.
140141
* Fix - Check order currency on pay for order page to display supported payment methods.

tests/phpunit/test-wc-stripe-webhook-state.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ public function test_get_error_reason_empty_secret() {
248248

249249
$this->set_valid_request_data();
250250
$this->process_webhook();
251-
$this->assertEquals( 'The webhook secret is not set in the store', WC_Stripe_Webhook_State::get_last_error_reason() );
251+
$this->assertEquals( 'The webhook secret is not set in the store. Please configure the webhooks', WC_Stripe_Webhook_State::get_last_error_reason() );
252252
}
253253

254254
// Test failure reason: invalid signature.

0 commit comments

Comments
 (0)