Skip to content

Commit f2a418a

Browse files
committed
Add webhooks helper
1 parent fa13513 commit f2a418a

File tree

1 file changed

+55
-0
lines changed

1 file changed

+55
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import base64
2+
import hashlib
3+
import hmac
4+
5+
6+
def verify_signature(
7+
*,
8+
request_body: str,
9+
signature_header: str,
10+
signature_key: str,
11+
notification_url: str,
12+
) -> bool:
13+
"""
14+
Verifies and validates an event notification. See the `documentation`_ for more details.
15+
16+
Args:
17+
request_body: The JSON body of the request.
18+
signature_header: The value for the `x-square-hmacsha256-signature` header.
19+
signature_key: The signature key from the Square Developer portal for the webhook subscription.
20+
notification_url: The notification endpoint URL as defined in the Square Developer portal for the webhook
21+
subscription.
22+
23+
Returns:
24+
bool: True if the signature is valid, indicating that the event can be trusted as it came from Square.
25+
False if the signature validation fails, indicating that the event did not come from Square, so it may
26+
be malicious and should be discarded.
27+
28+
Raises:
29+
ValueError: if `signature_key` or `notification_url` are empty.
30+
31+
.. _documentation:
32+
https://developer.squareup.com/docs/webhooks/step3validate
33+
"""
34+
if not request_body:
35+
return False
36+
if not signature_key:
37+
raise ValueError("signature_key is empty")
38+
if not notification_url:
39+
raise ValueError("notification_url is empty")
40+
41+
# Perform UTF-8 encoding to bytes
42+
payload = notification_url + request_body
43+
payload_bytes = payload.encode("utf-8")
44+
signature_header_bytes = signature_header.encode("utf-8")
45+
signature_key_bytes = signature_key.encode("utf-8")
46+
47+
# Compute the hash value
48+
hashing_obj = hmac.new(
49+
key=signature_key_bytes, msg=payload_bytes, digestmod=hashlib.sha256
50+
)
51+
hash_bytes = hashing_obj.digest()
52+
53+
# Compare the computed hash vs the value in the signature header
54+
hash_base64 = base64.b64encode(hash_bytes)
55+
return hmac.compare_digest(hash_base64, signature_header_bytes)

0 commit comments

Comments
 (0)