Mocking Stripe signature checks in a pytest fixture#
I’m writing some code that accepts webhooks from Stripe. I wanted to simulate hits to this endpoint in my Django tests. Stripe uses a Stripe-Signature
header and I wanted a way to mock my code so that I didn’t need to calculate the correct signature.
Here’s the pattern I used:
1import pytest2from unittest.mock import patch3
4@pytest.fixture5def mock_stripe_verify_header():6 with patch("stripe.WebhookSignature.verify_header") as mock_verify:7 mock_verify.return_value = None8 yield mock_verify
This gives me a mock_stripe_verify_header
fixture which I can pass to a test function in order to cause the verify_header()
method in the Stripe library to always pass - as opposed to raising exceptions, which it does when the signature check fails. The corresponding Django view code that uses this API does so via stripe.Webhook.construct_event()
and looks like this:
1@csrf_exempt2@require_POST3def stripe_webhook(request):4 payload = request.body5 sig_header = request.META["HTTP_STRIPE_SIGNATURE"]6 try:7 event = stripe.Webhook.construct_event(8 payload, sig_header, settings.STRIPE_WEBHOOK_SECRET9 )10 except (ValueError, stripe.error.SignatureVerificationError) as ex:11 # Invalid payload or signature12 return HttpResponse(status=400, content=json.dumps({"error": str(ex)}))
Now I can write tests like this one:
1STRIPE_PAYLOAD = {2 "id": "evt_test123",3 "object": "event",4 "type": "payment_intent.succeeded",5 "created": 1630000000,6 "data": {7 "object": {8 "id": "pi_test123",9 "object": "payment_intent",10 "amount": 1000,11 "currency": "usd",12 }13 },14 "livemode": False,15 "api_version": "2020-08-27",16}17
18@pytest.mark.django_db19def test_valid_webhook(client, mock_stripe_verify_header):20 response = client.post(21 WEBHOOK_URL,22 data=json.dumps(STRIPE_PAYLOAD),23 content_type="application/json",24 HTTP_STRIPE_SIGNATURE="ignored",25 )26 assert response.status_code == 200
Or if I want to test what happens when the signature check fails, I can use mock_stripe_verify_header.side_effect
to cause it to trigger the expected exception:
1def test_invalid_webhook(client, mock_stripe_verify_header):2 mock_stripe_verify_header.side_effect = SignatureVerificationError(3 "Invalid signature", sig_header="ignored"4 )5 response = client.post(6 WEBHOOK_URL,7 data=json.dumps(STRIPE_PAYLOAD),8 content_type="application/json",9 HTTP_STRIPE_SIGNATURE="ignored",10 )11 assert response.status_code == 40012 assert response.content == b'{"error": "Invalid signature"}'