diff --git a/.changelog/preserve-receipt-subscription-id.md b/.changelog/preserve-receipt-subscription-id.md new file mode 100644 index 0000000..23e21d6 --- /dev/null +++ b/.changelog/preserve-receipt-subscription-id.md @@ -0,0 +1,5 @@ +--- +pympp: patch +--- + +Preserve `subscriptionId` when parsing and formatting payment receipts. diff --git a/src/mpp/__init__.py b/src/mpp/__init__.py index 12ac054..77d0545 100644 --- a/src/mpp/__init__.py +++ b/src/mpp/__init__.py @@ -380,6 +380,7 @@ class Receipt: reference: str method: str = "tempo" external_id: str | None = None + subscription_id: str | None = None extra: dict[str, Any] | None = None @classmethod @@ -398,6 +399,7 @@ def success( timestamp: datetime | None = None, method: str = "tempo", external_id: str | None = None, + subscription_id: str | None = None, ) -> Receipt: """Create a success receipt with current timestamp.""" return cls( @@ -406,6 +408,7 @@ def success( reference=reference, method=method, external_id=external_id, + subscription_id=subscription_id, ) diff --git a/src/mpp/_parsing.py b/src/mpp/_parsing.py index 689ec21..5d8d17e 100644 --- a/src/mpp/_parsing.py +++ b/src/mpp/_parsing.py @@ -325,6 +325,7 @@ def parse_payment_receipt(header: str) -> Receipt: reference=str(data["reference"]), method=method, external_id=str(data["externalId"]) if data.get("externalId") else None, + subscription_id=str(data["subscriptionId"]) if data.get("subscriptionId") else None, extra=extra if isinstance(extra, dict) else None, ) @@ -345,6 +346,8 @@ def format_payment_receipt(receipt: Receipt) -> str: payload["method"] = receipt.method if receipt.external_id: payload["externalId"] = receipt.external_id + if receipt.subscription_id: + payload["subscriptionId"] = receipt.subscription_id if receipt.extra: payload["extra"] = receipt.extra return _b64_encode(payload) diff --git a/tests/test_parsing.py b/tests/test_parsing.py index 6b575e5..60a3301 100644 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -338,6 +338,7 @@ def test_roundtrip_with_optional_fields(self) -> None: method="tempo", external_id="order-123", extra={"plan": "pro"}, + subscription_id="sub_123", ) header = receipt.to_payment_receipt() @@ -346,6 +347,23 @@ def test_roundtrip_with_optional_fields(self) -> None: assert parsed.method == "tempo" assert parsed.external_id == "order-123" assert parsed.extra == {"plan": "pro"} + assert parsed.subscription_id == "sub_123" + + def test_parse_preserves_foreign_subscription_id(self) -> None: + payload = { + "status": "success", + "method": "tempo", + "timestamp": "2024-01-20T12:00:00Z", + "reference": "0xabc123def456", + "subscriptionId": "sub_123", + } + header = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().rstrip("=") + + parsed = Receipt.from_payment_receipt(header) + + assert parsed.subscription_id == "sub_123" + roundtripped = Receipt.from_payment_receipt(parsed.to_payment_receipt()) + assert roundtripped.subscription_id == "sub_123" def test_parse_invalid_timestamp(self) -> None: payload = {