有没有不在已发布列表里的状态?
有两个,都值得了解,因为它们解释的是令人困惑的现象,而不是需要你操作的东西。
第二个是商户经常问的问题的答案,值得直白说明:
AUTHORIZATION_REQUIRED 同时涵盖*“支付方式正在等待用户授权”和”这个订单上还什么都没发生”*。仅凭状态无法区分二者。
我的订单卡在 AUTHORIZATION_REQUIRED,是用户关了 WebView 吗?
可能——但状态不会告诉你这一点,而且这不是唯一的解释。AUTHORIZATION_REQUIRED 意味着没有结果返回,与以下所有情况都吻合:
- 用户根本没打开支付页面。
- 用户打开后放弃了——关闭了 WebView、杀掉了应用、走开了。
- 用户还在页面上,认证进行中。
- 支付尝试尚未发起。
哪些状态是终态,我能把它存为业务结果吗?
两个状态是终态:PAY_SUCCESS 和 ORDER_CLOSE。其他都是过渡态,处于过渡态的订单仍可能变化。
PAY_SUCCESS 和 ORDER_CLOSE 不会互相翻转。订单一旦关闭,支付方式之后传来的成功报告不会重新打开它,也不会把它改写为 PAY_SUCCESS;一旦成功,就保持成功。所以是的——你可以把终态持久化为你的业务结果。
唯一不安全的持久化假设是:终态意味着资金不会再有任何变化。成功的订单之后仍可能被退款或发生争议,而这两者都不改变 orderStatus——见下文的退款问题。
订单在每个状态下我能做什么?
仔细读这两个边界,因为集成往往在这里出错:
- **只有
PAY_SUCCESS才能退款。**不存在”退款一个待支付订单”——如果钱还没到,你要的操作是取消。 - 取消是释放你不打算请款的授权的方式,而且它实质上优于先授权、请款再退款。
我调用了取消但被拒绝,这些错误是什么意思?
各自如何处理:
- **
A0013不是可以重试的错误。**用户已付款。如果你不想履约,操作是退款,不是取消。 A0015是关于支付方式的事实,不是关于你的请求。有些方式根本没有取消操作;对于这些方式,未支付订单的结束方式是过期。请为”取消可能不可用”做设计,让过期机制来完成。- **
A0016来自支付方式一侧。**重试一次或许值得,但如果重复出现,请停止并让订单过期。 - 取消一个已关闭的订单会成功且什么都不做。这使取消可以安全地从清理任务中调用,无需先检查状态。
paymentRequestId 或 acquiringOrderId 来标识订单,加上你的 merchantId;两个 id 都不发是校验失败。
取消之后,用户还能在支付方式的页面上付款吗?
取消把订单置为ORDER_CLOSE——一个终态——并请求支付方式也取消其一侧。从那一刻起订单无法完成:之后到达的成功报告不会被应用,所以订单不会在你背后悄悄变成已支付订单。
取消无法保证的是已经打开在用户面前的第三方支付页面停止接受输入。在支付方式完全不支持取消的情况下(A0015),从定义上讲他们那边什么都没被取消。所以对”用户还能付款吗”的诚实回答是:*他们可能仍能交出钱,而它不会成为已完成的订单。*在那个窗口内流动的资金是回收场景,不是履约场景——处理方式见超时与结果何时终态。
实用规则:在你决定不卖时取消,不要把取消当成关闭用户仍在查看的收银台的手段。
“设一个短过期时间,到期后取消”是好的设计吗?
是合理的设计,但有一处修正和一处提醒。- **修正:**你不需要取消调用。到达过期时间仍未支付的订单会替你关闭,并通知你。之后加上你自己的取消无害——对已关闭订单是空操作——但它不是让订单结束的原因。
- **提醒:**短过期时间不会缩短支付方式自己的窗口,也不会阻止他们那边收取迟到的付款。所以三分钟的过期时间给你的是三分钟的收银台,而不是三分钟后不会有资金流动的保证。
订单会精确在我设置的过期时间关闭吗?
接近,但不精确到秒。关闭由周期性扫描执行,因此订单在名义过期时间之后可能仍在技术上短暂保持打开,高并发下一批到期订单可能溢出到下一轮。关闭之前会检查支付方式,已有支付进行中的订单会被放过,而不是在支付底下被关闭。 这对你意味着:- **不要把”订单已关闭”当时钟用。**如果你的履约逻辑需要截止时间,用你自己的计时器对照你发送的过期时间;用订单状态了解结果,而不是时间。
- **不要把略微迟到的关闭当作事故。**这就是设计。
- **过去的过期时间不会被拒绝——而是被丢弃。**如果创建订单时
orderExpiredAt已经落后于时钟,该值会被丢弃,订单改用支付方式的默认过期时间。所以你这边的时钟 bug 不会大声失败;它会产生一个比你预期保持打开更久的订单。发送前请检查时间戳。
orderExpiredAt,订单最终仍会被关闭,但该时间不是你设置的值,也不应依赖它。只要窗口对你的业务重要,就发送明确的过期时间。
订单因超时关闭时我会收到通知吗?
会。关闭的订单产生普通的支付通知——PAYMENT_NOTIFICATION,携带与 Order Inquiry 相同的订单结构,orderStatus = ORDER_CLOSE,原因在 orderFailedReason 中。没有单独的”订单已过期”事件类型可订阅,也没有额外需要启用的东西。
所以一个处理 PAYMENT_NOTIFICATION 并按 orderStatus 分支的集成已经处理了超时关闭。如果你没有收到,那是 Webhook 投递问题——见 Portal Webhook 日志和 Webhook 重试。
我能在错误码表里查 orderFailedReason.orderFailedCode 吗?
不能——那是另一套码。Axxxx 码是你的请求有误或被拒绝时 API 调用同步返回给你的。orderFailedReason 里的码描述的是订单为什么以那种方式结束,是一个独立的、面向原因的命名空间。在一方的表里查另一方的码,正是商户得出”该码不存在”结论的原因。
能基于它做自动化吗?部分可以,且要谨慎:
- 要基于你可以依赖的粗粒度区分做分支——超时关闭与支付拒绝是不同的业务事件,二者又都与取消不同。
- 要始终把
orderFailedDescription与码一起存储,因为当一个你从未见过的码到来时,它是仍然可读的部分。 - 不要构建把未识别原因视为你自己代码失败的穷举 switch。这个集合比你测试时见到的那几个大,而且随着支付方式变化会出现新原因。默认处理为”已关闭、未支付、原因已记录”,并提醒人工处理而不是猜测。
我能请款少于授权金额吗?
可以。请款金额可以是大于零且不超过授权金额的任意值;授权的剩余部分会被释放而不是继续冻结。请款超过授权金额会被拒绝。
最后一行是让人意外的那一个。请款只存在于以手动请款模式创建的订单——你在创建订单时通过
paymentInfo.captureMode 选择加入,那里唯一接受的值是 manualCapture。普通订单会自动请款,你无需调用任何东西。
已发布的产品行为与代码在最关键的一点上一致:请款实际上是单次的——一旦请款,即便是部分请款,授权的其余部分就会释放,因此你无法分期提取一笔授权。对于按用量计费,模式是存储 token 加周期性扣款,而不是重复请款。见 Auth-Capture 和 Tokenization。
我有多长时间可以请款?
窗口从订单创建时起按整天计算,而不是从你注意到授权时。它是单一的平台级设置,对每个商户一视同仁——不按账户协商,拒绝消息的措辞也不是当前数值的可靠指引。在围绕它设计履约流程之前,请向支持确认该窗口。 窗口过后会发生两件事,你应该为两者都做好准备:- **你的请款调用开始失败。**它以不支持被拒绝,而不是排队。
- **授权会替你释放。**过期的授权会被主动取消,因此客户的资金不会因为你忘了而一直被冻结。
为什么我有些订单上的 refundExpiryAt 是空的?
refundExpiryAt 是订单不再能退款的时点。它由你协议中该支付方式的退款窗口推导而来,从订单的创建日期起算——不是从支付成功那一刻——并落在那一天的结束。
两个后果:
- 以天表示的窗口是日历窗口。当天晚些时候创建的订单,可用窗口比早些时候创建的略短,因为边界无论如何都是一天的结束。
- 时钟不会在支付成功时重新开始。一个在创建后很久才支付的订单,已经用掉了部分退款窗口。
null:
所以不要把缺失的
refundExpiryAt 读成”永远可退”。把它读成”该订单没有公布截止时间”,查看按支付方式的退款限制了解适用规则,并用退款 API 上的 remainingRefundAmount 决定还有多少可退。
退款会改变原订单的状态吗?
不会——这是这个接口上最常见的误报。退款永远不会改写orderStatus;全额退款的订单仍然报告 PAY_SUCCESS。退款进度在退款记录和订单单独的退款状态字段上,而不是支付状态上。
如果你的账务用 orderStatus 作为退款状态的键,它会看起来永远卡住。请改用退款记录作为键——按支付方式的退款限制列出了每个问题该看哪里。
卡在一个不动的状态上?请联系支持,附上
acquiringOrderId 和你看到的状态。