#ncls-webhook-signature
1 messages ยท Page 1 of 1 (latest)
Hi ๐
I would recommend logging the endpoint secret and ensuring it matches the secret for the webhook endpoint
We did, looks fine
What is the ID for the registered endpoint here?
we_1NjogwF8VC2qw3ffIZXNKbIx
^ This is my friend btw
im btw the friend of @idle arch
Also your code doesn't quite match what we have here: https://stripe.com/docs/webhooks#example-endpoint
ist currently in the Testmode
I got the code from here: https://stripe.com/docs/webhooks#verify-official-libraries
It used to work but somehow stopped
And it stopped working when you deployed your code?
No, it just randomly stopped recently. While we tested, everything was fine and we didn't change anything ever since but recently a customer couldn't pay using it and when we checked, we saw that it fails to verify the signature
It just randomly started doing that. We don't know why
At what time?
We can't tell when exactly it stopped working but the payment from the client was done on September 23rd
I can see that the live mode endpoint is disabled due to too many failed notification deliveries
I would recommend
- Double Checking the webhook secret
- Logging the payload
- Logging the
$sig_header - Testing with the
json_decodeapproach we list here: https://stripe.com/docs/webhooks#example-endpoint - Checking with your hosting provider to ensure they didn't start modifying the incoming POST request body.
- Done, secret is correct
- Payload looks fine
- Sig-Header looks fine
- Will most likely work since this approach doesn't seem to check the signature
- Seems to not be an issue since everything above looks fine
I can tell you that Stripe did not change anything about how Stripe signatures are constructed or the payloads of webhook events.
So I would suggest a closer look at your code
I tried generating the signature myself and they don't match unless I did it wrong. I did the following:
- Getting the timestamp from the request header, getting the payload and combining it with a dot so that the string looks something like this:
1696263552.{"key": "value"}(Is the space the issue? Shouldn't be, right?) - Then I encrypted that string using SHA-256 with our webhook secret as a key.
I don't know if I can share the signature from Stripe and what I got doing it manually due to security reasons but they are not the same and I don't know why.
Since I use the official Stripe PHP Library to do all the logic and compare them, it should work, right?
The Stripe library includes a tolerance parameter for calculating the timestamp.
Any whitespace could be the issue here as well
Since the payload must match exactly what we expect
I mean, if Stripe sends their payload with whitespace, which seems to be the case (please correct me if I'm wrong), then whitespace can't be the issue.
And what do you mean by that?
You can review the actual functions the PHP library uses here: https://github.com/stripe/stripe-php/blob/master/lib/WebhookSignature.php
This is the part that's causing our error:
// Check if expected signature is found in list of signatures from
// header
$signedPayload = "{$timestamp}.{$payload}";
$expectedSignature = self::computeSignature($signedPayload, $secret);
$signatureFound = false;
foreach ($signatures as $signature) {
if (Util\Util::secureCompare($expectedSignature, $signature)) {
$signatureFound = true;
break;
}
}
if (!$signatureFound) {
throw Exception\SignatureVerificationException::factory(
'No signatures found matching the expected signature for payload',
$payload,
$header
);
}
Yes and it hasn't changed in over 3 years
But what can cause this then? The secret is correct, the header and payload look fine too. Unless Stripe sends the wrong timestamp, uses another payload format while generating the signature or is using the wrong secret, there's nothing coming to my mind that can cause this behavior
If stripe were sending the wrong timestamp then all webhook events would be failing to deliver and that would definitely be a big deal.
Yes, I know. I'm absolutely clueless...
It makes zero sense. Triple checked everything
And this is for the LIve or the Test mode endpoints?
ncls-webhook-signature
@idle arch This is almost always a bug on your end. Either using the wrong secret even though you think you use the right one, or having something in your stack that "modifies" the JSON payload we send. Anything in your system that changes that JSON is going to cause issue.
Unfortunately this can be quite hard to debug but my recommendation is to add clear logs to your own code to dump the content of the payload and the secret to a file for example and confirm they look as expected
I just quadruple-checked the webhook secret again by copying it from Stripe and using CTRL + F in my logs. Looked fine. No whitespaces in front or back either.
Also logged the payload I get from Stripe. Format looks fine to me, but maybe you can spot somehing that's off since you see these way more often than me (I removed all IDs and personal info):
{
"id": "<hidden>",
"object": "event",
"api_version": "2022-11-15",
"created": 1696263552,
"data": {
"object": {
"id": "<hidden>",
"object": "checkout.session",
"after_expiration": null,
"allow_promotion_codes": null,
"amount_subtotal": 500,
"amount_total": 500,
"automatic_tax": {
"enabled": false,
"status": null
},
"billing_address_collection": null,
"cancel_url": null,
"client_reference_id": null,
"consent": null,
"consent_collection": null,
"created": 1696261752,
"currency": "eur",
"currency_conversion": null,
"custom_fields": [
],
"custom_text": {
"shipping_address": null,
"submit": null,
"terms_of_service_acceptance": null
},
"customer": null,
"customer_creation": "if_required",
"customer_details": {
"address": {
"city": null,
"country": "AT",
"line1": null,
"line2": null,
"postal_code": null,
"state": null
},
"email": "<hidden>",
"name": "<hidden>",
"phone": null,
"tax_exempt": "none",
"tax_ids": [
]
},
"customer_email": null,
"expires_at": 1696348152,
"invoice": null,
"invoice_creation": {
"enabled": false,
"invoice_data": {
"account_tax_ids": null,
"custom_fields": null,
"description": null,
"footer": null,
"metadata": {
},
"rendering_options": null
}
},
"livemode": false,
"locale": null,
"metadata": {
"invoice_id": "8859"
},
"mode": "payment",
"payment_intent": "<hidden>",
"payment_link": null,
"payment_method_collection": "if_required",
"payment_method_configuration_details": {
"id": "<hidden>",
"parent": null
},
"payment_method_options": {
},
"payment_method_types": [
"card",
"bancontact",
"eps",
"giropay",
"ideal",
"p24",
"sofort",
"alipay",
"klarna",
"link"
],
"payment_status": "paid",
"phone_number_collection": {
"enabled": false
},
"recovered_from": null,
"setup_intent": null,
"shipping_address_collection": null,
"shipping_cost": null,
"shipping_details": null,
"shipping_options": [
],
"status": "complete",
"submit_type": null,
"subscription": null,
"success_url": "<hidden>",
"total_details": {
"amount_discount": 0,
"amount_shipping": 0,
"amount_tax": 0
},
"url": null
}
},
"livemode": false,
"pending_webhooks": 1,
"request": {
"id": null,
"idempotency_key": null
},
"type": "checkout.session.completed"
}
Unfortunately I won't be able to help much more than this right now I'm sorry. The first step is to find the different between your local environment and your server where the code runs and diff the exact payload you get on both and the secrets. This is really something you need to debug on your side. For example make it so that the exact same Event goes to both places to compare the behaviour
There is just one place. I developed and tested it on the same exact server where it is currently still running
This does mean either you have the wrong secret or something did change in your set up or your code.
I'm sorry I know it's cryptic but there isn't much I can do here beyond recommending hardcoding the exact secret you want and making sure nothing in your stack is modifying the payload we send.
I copied the payload from the Stripe logs and the payload I logged myself and only difference I notice is due to the way the Stripe dashboard formats the pay load. Checked the network tab to verify that it's the website and that nothing on my end is adding additional line breaks but yeah... So in the end, no difference...
I copied the payload from the Stripe logs
what does that means exactly? An event and a Request Log are completely unrelated
The log from the webhook where it shows what it sent
That doesn't mean it's the real/exact payload so that doesn't really prove anything
What is it then?
not sure what you mean. You're assuming you can copy-paste some text output in a browser to mirror the exact payload we sent you and I don't think that's true/correct
Huh?
It's just text, it can be different from what a real HTTPS request will do to your server
here's my exact code https://pastebin.com/raw/xa4MaaYA
I just tried exactly that with the right webhook secret I just got on a new endpoint and that works fine for me. Can you try that
Ok, I'm an idiot. Or my friend rather because I never use Stripe...๐
I didn't know that live and test mode even had different webhook secrets. Sorry for that...๐
All good
This is surprisingly hard to debug ๐ฆ
We can't know if the webhook secret is the right one
Yeah, I didn't know that and just saw it by accident while clicking my way through the dashboard. I now added two modes to my plugin to switch between test and live mode with their respectable stuff.
Thanks for your help. You can close this thread, if you guys do that.
Good evening