Stripe: No signatures found matching the expected signature for payload using Django

Viewed 1429

I am getting the below error while Stripe calling the defined Web hook

No signatures found matching the expected signature for payload

I am following this article: https://stripe.com/docs/billing/subscriptions/checkout#provision-and-monitor

And I have following code:

@csrf_exempt
def saaswebhookview(request):
    try:
        stripe.api_key = settings.STRIPE_SECRET_KEY
        webhook_secret = 'stripe_key'
        request_data = request.POST
        if webhook_secret:
            try:
                signature = request.headers.get('stripe-signature')
                # signature = request.META['stripe-signature']
                event = stripe.Webhook.construct_event(
                    payload=request.POST, sig_header=signature, secret=webhook_secret)
                data = event['data']
            except Exception as e:
                print(str(e))
                return JsonResponse({'status': 'error', 'error': str(e)})
            event_type = event['type']
        else:
            data = request_data['data']
            event_type = request_data['type']
        data_object = data['object']
        if event_type == 'checkout.session.completed':
            print(data)
        elif event_type == 'invoice.paid':
            print(data)
        elif event_type == 'invoice.payment_failed':
            print(data)
        else:
            print('Unhandled event type {}'.format(event_type))
        return JsonResponse({'status': 'success'}, safe=False)
    except Exception as e:
        return JsonResponse({'status': 'success', 'error': str(e)}, safe=False)

But strangely this throws me the error don't know why?

3 Answers

The Stripe library requires the raw body of the webhook request for signature verification to work. It looks like you're supplying request.POST, which is an altered version of the body.

If you use request.body instead it should work as expected.

Script to verify your Stripe Webhook API with generated signature

import hmac
import time
from hashlib import sha256

import requests
import stripe

webhook_url = 'https://your-site/api/stripe/webhook/'
stripe_secret = 'YOUR_STRIPE_WEBHOOK_SIGN_SECRET'  # whsec_..
webhook_json_file = 'PATH_TO_JSON_FILE_WITH_WH_DATA/webhook_example.json'


def generate_stripe_signature_header(payload: str):
    timestamp_utc = int(time.time())
    signed_payload = "%d.%s" % (timestamp_utc, payload)
    v1_sig = compute_signature(signed_payload, secret=stripe_secret)
    return f't={timestamp_utc},v1={v1_sig}'


def compute_signature(payload: str, secret):
    """Take from stripe"""
    mac = hmac.new(
        secret.encode("utf-8"),
        msg=payload.encode("utf-8"),
        digestmod=sha256,
    )
    return mac.hexdigest()


with open(webhook_json_file, 'r') as f:
    payload = f.read()

signature = generate_stripe_signature_header(payload=payload)

headers = {
    'STRIPE-SIGNATURE': signature,  
    'Content-Type': 'application/json',
}

session = requests.Session()
session.headers.update(**headers)
response = session.post(webhook_url, data=payload)

print(response.content)
print(response.status_code)
response.raise_for_status()

P.S. IF you want to use it for unit tests / Django Test Client, you need to add HTTP_ prefix to the signature header

signature = generate_stripe_signature_header(payload=payload)
headers = {
    'HTTP_STRIPE-SIGNATURE': signature,  
    'Content-Type': 'application/json',
}
self.client.post(webhook_url, data=payload, **headers)

In my case, for to resolve my problem I decoding the body:

payload = request.body.decode('utf-8')
Related