--- url: >- https://acquirer-api-docs-v4-en.pingpongx.com/notes/zh/onlinePayment/bestPractices/rescue/index.md description: 说明可重试支付的交易挽回策略,覆盖 Hosted 与 Non-hosted 场景下的重试窗口、requestId 使用、重复支付防护和订单状态处理。 --- 交易挽回用于处理买家支付未完成、支付失败但仍可能重新支付、网络异常导致结果暂不明确等场景。商户可以在订单未成功前,引导买家继续支付或重新发起支付请求,以提升支付成功率。 本文关注“如何安全地重新推进支付”。各支付状态的含义、失败终态/失败非终态差异,以及订单幂等规则,请参考[交易状态与幂等](/notes/zh/onlinePayment/bestPractices/paystatus/);如何通过通知和查询确认最终结果,请参考[如何获取订单状态](/notes/zh/onlinePayment/bestPractices/fetchStatus/)。 ## 适用场景 以下场景通常可以考虑交易挽回: | 场景 | 是否建议挽回 | 处理建议 | |:---|:---:|:---| | 买家支付过程中主动放弃、关闭页面或未完成钱包/银行页面操作 | 是 | 保持订单为待支付或支付中,在支付有效期内引导买家继续支付。 | | 支付返回 `FAILED`,且业务采用失败非终态模式 | 是 | 可引导买家更换支付方式或重新发起支付请求。 | | 支付返回 `INIT` 或 `PROCESSING` | 谨慎 | 不应立即创建新支付,应先等待异步通知或调用交易查询确认结果。 | | 支付已返回 `SUCCESS` 或 `AUTH_SUCCESS` | 否 | 不再重新支付;`AUTH_SUCCESS` 场景应按业务需要发起 Capture 或 Void。 | | 支付已返回 `CANCEL` 或 `CLOSED` | 通常否 | 该笔支付不可继续推进;如买家仍需购买,应重新创建订单或新支付请求。 | | 失败终态模式下返回 `FAILED` | 否 | 该笔交易不可继续推进为成功;如需继续购买,应重新创建支付请求或订单。 | ::: warning 注意 在订单最终状态不明确时,不要并发创建多笔支付请求。并发支付可能导致重复扣款,后续需要人工核对并发起退款。 ::: ## 重试前确认 发起交易挽回前,商户服务端建议先完成以下检查: 1. 查询商户订单当前状态,确认订单尚未支付成功、未发货、未履约。 2. 通过[支付通知](/notes/zh/notify/payment/notify/)或[交易查询](/notes/zh/checkout/api/getOne/)确认 PingPong 侧交易状态。 3. 检查原支付会话或原支付请求是否仍在有效期内。 4. 确认本次重试是否会产生新的支付请求,并做好 `merchantTransactionId`、`requestId`、`transactionId` 的关联记录。 5. 在商户系统内对同一订单加锁或做并发控制,避免多个终端、多个浏览器窗口同时发起支付。 ## Hosted 模式挽回 Hosted 模式包括跳转收银台、内嵌 SDK 等由 PingPong 收银台承接买家支付体验的接入方式。商户服务端创建支付会话后,前端通常使用 `paymentUrl` 或 SDK 渲染能力让买家继续完成支付。 | 买家状态 | 商户处理建议 | |:---|:---| | 买家未打开收银台 | 可继续使用当前有效的 `paymentUrl` 或支付会话引导买家支付。 | | 买家打开收银台但未完成支付 | 在支付会话仍有效时,可引导买家回到原收银台继续支付或重新选择支付方式。 | | 收银台返回失败但订单未进入终态 | 先查询交易状态,再决定是否继续引导买家支付。 | | 订单已关闭或支付会话过期 | 不再复用原支付会话;如买家仍需购买,应重新创建订单或新支付会话。 | Hosted 模式下,前端页面展示的支付失败、取消或关闭不应直接作为订单最终状态。商户服务端仍需以异步通知或交易查询返回的 `status` 为准。 ## Non-hosted 模式挽回 Non-hosted 模式下,商户服务端直接调用[下单并支付](/notes/zh/checkout/api/uniformly/)创建支付请求,并根据同步响应中的 `status` 或 `action` 推进后续流程。 `requestId` 是 Non-hosted 模式下控制支付请求幂等的重要字段: | 目标 | `requestId` 使用建议 | 说明 | |:---|:---|:---| | 重试同一次支付请求 | 复用原 `requestId` | 相同 `merchantTransactionId + requestId` 会按幂等规则返回同一支付请求结果,适合网络超时、响应未收到等场景。 | | 让买家重新发起一笔新的支付请求 | 使用新的 `requestId` | 适合买家更换支付方式、原请求失败且允许重新支付的场景。 | | 避免同一订单产生多笔支付请求 | 固定复用同一 `requestId` | 商户不希望同一订单有多次支付尝试时,可始终复用同一 `requestId`。 | | 需要多次支付尝试并统计成功率 | 每次尝试使用新的 `requestId` | 商户侧需保存每次尝试记录,并将其关联到同一个 `merchantTransactionId`。 | ::: warning 重复支付风险 如果同一笔商户订单在未支付成功前,使用不同 `requestId` 并发发起多笔支付请求,或者在 Hosted 模式下同时打开多个可支付入口,可能出现重复支付。商户应通过订单锁、前端按钮防重复点击、服务端状态校验等方式限制并发。 ::: ## 状态处理建议 交易挽回过程中,商户可按以下方式处理订单状态: | PingPong 状态 | 是否继续挽回 | 商户订单处理 | |:---|:---:|:---| | `INIT` | 是 | 保持待支付,继续等待买家支付或稍后查询。 | | `PROCESSING` | 暂不新建支付 | 保持支付中,等待异步通知或定时查询,不建议立即创建新支付请求。 | | `FAILED` | 取决于幂等模式 | 失败非终态可引导继续支付;失败终态应结束该笔请求并重新创建支付请求或订单。 | | `SUCCESS` | 否 | 标记支付成功,并按业务规则履约;重复通知或重复查询需幂等忽略。 | | `AUTH_SUCCESS` | 否 | 标记预授权成功,后续发起 Capture 或 Void,不应重新支付。 | | `CANCEL` | 否 | 标记取消或失败,不继续履约;如需继续购买,重新创建订单或支付请求。 | | `CLOSED` | 否 | 标记订单关闭,原支付入口不可继续使用。 | ## 推荐实现 建议商户在服务端为每笔订单维护支付尝试记录,至少保存以下信息: | 字段 | 说明 | |:---|:---| | `merchantTransactionId` | 商户订单号,用于关联同一业务订单。 | | `requestId` | 本次支付请求号,Non-hosted 模式下用于请求级幂等。 | | `transactionId` | PingPong 返回的交易号,用于交易查询、对账和售后操作。 | | `status` | 当前支付请求状态。 | | `paymentMethod` | 买家本次选择的支付方式。 | | `createdTime` / `updatedTime` | 支付尝试创建和最后更新时间。 | 实现时建议: 1. 商户订单进入支付流程前,先判断是否已有 `SUCCESS` 或 `AUTH_SUCCESS` 结果。 2. 创建支付请求前,对商户订单加锁,避免并发创建多个支付请求。 3. 同一 `requestId` 的重试应视为同一次支付请求,不重复创建支付尝试记录。 4. 新 `requestId` 应记录为新的支付尝试,并与原商户订单关联。 5. 异步通知和查询结果都应幂等更新,不能用较早的中间态覆盖已落库的终态。 ## 重新创建订单号 如果原订单已经关闭、失败终态结束,或不在可重试窗口内,建议创建新的商户订单或在原订单号后追加后缀发起新交易。例如: ```text PMT-202607230001-R1 PMT-202607230001-R2 ``` 这样既能避免与原交易的幂等规则冲突,也便于在对账、成功率分析和客服排查时将多次支付尝试关联回同一笔业务订单。 ## 排查清单 如果交易挽回后仍无法支付成功,可按以下方向排查: | 问题 | 排查方向 | |:---|:---| | 买家重复扣款 | 检查是否并发创建多个支付请求、是否使用了不同 `requestId`、是否在终态前重复开放支付入口。 | | 买家重新支付后仍失败 | 检查支付方式能力、风控审核结果、支付方式页面返回原因,以及是否属于失败终态。 | | 前端显示失败但后台成功 | 以前端结果页为辅助信息,最终以异步通知或交易查询的 `status` 为准。 | | 长时间 `PROCESSING` | 继续等待异步通知,并按业务策略定时调用交易查询;不要立即创建新支付。 | | 订单号无法再次下单 | 检查 `merchantTransactionId` 是否已进入终态或仍受幂等规则约束,必要时使用新的订单号或后缀。 |