2. 微信支付SDK接入与统一下单

微信支付SDK接入与统一下单 学习卡片

微信支付 App 支付链路与关键对象

微信 App 支付与支付宝最大的差异在于:调起支付的参数不是客户端自己拼的,而是服务端调用微信「统一下单」接口后返回的 prepay_id。客户端只负责把服务端下发的这组参数交给微信 SDK,然后等待微信 App 回调结果。整条链路涉及三个角色:Android 客户端、业务服务端、微信支付系统。

Android 客户端 业务服务端 微信支付系统 ① 提交订单 out_trade_no + total_fee ② 统一下单 /pay/unifiedorder ③ 返回 prepay_id + 签名参数 ④ 二次签名 appId/partnerId/prepayId... ⑤ 调起微信支付 PayReq → sendReq() ⑥ 用户确认支付 微信App内完成 ⑦ 回调 WXPayEntryActivity ⑧ 异步通知 notify_url 验签 关键约束:prepay_id 有效期 2 小时;客户端回调仅作 UI 提示,最终以服务端异步通知验签结果为准 WXPayEntryActivity 必须在 AndroidManifest 中注册,且包名路径固定为 wxapi 目录

SDK 集成与依赖配置

微信开放平台 App 支付 SDK 以 aar 形式提供,需从微信开放平台下载后放入 libs/ 目录。在 app/build.gradle 中声明依赖:

dependencies {
    implementation files('libs/wechat-sdk-android-without-mta-6.8.0.aar')
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    implementation 'com.google.code.gson:gson:2.10.1'
}

在 AndroidManifest.xml 中注册微信回调 Activity,包名路径必须严格为 你的应用包名.wxapi.WXPayEntryActivity:

<activity
    android:name=".wxapi.WXPayEntryActivity"
    android:exported="true"
    android:launchMode="singleTop"/>
包名路径错误是微信支付回调不触发的最常见原因。微信 SDK 通过反射查找 {packageName}.wxapi.WXPayEntryActivity,路径不对则回调静默失败。

服务端统一下单与客户端调起

服务端调用微信统一下单接口 https://api.mch.weixin.qq.com/pay/unifiedorder,核心参数如下:

参数说明示例值
appid开放平台应用 IDwx8888888888888888
mch_id商户号1900000109
out_trade_no商户订单号202501011200001
total_fee金额(分)1
body商品描述测试商品
notify_url异步通知地址https://your.domain/wx/notify
trade_type交易类型APP

统一下单返回 prepay_id 后,服务端需进行二次签名,将以下字段按字典序拼接后 MD5 签名,返回给客户端:

// 服务端二次签名后返回给客户端的参数
{
  "appId": "wx8888888888888888",
  "partnerId": "1900000109",
  "prepayId": "wx01120000abcdef1234567890",
  "packageValue": "Sign=WXPay",
  "nonceStr": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
  "timeStamp": "1735700000",
  "sign": "A1B2C3D4E5F6..."
}

客户端收到后构造 PayReq 并调起微信:

public void pay(PayParams params) {
    IWXAPI api = WXAPIFactory.createWXAPI(context, params.appId, true);
    api.registerApp(params.appId);

    PayReq req = new PayReq();
    req.appId     = params.appId;
    req.partnerId = params.partnerId;
    req.prepayId  = params.prepayId;
    req.packageValue = "Sign=WXPay";
    req.nonceStr  = params.nonceStr;
    req.timeStamp = params.timeStamp;
    req.sign      = params.sign;

    boolean ok = api.sendReq(req);
    if (!ok) {
        // 微信未安装或参数校验失败
        Toast.makeText(context, "调起微信失败,请检查微信是否安装", Toast.LENGTH_SHORT).show();
    }
}

WXPayEntryActivity 回调处理

支付完成后微信会回调 WXPayEntryActivity.onResp,通过 BaseResp.errCode 判断结果:

public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {

    private IWXAPI api;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        api = WXAPIFactory.createWXAPI(this, Constants.APP_ID);
        api.handleIntent(getIntent(), this);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        api.handleIntent(intent, this);
    }

    @Override
    public void onResp(BaseResp resp) {
        if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
            switch (resp.errCode) {
                case 0:
                    // 支付成功,但最终以服务端异步通知为准
                    Toast.makeText(this, "支付成功", Toast.LENGTH_SHORT).show();
                    break;
                case -1:
                    Toast.makeText(this, "支付失败:" + resp.errStr, Toast.LENGTH_SHORT).show();
                    break;
                case -2:
                    Toast.makeText(this, "已取消支付", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
        finish();
    }
}
客户端回调的 errCode == 0 只代表微信侧受理成功,不能作为发货依据。必须以服务端收到 notify_url 异步通知并验签通过后的结果为准。

验证与排障要点

  • 回调不触发:检查 WXPayEntryActivity 是否位于 {包名}.wxapi 目录,且已在 Manifest 注册。
  • 调起失败:sendReq 返回 false,通常是 appId 与开放平台不一致,或签名参数被篡改。
  • 签名错误:服务端二次签名时字段顺序、大小写、空值处理必须与微信文档一致,packageValue 固定为 Sign=WXPay。
  • 金额单位:total_fee 单位为分,1 元需传 100,传错会导致金额异常。