- 支付凭证隔离:PaymentCredentialVault(只活内存、白名单
stripe-link/shop-pay、只存不透明 id + 掩码标签、原始卡号
PAN 拒绝入库、按次释放绑定商家/金额/货币、单次 + TTL)
- 结账页检测:detect_checkout(URL + 无障碍树信号,确定性
规则、不读原始 DOM、与 P4 detect_form_risk 同输入形状)
- 每次购买人工审批:PaymentGate 经 P5 对话外通道发审批,
payload 含确切购买详情;完成时先验签、再验详情摘要
(篡改即拒且审批保持待处理)、最后单次消费答复
- 一次性卡号:OneTimeCard 绑定商家/金额/货币/有效期,单次
兑现;SimulatedWalletBackend 沙箱钱包(假 PAN 只在后端
私有内存、charge 只记账不联网、取消即 void)
- 限额:SpendLimits 三档 minor units 比对,限额不可用时
不编造上限(limits_checked=False)
- 审计:PaymentAudit 只留允许字段,秘密字段名丢弃 + redacted
- 审批通道最小扩展:ApprovalRequest 增加可选 details 字段,
不传时 payload 与 P5 完全一致
- 真实支付与扣款不做:全部沙箱验证,真实钱包集成待用户
另行批准;新测试 79 项,全量 cloud 套件 2037 OK
round-12 集成说明:本提交为 acf98f8 的 cherry-pick(只含 P10
自有 diff,基线 25bcd35 已含的 P3/P5 内容未重复合入;
approval_channel.py 仅加可选 details 字段)。
134 lines
5.4 KiB
Python
134 lines
5.4 KiB
Python
"""对齐 Muse P10 结账检测测试:detect_checkout 纯函数。
|
|
|
|
只用标准库,不启动浏览器、不联网。覆盖:
|
|
- URL 信号(结账路径 / 购物车 / 普通页 / 坏 URL);
|
|
- 无障碍树信号(支付字段 / 密码字段 / 下单按钮 / 金额文本);
|
|
- 中英文特征;节点为空;页面金额只作展示信号。
|
|
"""
|
|
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
try:
|
|
from cloud.chv.checkout_detect import detect_checkout
|
|
except ImportError:
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
|
from cloud.chv.checkout_detect import detect_checkout
|
|
|
|
|
|
def _node(role: str, name: str) -> dict:
|
|
return {"role": role, "name": name}
|
|
|
|
|
|
class CheckoutUrlTest(unittest.TestCase):
|
|
def test_checkout_path_is_checkout_high(self) -> None:
|
|
verdict = detect_checkout("https://shop.example.com/checkout")
|
|
self.assertTrue(verdict.is_checkout)
|
|
self.assertEqual(verdict.confidence, "medium")
|
|
self.assertIn("checkout_url", verdict.reasons)
|
|
|
|
def test_payment_path_is_checkout(self) -> None:
|
|
verdict = detect_checkout(
|
|
"https://shop.example.com/order/payment?step=2")
|
|
self.assertTrue(verdict.is_checkout)
|
|
self.assertIn("checkout_url", verdict.reasons)
|
|
|
|
def test_cart_path_is_not_checkout(self) -> None:
|
|
verdict = detect_checkout("https://shop.example.com/cart")
|
|
self.assertFalse(verdict.is_checkout)
|
|
self.assertEqual(verdict.confidence, "low")
|
|
self.assertIn("cart_url", verdict.reasons)
|
|
|
|
def test_plain_page_is_not_checkout(self) -> None:
|
|
verdict = detect_checkout("https://shop.example.com/products/1")
|
|
self.assertFalse(verdict.is_checkout)
|
|
self.assertEqual(verdict.confidence, "low")
|
|
self.assertEqual(verdict.reasons, [])
|
|
|
|
def test_bad_url_does_not_crash(self) -> None:
|
|
verdict = detect_checkout("::::not a url::::")
|
|
self.assertFalse(verdict.is_checkout)
|
|
|
|
def test_no_nodes_url_only_decision(self) -> None:
|
|
verdict = detect_checkout(
|
|
"https://shop.example.com/checkout", None)
|
|
self.assertTrue(verdict.is_checkout)
|
|
|
|
|
|
class CheckoutNodesTest(unittest.TestCase):
|
|
def test_payment_fields_make_checkout_high(self) -> None:
|
|
nodes = [_node("textbox", "Card number"),
|
|
_node("textbox", "Expiry date"),
|
|
_node("textbox", "Security code (CVC)")]
|
|
verdict = detect_checkout("https://shop.example.com/buy",
|
|
nodes)
|
|
self.assertTrue(verdict.is_checkout)
|
|
self.assertEqual(verdict.confidence, "high")
|
|
self.assertEqual(verdict.form_risk, "payment")
|
|
self.assertIn("payment_fields", verdict.reasons)
|
|
|
|
def test_password_field_marks_form_risk(self) -> None:
|
|
nodes = [_node("textbox", "Password")]
|
|
verdict = detect_checkout("https://shop.example.com/login",
|
|
nodes)
|
|
self.assertFalse(verdict.is_checkout)
|
|
self.assertEqual(verdict.form_risk, "password")
|
|
|
|
def test_order_button_plus_amount_is_checkout(self) -> None:
|
|
nodes = [_node("button", "Place order"),
|
|
_node("text", "Total: $50.00")]
|
|
verdict = detect_checkout("https://shop.example.com/review",
|
|
nodes)
|
|
self.assertTrue(verdict.is_checkout)
|
|
self.assertEqual(verdict.confidence, "medium")
|
|
self.assertEqual(verdict.amount_text, "$50.00")
|
|
|
|
def test_order_button_alone_is_not_checkout(self) -> None:
|
|
nodes = [_node("button", "Place order")]
|
|
verdict = detect_checkout("https://shop.example.com/review",
|
|
nodes)
|
|
self.assertFalse(verdict.is_checkout)
|
|
|
|
def test_amount_only_is_not_checkout(self) -> None:
|
|
nodes = [_node("text", "Total: $50.00")]
|
|
verdict = detect_checkout("https://shop.example.com/review",
|
|
nodes)
|
|
self.assertFalse(verdict.is_checkout)
|
|
self.assertEqual(verdict.amount_text, "$50.00")
|
|
|
|
def test_chinese_payment_hints(self) -> None:
|
|
nodes = [_node("textbox", "卡号"),
|
|
_node("button", "立即支付")]
|
|
verdict = detect_checkout("https://shop.example.com/buy",
|
|
nodes)
|
|
self.assertTrue(verdict.is_checkout)
|
|
self.assertEqual(verdict.form_risk, "payment")
|
|
|
|
def test_checkout_url_plus_payment_fields_high(self) -> None:
|
|
nodes = [_node("textbox", "Card number"),
|
|
_node("button", "Pay now")]
|
|
verdict = detect_checkout("https://shop.example.com/checkout",
|
|
nodes)
|
|
self.assertTrue(verdict.is_checkout)
|
|
self.assertEqual(verdict.confidence, "high")
|
|
self.assertIn("checkout_url", verdict.reasons)
|
|
self.assertIn("payment_fields", verdict.reasons)
|
|
|
|
def test_yen_and_currency_code_amounts(self) -> None:
|
|
nodes = [_node("text", "合計:¥1,200"),
|
|
_node("text", "Total 25.00 USD")]
|
|
verdict = detect_checkout("https://shop.example.com/review",
|
|
nodes)
|
|
self.assertEqual(verdict.amount_text, "¥1,200")
|
|
|
|
def test_empty_nodes_plain_page(self) -> None:
|
|
verdict = detect_checkout("https://shop.example.com/", [])
|
|
self.assertFalse(verdict.is_checkout)
|
|
self.assertIsNone(verdict.form_risk)
|
|
self.assertIsNone(verdict.amount_text)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|