什么能模拟,什么不能?给我完整列表。
“否”行中的所有内容都是确实不存在,而不是未记录,所以请围绕它
规划:用针对录制载荷的单元测试来验证那些代码路径,
并在上线后在生产中验证真实行为。
哪些保留金额触发哪个错误,在哪些端点上?
沙箱会拦截特定的订单金额并返回匹配的错误, 而不是正常处理。映射如下:
请仔细阅读”适用于”列,因为错误就发生在这里:
- 在创建时,金额取自请求——订单创建时的订单金额、 订阅创建时的金额。
- 在取消时,你不传金额。匹配的金额是存储在
你正在取消的订单或订阅上的金额。所以要测试取消错误,你
必须先以
9.3创建订单,然后取消它。 - 这些金额是仅沙箱的规则。生产中不存在类似的东西, 在生产中这些金额就是普通金额。
为什么退款保留金额不起作用?
因为退款路径不使用退款专用的金额。已发布的 沙箱模拟器 页面为退款 API 列出的9.5 和 9.6 族不生效——995 的退款就是一笔普通退款。
退款路径会响应的是创建族:
refundAmount 等于 90 / 9.1 / 9.2 族中某个值(即在订单创建时
触发 C0005、C0001 和 E0001 的同一组金额)时,会使
退款请求在请求时以匹配类别的错误失败——90 和 9.1 族为
渠道或系统错误,9.2 族为未知状态错误。
为该请求打开的退款订单以 REFUND_FAILED 结束,
并向你的退款通知 URL 发送一条报告失败的退款通知。这让你能够验证
对被拒绝的退款请求和失败的退款记录的处理;它不能给你的是一笔
先进入处理中、之后再失败的退款,而且在沙箱中没有其他方式可以强制产生这种情况。
在退款路径上你仍然可以验证的:
对于真正的退款失败,请用录制的载荷测试你的处理程序,
而不是尝试触发一个。
我的真实价格正好落在保留金额上,沙箱订单一直失败
这是一个已知情况,并且有一个不是”改价格”的解决办法: 沙箱商户 ID 可以被免除金额模拟。 带上你的沙箱商户 ID 联系 集成支持并申请免除;一旦 应用,保留金额在你的沙箱中就表现为普通金额。 在此之前,变通方法是避开上述五个族。实际上这 意味着避开以90 结尾的金额,以及 9.x / x91–x96 模式——
加一分钱(例如用 90.01 代替 90)就够了,因为匹配
是精确的。
沙箱真的会发送Webhook 通知吗?
会。沙箱的Webhook 通知投递遵循与生产相同的契约——相同的 签名、相同的可接受响应体、相同的重试计划。 Webhook 通知投递契约 FAQ中的一切 都原样适用。 两件并不不同、却被归咎为看起来不同的事:- 你的
notifyUrl必须可公开访问。 沙箱无法访问localhost或私有地址,你的 TLS 证书必须是公开可信的。 本地开发请使用隧道工具。 - 感觉上的沙箱延迟通常是重试计划。 如果第一次投递 失败,后续尝试会按递增的退避进行——计划参见 Webhook 通知重试。 一条”花了几分钟才到”的通知几乎总是意味着最初的 尝试被拒绝了——在断定沙箱慢之前,先检查 Portal Webhook 通知日志中 同一记录上更早的失败尝试。
我应该用哪些测试卡号?
当流程要求你输入卡时,使用沙箱收银台页面上的快速填充按钮。 它们会一键填入卡号、有效期和 CVV,对应成功 或拒绝,按卡品牌,覆盖你的沙箱已启用的品牌 ——这既消除了抄写错误,也消除了对 该环境期望什么值的猜测。 如果你需要写下来的值,Portal 自己的列表在 Merchant Portal → Integration → Test Cards,每张卡有复制操作, 以及两个场景类别:successful 和 failed。 沙箱测试 FAQ中的表格与 Portal 列表一致。较早发布的模拟器页面并非每个卡组织都一致, 所以优先使用产品内的来源而不是任何文档,并优先使用收银台 快速填充而不是两者。有效期和 CVV 填什么?会校验吗?
任何未来的有效期,以及任何长度正确的 CVV——三位,使用四位的 卡组织则为四位。收银台页面上的快速填充会为你提供有效的 组合。 这对覆盖范围的启示是:因为有效期和 CVV 没有实质性的 校验,沙箱无法验证你对有效期拒绝或 CVV 不匹配的处理。这些属于”用录制载荷测试”的列表。Portal 测试卡页面只显示成功和失败。3DS、余额不足、CVC 错误和风控拒绝的卡在哪里?
它们不存在。Portal 测试卡列表只有两个场景类别, 成功和失败,没有任何卡号映射到特定的 拒绝原因。 对验收规划的影响:沙箱能证明你的代码在成功和失败时 走了正确的分支。它不能让你枚举拒绝 原因。当你的产品按拒绝原因有不同的行为时——余额不足时提示重试, 硬拒绝时提示”换一张卡”——请在单元测试中用orderFailedReason 驱动这些分支,
使用
错误码中的代码。
我如何模拟续订,能连续模拟多个周期吗?
从订阅管理页面,通过调用POST /api/v1/subscription/manage 并打开返回的 managementUrl 获得。它
提供模拟下一次支付成功和模拟下一次支付失败,
每次点击推进一个周期——所以你可以串联:第 2 期成功,
第 3 期失败。
前提条件,因为这些正是让按钮不可用的原因:
如果页面渲染了但模拟控件不见了,请先检查
订阅的状态——未激活或已取消的订阅是
最常见的原因。
模拟的失败周期之后会按正常计划重试吗?
不会——这是本页最重要的一点。 模拟失败的 续订会先把续订重试额度设为零,所以失败的周期 直接进入终态,而不是进入重试计划。 需要围绕它规划的两个后果:- 你无法通过模拟失败在沙箱中观察续订重试行为。 生产中运行的重试计划被模拟本身 绕过了。
- 该更改会持久保留在该沙箱订阅的配置上。 之后续订 重试保持禁用,所以一个你用来模拟失败的订阅 不再是重试测试的干净对象。任何与重试相关的测试请创建一个新的 订阅。
我能模拟顾客在钱包 App 内取消吗?
可以,针对支持它的支付方式。模拟会产生 与真实顾客侧取消相同的带USER_CANCELLED 的
SUBSCRIPTION_STATUS_NOTIFICATION,所以你的处理程序看到的是真实的东西。
两道门槛决定它对某个订阅是否可用:
- 订阅必须是
ACTIVE。 - 支付方式必须支持它。 钱包侧取消是通过 复现该支付方式在取消时实际发送的消息来模拟的, 所以它只存在于该消息已建模的方式上。 未建模时,调用会以专门的”this payment method does not support channel cancel”错误失败,而不是静默地什么都不做——这 本身就回答了你的方式是否被覆盖:试一下并读取 错误。
USER_CANCELLED 载荷测试处理程序。通知的形态不因支付方式而异。
我能在沙箱中测试拒付吗?
不能。沙箱中没有拒付或争议模拟——没有保留 金额、没有按钮、没有端点。 替代做法:
在围绕它规划之前还值得了解:拒付通知只有在 Portal 中配置了
Chargeback Notify URL 时才会投递,而且它
只能在 Portal 配置——没有请求参数。所以首先要
验证的是该 URL 在两个环境中都已设置。
订单卡在未知状态。我如何把它推进到最终结果?
E0001 在创建时由保留金额返回,请求在
任何订单创建之前就被拒绝——对该请求 ID 的查询
返回”order does not exist”。这正是重点:未知状态响应
不能告诉你订单是否存在,所以要验证的行为是
你的恢复逻辑——把结果视为未确定,查询,并根据
查询返回的内容行动;绝不要自己把订单标记为失败或关闭它。当
查询确认不存在任何订单时,用完全相同的参数重新提交(你的
paymentRequestId 使这样做是安全的)。
沙箱中没有控制手段能产生一个先停留在
未知状态、之后再翻转为最终结果的订单。所以要展示的行为
是查询-然后-决定的循环以及”没有自行关闭”这一属性,而不是
一个你手工产生的最终状态。验收标准对此的表述是:不要
关闭订单,用相同参数重试,并等待查询结果
或通知。参见
集成验收标准。
我需要在特定国家的网络出口才能测试吗?
沙箱不需要。沙箱支付路径中没有任何东西检查你的 请求或浏览器来自哪里,而且由于沙箱不连接 真实的支付渠道,也没有渠道侧的地理限制需要 满足。从任何地方运行的沙箱测试都是有效的沙箱测试。 地理确实重要的地方是对真正 依赖顾客所在地区的方式做生产验证——移动钱包产品的 设备或账户地区设置,以及这些产品公布的支持国家列表。 这些检查列在 沙箱与测试的 各支付方式章节下,它们是生产检查,不是沙箱检查。哪些事情只能在生产中验证?
把这份列表放在你的上线计划旁边,因为每一项都是 沙箱跑绿了也什么都证明不了的地方:
移动钱包的检查尤其有自己的前提条件列表——
账户、卡、设备地区、App 内浏览器配置,以及必须在发布前
获批的生产访问申请。请按
沙箱与测试和
支付方式集成说明
逐项处理,而不是在切换时才发现它们。
我必须逐个测试每个已签约的支付方式吗?
不必。验收标准规定的是代表性覆盖而不是 穷尽覆盖:至少一张卡、每个国家每种类型至少一个、至少 一个虚拟账户、每个有特殊必填参数的方式,以及 移动钱包的手动设备测试。完整规则集在 集成验收标准。 两个经常出现的追问:- 你的协议不允许退款的方式不需要退款 用例。只对你的集成实际包含的 API 和行为运行条件 用例——模板正是出于这个原因把它们标记为条件项。
- 第二次集成不是完整重跑。 一旦你至少通过过一次验收, 后续的一次性支付集成使用附加 集成模板,只覆盖增量部分。你的第一次集成 始终使用标准模板。
下一步:上线清单用于 切换本身,以及 沙箱环境与访问 FAQ 了解哪些内容会、哪些不会从沙箱带过去。