该技能面向需要在测试环境中端到端验证支付和结账流程的团队,默认以 Stripe 为主,同时给出 Adyen、Braintree、PayPal 的通用移植思路。
【用途】 覆盖从一次性支付到订阅续费、3DS/SCA 挑战、Webhook 投递与对账的完整支付测试链路,帮助避免使用真实卡、误信客户端成功标志、错误处理订阅失败等常见问题。
【主要能力】
- 测试卡与结果:提供成功、拒付(card_declined/generic_decline)、余额不足(insufficient_funds)、3DS 总是挑战、附加成功但扣款失败等确定性卡号,例如 4242424242424242、4000000000000002、4000000000009995、4000000000003220、4000000000000341。强调使用 pk_test_/sk_test_ 测试密钥。
- 3DS/SCA:说明 Stripe 挑战框嵌套于 Stripe 模态框内的结构,需链式 frameLocator(外层 → 内层),然后点击 Complete authentication。指出 page.locator('#card')、page.frames()[1]、waitForTimeout 等常见错误做法。
- 测试时钟:用 Stripe 服务端 test clock 模拟未来续费,创建时以 frozen_time 冻结并绑定客户,advance 只能向前且单次最多推进两个计费周期。指出客户端假时钟(jest.useFakeTimers、sinon、mock Date)无法影响 Stripe 计费引擎。
- Webhook:本地用 stripe listen --forward-to localhost:3000/webhooks 隧道投递,stripe trigger 触发事件;whsec_… 为签名密钥,与 sk_test_… 不同。Webhook 路由需在全局 express.json() 之前挂载 express.raw 以保留原始请求体,使用 constructEvent 验签,失败返回 400。幂等需以 event.id 加 UNIQUE 约束持久化去重,内存 Set 无效。
- 失败支付与退款:以 4000000000000341(SDK token pm_card_chargeCustomerFail)订阅后在续费时失败,产生 invoice.payment_failed 并使订阅进入 past_due,再通过 refunds.create 退款解决,而不是删除订阅。
- 对账:只有收到签名验证通过的 payment_intent.succeeded Webhook 并用 paymentIntents.retrieve 复核后,才标记订单已支付;不得依赖 return_url 重定向或客户端成功标志,也不要用 sleep 轮询。
- 多 PSP:Adyen、PayPal、Braintree 各有沙箱卡与沙箱买家账户,不能复用 Stripe 卡号或生产密钥;Adyen 部分拒付由金额(.13、.51)驱动,事件为 HMAC 签名通知;PayPal 用沙箱买家账户登录并通过 Orders API/Webhook 服务端确认;Braintree 金额决定交易结果、卡号决定验证。
【使用方式】 先检查项目根目录下的 .agents/qa-project-context.md,跳过其中已说明的 PSP、技术栈、测试框架和已有夹具信息;随后明确:使用哪个 PSP 及是否为 Stripe、一次性支付还是订阅或两者兼有、是否涉及 SCA/3DS、当前是在 Webhook 还是在重定向时履约、Webhook 处理器在测试中运行于本地还是部署的预览环境。按上述原则编写 Playwright 测试,并通过验证步骤自检。
【适用场景】 结账流程回归测试、订阅续费与失败续费测试、3DS/SCA 挑战自动化、Webhook 签名与幂等验证、支付对账测试,以及向多 PSP 移植测试结构。
【限制】 默认以 Stripe 为主,其他 PSP 仅给出通用模式;价格、收费、具体平台支持等资料未提供。不得使用真实卡或实时密钥,任何环境下真实 PAN 都会违反 Stripe 服务协议并引入 PCI 范围;禁止用 4111111111111111 这类通用 Luhn 号作为 Stripe 测试卡。资料未提供官方文档链接、演示地址或性能数据。