Почему заголовок Stripe-Signature никогда не совпадает с подписью в request.body?
Я использую Python с Django и пытаюсь правильно получать события webhook от stripe.
Однако я постоянно получаю эту ошибку:
stripe.error.SignatureVerificationError: Не найдено подписей, соответствующих ожидаемой подписи для полезной нагрузки
Вот код:
WEBHOOK_SECRET = settings.STRIPE_WEBHOOK_SK
@csrf_exempt
def webhook(request):
sig_header = request.headers.get('Stripe-Signature', None)
payload = request.body
try:
event = stripe.Webhook.construct_event(
payload=payload,
sig_header=sig_header,
secret=WEBHOOK_SECRET
)
except ValueError as e:
raise e
except stripe.error.SignatureVerificationError as e:
raise e
return HttpResponse(status=200)
Я также попробовал изменить формат тела запроса следующим образом:
payload = request.body.decode('utf-8')
# and also
payload = json.loads(request.body)
И все равно не повезло.
Ошибка исходит от метода класса verify_header()
внутри класса WebhookSignature
.
Это та часть метода, где он терпит неудачу:
if not any(util.secure_compare(expected_sig, s) for s in signatures):
raise error.SignatureVerificationError(
"No signatures found matching the expected signature for payload",
header,
payload,
)
Поэтому я распечатал exptected_sig
и signatures
до этой строки и обнаружил, что независимо от того, в каком формате находится request.body
, signatures
всегда присутствует (что хорошо), но они никогда не соответствуют подписи из заголовка.
Почему так?
Когда Stripe рассчитывает подпись для события, которое она отправляет вам, она использует определенную "полезную нагрузку", представляющую все содержимое события. Подпись выполняется именно на этой полезной нагрузке, и любое изменение в ней, например, добавление новой строки, удаление пробела или изменение порядка свойств, изменит полезную нагрузку и соответствующую подпись.
Когда вы проверяете подпись, вам нужно убедиться, что вы передаете точную необработанную полезную нагрузку, которую прислала вам Stripe, иначе вычисленная вами подпись не будет соответствовать подписи Stripe.
Фреймворки иногда пытаются быть полезными при получении запроса, они определяют JSON и автоматически разбирают его для вас. Это означает, что вы думаете, что получаете "необработанную полезную нагрузку/тело", но на самом деле вы получаете альтернативную версию. Она имеет то же содержание, но не совпадает с тем, что прислала вам Stripe.
Это довольно часто встречается, например, в Express в Node.js. Поэтому, как разработчик, вы должны явно запросить точную необработанную/оригинальную полезную нагрузку, которую вам прислала Stripe. И то, как это сделать, может зависеть от множества факторов. На гитхабе stripe-node есть 2 проблемы с многочисленными потенциальными исправлениями здесь и здесь.
В Django может произойти то же самое, и вам нужно убедиться, что ваш код запрашивает необработанную полезную нагрузку. Похоже, что вы используете request.body
, как и ожидалось, но это один из моментов, в котором стоит разобраться подробнее.
Кроме того, еще одна распространенная ошибка - использование неправильного секрета Webhook. Например, если вы используете Stripe CLI, он создает для вас новый секрет, который отличается от того, который вы видите в Dashboard для этой конечной точки Webhook. Вам нужно убедиться, что вы используете правильный секрет в зависимости от среды, в которой вы работаете.