2026-04-28 10:45:29 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import base64
|
|
|
|
|
import json
|
|
|
|
|
|
|
|
|
|
from v1.payments.apple_verifier import (
|
|
|
|
|
AppleJwsVerifier,
|
|
|
|
|
VerificationError,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _make_jws_parts(header: dict[str, object], payload: dict[str, object]) -> tuple[str, str]:
|
|
|
|
|
h = base64.urlsafe_b64encode(json.dumps(header).encode()).rstrip(b"=").decode()
|
|
|
|
|
p = base64.urlsafe_b64encode(json.dumps(payload).encode()).rstrip(b"=").decode()
|
|
|
|
|
return h, p
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestAppleJwsVerifierInvalidInput:
|
|
|
|
|
def test_invalid_header_returns_error(self) -> None:
|
|
|
|
|
verifier = AppleJwsVerifier()
|
|
|
|
|
result = verifier.verify_signed_transaction(
|
|
|
|
|
"not-a-jws",
|
|
|
|
|
expected_bundle_id="com.meeyao.qianwen",
|
2026-04-28 17:21:14 +08:00
|
|
|
expected_product_id="com.meeyao.qianwen.starter_pack",
|
2026-04-28 10:45:29 +08:00
|
|
|
)
|
|
|
|
|
assert isinstance(result, VerificationError)
|
|
|
|
|
assert result.code == "PAYMENT_TRANSACTION_INVALID"
|
|
|
|
|
assert "decode" in result.detail.lower() or "header" in result.detail.lower()
|
|
|
|
|
|
|
|
|
|
def test_missing_x5c_returns_error(self) -> None:
|
|
|
|
|
verifier = AppleJwsVerifier()
|
|
|
|
|
h, p = _make_jws_parts({"alg": "ES256"}, {"bundleId": "test"})
|
|
|
|
|
result = verifier.verify_signed_transaction(
|
|
|
|
|
f"{h}.{p}.fake",
|
|
|
|
|
expected_bundle_id="com.meeyao.qianwen",
|
2026-04-28 17:21:14 +08:00
|
|
|
expected_product_id="com.meeyao.qianwen.starter_pack",
|
2026-04-28 10:45:29 +08:00
|
|
|
)
|
|
|
|
|
assert isinstance(result, VerificationError)
|
|
|
|
|
assert "x5c" in result.detail
|
|
|
|
|
|
|
|
|
|
def test_short_x5c_returns_error(self) -> None:
|
|
|
|
|
verifier = AppleJwsVerifier()
|
|
|
|
|
h, p = _make_jws_parts({"alg": "ES256", "x5c": ["one"]}, {"bundleId": "test"})
|
|
|
|
|
result = verifier.verify_signed_transaction(
|
|
|
|
|
f"{h}.{p}.fake",
|
|
|
|
|
expected_bundle_id="com.meeyao.qianwen",
|
2026-04-28 17:21:14 +08:00
|
|
|
expected_product_id="com.meeyao.qianwen.starter_pack",
|
2026-04-28 10:45:29 +08:00
|
|
|
)
|
|
|
|
|
assert isinstance(result, VerificationError)
|
|
|
|
|
assert "x5c" in result.detail
|
|
|
|
|
|
|
|
|
|
def test_issuer_subject_mismatch_returns_error(self) -> None:
|
|
|
|
|
verifier = AppleJwsVerifier()
|
|
|
|
|
leaf_cert_b64 = base64.b64encode(b"fake_leaf_cert").decode()
|
|
|
|
|
intermediate_cert_b64 = base64.b64encode(b"fake_intermediate_cert").decode()
|
|
|
|
|
root_cert_b64 = base64.b64encode(b"fake_root_cert").decode()
|
|
|
|
|
h, p = _make_jws_parts(
|
|
|
|
|
{"alg": "ES256", "x5c": [leaf_cert_b64, intermediate_cert_b64, root_cert_b64]},
|
|
|
|
|
{"bundleId": "com.meeyao.qianwen"},
|
|
|
|
|
)
|
|
|
|
|
result = verifier.verify_signed_transaction(
|
|
|
|
|
f"{h}.{p}.fake",
|
|
|
|
|
expected_bundle_id="com.meeyao.qianwen",
|
2026-04-28 17:21:14 +08:00
|
|
|
expected_product_id="com.meeyao.qianwen.starter_pack",
|
2026-04-28 10:45:29 +08:00
|
|
|
)
|
|
|
|
|
assert isinstance(result, VerificationError)
|
|
|
|
|
assert "fingerprint" in result.detail or "issuer" in result.detail or "subject" in result.detail
|