2. 支付渠道接入基础

在 Android 端做支付,第一步不是写“下单”按钮,而是把渠道 SDK 的接入边界划清楚。支付宝、微信支付、银联三个渠道虽然都叫“支付 SDK”,但它们在 Android 侧的职责并不相同:支付宝 SDK 主要负责把服务端生成的订单串交给支付宝客户端完成收银,并把同步结果回调给 App;微信支付 SDK 主要负责向微信客户端发起支付请求,并接收微信回传的支付结果;银联 SDK 则更接近“收银台控件”,它既可能承载银行卡支付,也可能承载云闪付、手机 Pay 等能力,且通常需要额外的安全控件与混淆规则。理解这一点,才能避免把“渠道 SDK 调用成功”误判为“订单已支付成功”。

一、三个渠道在 Android 侧的接入形态

支付宝的典型接入方式是:服务端调用支付宝开放平台的下单接口,拿到一个 orderInfo 字符串;Android 端只负责把这个字符串原样交给 PayTask.payV2(),然后解析返回的 Map<String, String>。这个 Map 里最关键的是 resultStatus 和 result,其中 resultStatus=9000 表示支付成功,6001 表示用户中途取消,8000/4000 等则代表正在处理或失败。注意,9000 只是支付宝客户端给出的同步结果,最终订单状态仍应以服务端异步通知为准。

微信支付的接入方式略有不同。App 端需要先由服务端调用统一下单接口,拿到 prepay_id,再由服务端二次签名生成 appId、partnerId、prepayId、package、nonceStr、timeStamp、sign 这组参数。Android 端通过 IWXAPI.sendReq() 把 PayReq 发给微信客户端。支付完成后,微信会回调到你在 WXPayEntryActivity 中实现的 onResp(),其中 errCode=0 表示成功,-1 表示错误,-2 表示用户取消。这里最容易踩的坑是 WXPayEntryActivity 的包名和路径必须与微信开放平台登记的一致,否则回调不会进入你的代码。

银联的接入更偏“控件化”。以银联手机支付控件为例,通常需要在布局中放置 UPPayAssistEx 或通过 UPPayAssistEx.startPay() 启动支付流程,并传入 tn(交易流水号)和模式参数 mode。银联返回的结果往往通过 onActivityResult 回传,需要解析 result_data 并判断 pay_result。银联还涉及 assets 下的配置文件、jniLibs 下的 so 库,以及较严格的混淆 keep 规则,接入成本通常高于前两者。

三大支付渠道 Android 端接入链路对比 服务端 统一下单 / 签名 生成订单参数 支付宝 SDK PayTask.payV2(orderInfo) 返回 resultStatus 微信支付 SDK IWXAPI.sendReq(PayReq) WXPayEntryActivity 回调 银联 SDK UPPayAssistEx.startPay onActivityResult 回传 支付宝客户端 收银台 / 扫码 / 密码 微信客户端 支付面板 / 指纹 / 密码 银联收银台 银行卡 / 云闪付 同步结果 9000 / 6001 / 8000 onResp 回调 errCode 0 / -1 / -2 pay_result success / fail / cancel 最终订单状态:以服务端异步通知 / 主动查询为准 客户端同步结果仅用于 UI 引导,不可作为发货依据

二、依赖引入与工程配置

三个渠道的依赖引入方式差异明显。支付宝通常以本地 aar 或 Maven 坐标引入,例如在 app/build.gradle 中声明 implementation files('libs/alipaysdk.aar'),并在 AndroidManifest.xml 中声明支付宝所需的 H5PayActivity 等组件。微信支付需要引入 com.tencent.mm.opensdk:wechat-sdk-android,并在 build.gradle 中配置 applicationId 与微信开放平台登记包名一致。银联则通常需要把 UPPayAssistEx.jar、UPPayPluginExPro.jar 以及对应 so 库放入工程,并在 AndroidManifest.xml 中注册银联的 Activity 与权限。

一个常见的工程配置片段如下,用于统一管理三个渠道的依赖与混淆规则:

