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:
import pytest
from unittest.mock import patch
@pytest.fixture
def mock_stripe_verify_header():
with patch("stripe.WebhookSignature.verify_header") as mock_verify:
mock_verify.return_value = None
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:
@csrf_exempt
@require_POST
def stripe_webhook(request):
payload = request.body
sig_header = request.META["HTTP_STRIPE_SIGNATURE"]
try:
event = stripe.Webhook.construct_event(
payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
)
except (ValueError, stripe.error.SignatureVerificationError) as ex:
# Invalid payload or signature
return HttpResponse(status=400, content=json.dumps({"error": str(ex)}))
Now I can write tests like this one:
STRIPE_PAYLOAD = {
"id": "evt_test123",
"object": "event",
"type": "payment_intent.succeeded",
"created": 1630000000,
"data": {
"object": {
"id": "pi_test123",
"object": "payment_intent",
"amount": 1000,
"currency": "usd",
}
},
"livemode": False,
"api_version": "2020-08-27",
}
@pytest.mark.django_db
def test_valid_webhook(client, mock_stripe_verify_header):
response = client.post(
WEBHOOK_URL,
data=json.dumps(STRIPE_PAYLOAD),
content_type="application/json",
HTTP_STRIPE_SIGNATURE="ignored",
)
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:
def test_invalid_webhook(client, mock_stripe_verify_header):
mock_stripe_verify_header.side_effect = SignatureVerificationError(
"Invalid signature", sig_header="ignored"
)
response = client.post(
WEBHOOK_URL,
data=json.dumps(STRIPE_PAYLOAD),
content_type="application/json",
HTTP_STRIPE_SIGNATURE="ignored",
)
assert response.status_code == 400
assert response.content == b'{"error": "Invalid signature"}'
Created 2024-07-01T17:50:48-07:00 · Edit