#diezler-webhook-signature-django
1 messages · Page 1 of 1 (latest)
The three things that can typically go wrong here are passing in the wrong secret, passing in the wrong signature header, and modifying the payload before passing it in to the construct event function
To double check the first two:
- Is this webhook secret the secret for this specific webhook endpoint? Different webhook endpoints on your account will have different secrets. For example each endpoint created in your dashboard will have a secret, and if you are using a CLI webhook listener, that will have a different secret from all of your webhook endpoints
- Can you print out
sig_headerto make sure that variable is populated?
done both, the endpoint is hosted with local cli and I copied the secret of that spefic endpoint into the code,
the signature contains data aswell
I get this from the headers
(blurred, dont know if sensitive data)
t=**********,v1=**************************************************,v0=**************************************************
Fine to share here, but that is the right format so you have that set correctly
Now we get to the fun of making sure your server is passing in the raw event body
It looks like you may need to use request.body instead of request.data? https://stackoverflow.com/questions/22368190/django-cant-access-raw-post-data
I am experiencing a strange thing with Django, here is my views.py:
def api(request):
return HttpResponse("%s %s" % (request.method,request.raw_post_data))
Now I make an HTTP POST with POSTMAN (
This is what i get when printing the payload
{'id': 'evt_3OKMwYEfrxFmu5w60YtmKskr', 'object': 'event', 'api_version': '2023-10-16', 'created': 1701876202, 'data': {'object': {'id': 'pi_3OKMwYEfrxFmu5w60LTAQV5v', 'object': 'payment_intent', 'amount': 1000, 'amount_capturable': 0, 'amount_details': {'tip': {}}, 'amount_received': 0, 'application': None,
'application_fee_amount': None, 'automatic_payment_methods': None, 'canceled_at': None, 'cancellation_reason': None, 'capture_method': 'automatic', 'client_secret': 'pi_3OKMwYEfrxFmu5w60LTAQV5v_secret_5BTvf0FQPTBZQjnLkbSF5jZkJ', 'confirmation_method': 'automatic', 'created': 1701876202, 'currency': 'eur',
'customer': None, 'description': None, 'invoice': None, 'last_payment_error': None, 'latest_charge': None, 'livemode': False, 'metadata': {}, 'next_action': None, 'on_behalf_of': None, 'payment_method': None, 'payment_method_configuration_details': None, 'payment_method_options': {'card': {'installments': N
one, 'mandate_options': None, 'network': None, 'request_three_d_secure': 'automatic'}}, 'payment_method_types': ['card'], 'processing': None, 'receipt_email': None, 'review': None, 'setup_future_usage': None, 'shipping': None, 'source': None, 'statement_descriptor': None, 'statement_descriptor_suffix': None, 'status': 'requires_payment_method', 'transfer_data': None, 'transfer_group': None}}, 'livemode': False, 'pending_webhooks': 2, 'request': {'id': 'req_WOTUdWsUjOrftr', 'idempotency_key': 'a4307b51-6883-4c86-aa96-edc5e6cb01dc'}, 'type': 'payment_intent.created'}
when I use the body, I get that i cant read the stream again.
django.http.request.RawPostDataException: You cannot access body after reading from request's data stream
That is the payload but unfortunately I can't tell if it is correct or not. The construct event function requires the raw request body so even things like missing spaces will throw off the hash calculation
Gotcha, I'd reccommend looking more in to how to get a raw POST request body in django. We don't know as much about django specifically on here but it looks like the raw request body is the issue from the Stripe perspective
Alright, gonna try to find a way to get the raw body
For anyone else encoutering the problem, you need to create a custom parser
class BodySavingJSONParser(BaseParser):
"""
Parses JSON-serialized data.
"""
media_type = 'application/json'
renderer_class = renderers.JSONRenderer
strict = api_settings.STRICT_JSON
def parse(self, stream, media_type=None, parser_context=None):
"""
Parses the incoming bytestream as JSON and returns the resulting data.
"""
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
request = parser_context.get('request')
try:
decoded_stream = codecs.getreader(encoding)(stream)
decoded_content = decoded_stream.read()
# Saving decoded request original body to original_body
setattr(request, 'original_body', decoded_content)
parse_constant = json.strict_constant if self.strict else None
return json.loads(decoded_content, parse_constant=parse_constant)
except ValueError as exc:
raise ParseError('JSON parse error - %s' % str(exc))
Glad you figured it out! Thanks for reporting back, will definitely reccommend that to django users in future.