android {
    defaultConfig {
        applicationId "com.example.paydemo"
        minSdkVersion 21
        targetSdkVersion 34
    }
    packagingOptions {
        // 银联 so 库较多,避免重复打包
        pickFirst 'lib/arm64-v8a/libuptsms.so'
        pickFirst 'lib/armeabi-v7a/libuptsms.so'
    }
}

dependencies {
    // 支付宝
    implementation files('libs/alipaysdk.aar')
    // 微信支付
    implementation 'com.tencent.mm.opensdk:wechat-sdk-android:6.8.24'
    // 银联
    implementation files('libs/UPPayAssistEx.jar')
    implementation files('libs/UPPayPluginExPro.jar')
}

// 混淆规则
-keep class com.alipay.** { *; }
-keep class com.tencent.mm.opensdk.** { *; }
-keep class com.unionpay.** { *; }
-keep class org.simalliance.openmobileapi.** { *; }

这里要特别注意:微信 SDK 的 WXPayEntryActivity 必须放在 你的包名.wxapi 目录下,且类名固定为 WXPayEntryActivity,否则微信客户端无法找到回调入口。银联的混淆规则如果漏写,常见表现是支付控件启动后闪退或返回 pay_result=fail,但日志里看不到明显异常,排查成本很高。

三、初始化与调用时序

支付宝 SDK 不需要显式初始化,直接调用 PayTask 即可,但建议在 Application 中提前做一次 PayTask 的预热,减少首次支付时的类加载耗时。微信支付需要在 Application 中注册 IWXAPI:

public class PayApplication extends Application {
    public static IWXAPI wxApi;

    @Override
    public void onCreate() {
        super.onCreate();
        wxApi = WXAPIFactory.createWXAPI(this, "你的AppID", true);
        wxApi.registerApp("你的AppID");
    }
}

银联的初始化通常发生在调用 startPay 之前,需要确保 assets 下的 UPPayPluginExPro 配置文件和 so 库已正确加载。如果使用云闪付模式,还需要额外判断设备是否安装了银联可受理的 App。

四、结果判断与常见异常

三个渠道的结果判断逻辑可以归纳为一张对照表:

渠道 成功标识 取消标识 失败/处理中 最终依据
支付宝 resultStatus=9000 6001 8000/4000/5000 服务端异步通知
微信支付 errCode=0 errCode=-2 errCode=-1 服务端查询/回调
银联 pay_result=success pay_result=cancel pay_result=fail 服务端查询/对账

实际接入中,最常见的异常有三类。第一类是“客户端成功、服务端未收到通知”,此时不能直接发货,应主动调用渠道的订单查询接口确认。第二类是“回调不触发”,微信通常是 WXPayEntryActivity 路径错误,支付宝通常是 orderInfo 被二次编码或截断。第三类是“签名错误”,微信的 sign 必须由服务端用商户私钥生成,客户端只做透传,任何在客户端拼签名或改参数的做法都会导致 errCode=-1。

建议在接入阶段就为每个渠道建立独立的日志标签,例如 Pay-Alipay、Pay-Wechat、Pay-UnionPay,并在日志中记录订单号、渠道名、同步结果码和时间戳。这样在排查“用户说付了但订单没变”时,可以快速区分是客户端未回调、回调结果异常,还是服务端通知处理失败。
不要把客户端同步结果直接作为发货依据。支付宝的 9000、微信的 errCode=0、银联的 success 都只代表“客户端侧流程完成”,真正的资金到账与订单状态必须以服务端异步通知或主动查询结果为准。生产环境中,建议在客户端展示“支付结果确认中”,并在服务端确认后再更新订单状态。

把三个渠道的接入边界、依赖配置、初始化方式和结果判断梳理清楚之后,后续的订单状态机、幂等处理和异常补偿才有稳定的基础。渠道 SDK 本身并不复杂,复杂的是它们各自不同的回调路径、签名责任划分和结果语义,这些差异必须在接入阶段就明确下来。