#diezler-webhook-signature-django

1 messages · Page 1 of 1 (latest)

wary urchinBOT
regal garden
#

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:

  1. 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
  2. Can you print out sig_header to make sure that variable is populated?
hard magnet
#

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=**************************************************
regal garden
#

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

hard magnet
#

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'}
hard magnet
#

django.http.request.RawPostDataException: You cannot access body after reading from request's data stream

regal garden
#

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

hard magnet
#

Alright, gonna try to find a way to get the raw body

hard magnet
#

For anyone else encoutering the problem, you need to create a custom parser

https://stackoverflow.com/questions/47662496/how-to-access-request-body-when-using-django-rest-framework-and-avoid-getting-ra/47662497#47662497

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))
regal garden
#

Glad you figured it out! Thanks for reporting back, will definitely reccommend that to django users in future.

wary urchinBOT