Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mercadopago/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Config:

def __init__(self):
"""Builds version-dependent values (user_agent, tracking_id)."""
self.__version = "3.0.2"
self.__version = "3.1.0"
self.__user_agent = "MercadoPago Python SDK v" + self.__version
self.__product_id = "bc32bpftrpp001u8nhlg"
self.__tracking_id = "platform:" + platform.python_version()
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "mercadopago"
version = "3.0.2"
version = "3.1.0"
authors = [
{name = "Mercado Pago", email = "mp_sdk@mercadopago.com"}
]
Expand Down
22 changes: 13 additions & 9 deletions tests/test_webhook_signature_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@
def compute_hash(data_id, request_id, ts, secret):
parts = []
if data_id:
parts.append("id:{0}".format(data_id.lower()))
parts.append(f"id:{data_id.lower()}")
if request_id:
parts.append("request-id:{0}".format(request_id))
parts.append("ts:{0}".format(ts))
parts.append(f"request-id:{request_id}")
parts.append(f"ts:{ts}")
manifest = ";".join(parts) + ";"
return hmac.new(secret.encode("utf-8"), manifest.encode("utf-8"), hashlib.sha256).hexdigest()


def build_header(h, ts=TS, version="v1"):
return "ts={0},{1}={2}".format(ts, version, h)
return f"ts={ts},{version}={h}"


VALID_HASH = compute_hash(DATA_ID_LOWER, REQUEST_ID, TS, SECRET)
Expand Down Expand Up @@ -70,23 +70,27 @@ def test_missing_header_raises_missing_header(self):

# --- case 5 ---
def test_missing_ts_raises_missing_timestamp(self):
header = "v1={0}".format(VALID_HASH)
header = f"v1={VALID_HASH}"
with self.assertRaises(InvalidWebhookSignatureError) as ctx:
WebhookSignatureValidator.validate(header, REQUEST_ID, DATA_ID_LOWER, SECRET)
self.assertEqual(ctx.exception.reason, SignatureFailureReason.MISSING_TIMESTAMP)

# --- case 6 ---
def test_missing_v1_raises_missing_hash(self):
with self.assertRaises(InvalidWebhookSignatureError) as ctx:
WebhookSignatureValidator.validate("ts={0}".format(TS), REQUEST_ID, DATA_ID_LOWER, SECRET)
WebhookSignatureValidator.validate(
f"ts={TS}", REQUEST_ID, DATA_ID_LOWER, SECRET
)
self.assertEqual(ctx.exception.reason, SignatureFailureReason.MISSING_HASH)
self.assertEqual(ctx.exception.timestamp, TS)

# --- case 7 ---
def test_tampered_hash_raises_signature_mismatch(self):
tampered = VALID_HASH[:-2] + ("ff" if VALID_HASH.endswith("00") else "00")
with self.assertRaises(InvalidWebhookSignatureError) as ctx:
WebhookSignatureValidator.validate(build_header(tampered), REQUEST_ID, DATA_ID_LOWER, SECRET)
WebhookSignatureValidator.validate(
build_header(tampered), REQUEST_ID, DATA_ID_LOWER, SECRET
)
self.assertEqual(ctx.exception.reason, SignatureFailureReason.SIGNATURE_MISMATCH)

# --- case 8 ---
Expand Down Expand Up @@ -131,12 +135,12 @@ def test_non_payment_topic_uses_same_algorithm(self):

# --- supportedVersions ---
def test_supports_v1_when_both_present(self):
header = "ts={0},v1={1},v2=aaaa".format(TS, VALID_HASH)
header = f"ts={TS},v1={VALID_HASH},v2=aaaa"
WebhookSignatureValidator.validate(header, REQUEST_ID, DATA_ID_LOWER, SECRET,
supported_versions=["v1"])

def test_only_v2_in_header_only_v1_supported_raises_missing_hash(self):
header = "ts={0},v2=somehash".format(TS)
header = f"ts={TS},v2=somehash"
with self.assertRaises(InvalidWebhookSignatureError) as ctx:
WebhookSignatureValidator.validate(header, REQUEST_ID, DATA_ID_LOWER, SECRET,
supported_versions=["v1"])
Expand Down
Loading