1. 支付与订单集成全景

在 Android 应用里做支付与订单集成,真正困难的部分往往不是“调起一个收银台”,而是把支付渠道、订单状态、服务端回调、客户端 UI 与异常补偿串成一条可验证、可恢复、可观测的链路。用户看到的是“点一下付款”,工程上却涉及多个系统之间的状态同步:客户端发起下单,服务端创建订单并签名,SDK 调起支付,渠道异步回调,服务端更新订单,客户端再查询最终结果。任何一环缺失或乱序,都会表现为“钱扣了但订单没到账”“订单显示待支付但实际已成功”“重复点击生成两笔订单”等生产问题。

一、Android 支付场景的真实构成

Android 端常见的支付场景可以按“渠道形态”和“交互路径”两个维度理解。渠道形态包括:微信支付、支付宝、银联/云闪付、Google Play 结算、以及部分业务自有的余额或积分支付。交互路径则分为三类:

  • App 内调起:客户端集成渠道 SDK,通过服务端下发的预支付参数调起渠道 App 或渠道内嵌收银台,例如微信 PayReq、支付宝 PayTask.payV2()。
  • H5 / 网页支付:在 WebView 中打开收银台,支付完成后通过 return_url 或前端桥接回传结果,客户端不能仅凭前端跳转判断成功。
  • 系统级结算:Google Play 等由系统托管,客户端通过 BillingClient 查询购买状态,服务端通过 Real-time Developer Notifications 或轮询校验。

无论哪种路径,都必须明确一个原则:客户端拿到的支付结果只是“参考信号”,订单的最终状态以服务端与渠道对账后的结果为准。客户端负责发起、展示和引导,服务端负责签名、验签、幂等和状态机推进。

二、订单生命周期:从创建到终态

订单生命周期是本节最值得用图表达的核心对象。一个典型订单会经历:待支付(CREATED)→ 支付中(PAYING)→ 已支付(PAID)→ 已发货/已履约(FULFILLED),并可能进入 已关闭(CLOSED)、已退款(REFUNDED) 或 支付失败(FAILED)。关键点在于:

  • 创建即落库:客户端点击“立即购买”时,服务端先创建订单并返回 orderId,再基于该订单生成支付单,避免“先支付后建单”导致对账困难。
  • 支付单与订单分离:一个订单可能对应多次支付尝试(用户取消后重试、换渠道),因此支付单需要独立的 payId 与状态。
  • 状态只能单向推进:已支付不能回退为待支付;退款是新增状态而非覆盖原状态,保留完整流水。
  • 超时关闭:待支付订单通常设置 15~30 分钟有效期,由服务端定时任务或延迟消息关闭,客户端倒计时只是展示。

下面这张状态关系图把订单、支付单、渠道回调与客户端查询放在同一视图中,帮助理解“谁在什么时机改变哪个状态”。

订单与支付单状态流转(客户端 / 服务端 / 渠道) 客户端(Android) 服务端(订单/支付) 支付渠道 点击购买 创建订单请求 调起渠道 SDK PayReq / payV2 展示支付结果 查询订单终态 倒计时/重试 超时关闭提示 订单 CREATED 生成 orderId 支付单 PAYING 签名+预支付参数 验签+幂等更新 订单 PAID 履约/发货 FULFILLED 渠道收银台 用户确认支付 异步回调 notify 可能重复/延迟 主动查单 补偿回调丢失 终态: PAID / FULFILLED CLOSED(超时未支付) FAILED(渠道失败) REFUNDED(退款) 客户端展示以服务端查询结果为准;渠道回调必须验签、幂等;回调丢失时由主动查单补偿。

三、核心挑战:为什么支付集成容易出问题

支付链路的稳定性挑战集中在四个方面。第一是状态不一致:客户端收到“支付成功”回调,但服务端尚未收到渠道通知,此时若客户端直接跳转成功页,用户可能看到订单仍为待支付。正确做法是客户端在收到 SDK 结果后,轮询或长连接查询服务端订单状态,以服务端为准。第二是重复与幂等:用户快速点击、网络重试、渠道重复回调都会产生重复请求,服务端必须以 orderId 或 outTradeNo 作为幂等键,保证同一笔支付只推进一次状态。第三是回调丢失与延迟:渠道异步通知可能因网络、服务重启而丢失,需要定时对账任务主动查单。第四是多端一致性:用户在 Android 手机发起支付,又在平板或 H5 查看订单,状态必须来自同一服务端数据源。

常见误区:把渠道 SDK 的同步返回值当作支付成功的唯一依据。微信 onResp 返回 0 只代表“支付请求已受理”,并不等于资金已到账;支付宝同步结果也可能被伪造。必须以服务端验签后的异步通知或主动查单结果为准。

四、稳定性目标与可观测指标

稳定性目标需要可量化。建议围绕以下指标建立监控:支付成功率(成功订单数 / 发起支付订单数)、回调到达率(收到渠道通知的支付单占比)、对账差异率(渠道账单与服务端订单不一致的比例)、端到端耗时(从点击购买到订单变为 PAID 的 P95)、以及重复支付拦截次数。示例目标可设为:支付成功率 ≥ 99.5%,回调到达率 ≥ 99.9%,对账差异率 ≤ 0.01%,端到端 P95 ≤ 8 秒。这些数值属于合理假设,实际项目应结合渠道与业务量调整。

在实现层面,服务端应记录完整的支付流水:orderId、payId、channel、outTradeNo、channelTradeNo、amount、status、createdAt、paidAt、notifyRaw。客户端则需在关键节点埋点:下单请求、调起 SDK、SDK 返回、查询订单、展示结果,便于定位“卡在哪一步”。

实践建议:把“支付结果查询”设计成独立接口,支持按 orderId 查询订单终态,并允许客户端在支付后以 1s、2s、3s、5s 的退避间隔轮询,最多 5 次;超过后提示“支付结果确认中”,由服务端对账后通过推送或下次进入订单页时刷新。

五、本节小结与后续衔接