Subscribing to Stripe webhook events to be notified when payments are done? After implementing your code, it's time to make sure it works perfectly by coupling with some test cases. In this article, I'll be writing about why testing Stripe webhook handlers require special care, and how to test them.
This article is written with language-agnostic in mind. This should be applicable to any programming languages or frameworks.
Understanding Why Testing Stripe Webhook Handlers Is Different
I classify webhooks into two categories: secure ones and insecure ones.
For insecure webhook, events are dispatched by third-party providers without giving us the option to verify the authenticity of the events, essentially making us prone to various attacks (replay attack, spoofing attack).
To mitigate security issues with insecure webhooks, third-party service providers adopt various strategies to allow us (the service integrators) to verify the authenticity of webhook events received, which I classify them as secure webhooks. In the case of Stripe, at least with API version 2019-03-14, signs all webhook events with a symmetrical key known as the Endpoint Secret.
You probably have seen code examples provided by Stripe, demonstrating how to verify the authenticity of events in their Check The Webhook Signature documentation.
To test your webhook handlers, it is likely that you are mimicking webhook events and send them to your handlers as if what Stripe would do in production. To ensure a "legitimate" Stripe webhook event can be constructed for testing purpose, it is crucial to understand how Stripe signs their webhook payloads.
Understanding How Stripe Signs Their Webhook Payloads
As I mentioned, while implementing webhook handlers, we should be able to verify the authenticity of events receiving. To achieve that, Stripe uses the following algorithm when dispatching an event:
First, a signature is generated from the current time and the payload, and is signed with a symmetrical key (Stripe endpoint secret) using HCMA SHA256:
Tip: I discourage the use of real endpoint secrets in testing environment. Given the fact that we provide the endpoint secret to our webhook handlers, using any random string should work
Second, the signature is then concatenated with the timestamp and is passed as the value of the HTTP header Stripe-Signature:
Once you are clear with the way Stripe signs their webhook events, you can start writing some test cases.
Tip: Stripe libraries come with a private method to construct signatures. Exploring the source code should give you a good idea on how to implement signature generation with your programming language of choice
Getting Sample Webhook Events From Stripe
On your Stripe Dashboard, you should be able to access the feature "Send test webhook" while setting up endpoints. That feature mimics the actual payload Stripe sends but with fake data instead. With the sample payloads from the dashboard, I created a
PayloadFactory that generates customised webhook events to simulate various real-life scenarios.
With all the ingredients and the right recipe, you should be able to test your webhook handlers by simulating real-life scenario.
Bonus: The Trust Issue With Webhook Events
When listening to webhook events, we entrust the service provider to send us genuine and legitimate data. The webhook events are considered reliable when we can verify the authenticity of the payload. However, for service providers that don't provide such option, which I call those webhooks the insecure ones, does that mean we should not use their webhooks?
The answer varies. If webhook integration is the only option to support our business requirements, we have no choice. In this case, I advocate NOT to rely on any event payloads. Yes, you heard it right!
In the case of using unauthenticable webhooks, what we are listening is occurrences of events. If an event occurred and we are notified by the service provider, we should use the primary identifier (any ids) provided in the payload to re-query the actual state/data from our service provider. By doing so, we eliminate the possibility of any potential spoofing attacks.
What about Stripe Webhook? Now you ask, with the ability to verify the authenticity of webhook events, and the fact that the signature is generated from the event payload, we can be confident with the events we receive. Of course, this doesn't apply if our endpoint secret is exposed 😅
I hope this article helps! Feel free to share it with your mates 🤗 This is not an affiliated post. I welcome any constructive feedback.