电话咨询
QQ咨询
微信咨询
返回顶部

安卓微信支付小程序开发:5步集成API与3大关键避坑指南

安卓微信支付在小程序里的集成,一上来就盯着官方文档啃,结果发现“调起支付”那几步明明写得很清楚,但真到自己项目里就是各种“支付验证签名失败”或者“支付环境异常”。这不是你代码写得不对,而是安卓端和微信小程序之间的交互逻辑,跟纯H5支付或者App支付有本质区别。

我们先理清一个核心概念:小程序支付本质上是“微信客户端内部发起支付”。你的安卓代码并不直接调用微信支付SDK,而是通过小程序提供的wx.requestPayment接口来触发。这意味着,你的安卓端角色是“后台服务的提供者”和“小程序容器的载体”,而不是支付的直接执行者。

很多开发者会犯一个错误:在安卓端自己拼装支付参数,然后试图用Intent调起微信支付。这在小程序场景下是行不通的。正确流程应该是:安卓端只负责生成预支付订单的签名参数,然后把这些参数传给小程序前端,由小程序调用官方接口完成支付。这里有个容易被忽略的细节——签名用的prepay_id必须是从微信支付服务端下单接口返回的,不能自己在客户端生成。

举个实际例子。假设你有一个电商类小程序,用户点击“立即购买”后,安卓端需要做这几件事:

1. 服务端统一签名,客户端只做中转
下单接口(/pay/unifiedorder)必须在你的后端服务器上调用,返回的prepay_id连同appIdtimeStampnonceStrpackage(固定值prepay_id=xxx)、signType这五个参数,由后端完成签名。签名算法用MD5还是HMAC-SHA256?建议统一用HMAC-SHA256,安全性更高,而且微信官方推荐。签名后的paySign连同其他参数一起返回给安卓客户端。

安卓端拿到这六个参数(五个原始参数+paySign)后,通过WebView的JavaScript Bridge或者小程序SDK提供的接口,把它们传递给小程序页面。这里有一个关键点:参数传递必须用JSON字符串,且字段顺序不能乱。微信支付签名验签时,对字段顺序有严格要求,如果你在安卓端重新组装了JSON,顺序变了,签名就会失效。

2. 小程序端调用wx.requestPayment
在小程序的JS代码里,接收安卓传来的参数后,直接调用:

wx.requestPayment({
  timeStamp: data.timeStamp,
  nonceStr: data.nonceStr,
  package: data.package,
  signType: data.signType,
  paySign: data.paySign,
  success: function(res) {
    // 支付成功,通知安卓端更新UI
    wx.miniProgram.navigateBack()
  },
  fail: function(err) {
    // 支付失败,返回错误信息给安卓
  }
})

注意这里的success回调并不代表钱已经到账,它只是代表用户输入了密码并支付成功。真正的资金到账需要后端通过支付结果通知接口来确认。所以在success里,你应该让小程序通知安卓端去轮询或者等待后端回调,而不是直接显示“支付成功”。

3. 安卓端处理支付回调的坑
很多安卓开发者会去监听微信支付的回调广播,但在小程序场景下,这个广播是收不到的。因为支付是在小程序WebView内部完成的,安卓端只能通过小程序SDK提供的onPayResult或者JS Bridge来获取结果。如果你用的不是官方小程序SDK,而是自己封装的WebView,那就更麻烦了——你需要在WebView的shouldOverrideUrlLoading里拦截微信支付的回调URL,但微信支付的回调URL是动态的,而且经常变,不建议这么干。

更稳妥的做法是:让小程序支付成功后,主动调用安卓端的原生接口。比如在success回调里,通过wx.miniProgram.postMessage发送消息给安卓端,安卓端在WebView的onMessage里接收,然后根据消息内容去后端确认订单状态。这样既避免了拦截URL的不稳定性,又保证了数据一致性。

4. 支付环境检测的差异化处理
安卓端在拉起支付前,需要检测微信是否安装,以及微信版本是否支持小程序支付。但这里有个特殊情况:如果用户手机是华为或者小米,它们系统自带的“应用分身”功能可能会导致微信支付调起失败。因为分身微信的包名和签名跟原版不同,微信支付SDK在校验时会报错。

解决方案是:不依赖微信SDK的检测,而是直接尝试调起支付,如果失败,引导用户关闭分身或使用原版微信。具体做法是在调用wx.requestPayment之前,先用wx.miniProgram.getEnv判断当前是否在小程序环境,如果不是,则提示用户打开微信小程序。这一步会忽略,但实际测试中,有相当一部分用户是从浏览器直接打开链接的,没有经过小程序容器。

5. 签名算法的代码实现(附Java示例)
后端签名时,喜欢用Map来存参数,然后遍历拼接。但Map是无序的,会导致签名结果跟微信端不一致。正确做法是用TreeMap或者手动按字母顺序排序。下面是一个可用的Java签名方法:

public static String getSign(Map<String, String> params, String key) {
    // 按key排序
    TreeMap<String, String> sortedMap = new TreeMap<>(params);
    StringBuilder sb = new StringBuilder();
    for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
        if (entry.getValue() != null && !"".equals(entry.getValue())) {
            sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }
    }
    sb.append("key=").append(key);
    // 使用HMAC-SHA256
    Mac mac = Mac.getInstance("HmacSHA256");
    SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
    mac.init(secretKey);
    byte[] digest = mac.doFinal(sb.toString().getBytes("UTF-8"));
    return bytesToHex(digest).toUpperCase();
}

注意这里的key是商户平台设置的API密钥,不是AppSecret。另外,package参数的值是prepay_id=xxx,这个等号两边不能有空格,栽在这上面。

6. 扩展话题:安卓端如何管理支付状态
支付过程中,用户可能会中途退出小程序,或者切换到其他App再回来。这时候安卓端需要有一个状态管理机制。建议在安卓端维护一个PendingOrder队列,每次发起支付前,把订单号存入本地数据库或者内存缓存,支付成功后从队列移除。如果用户返回时发现队列里还有未完成的订单,就主动去后端查询该订单的支付状态。这样即使用户退出了小程序,重新打开后也能看到正确的支付结果。

还有一个小技巧:在支付请求发出后,安卓端启动一个30秒的定时器,定时器触发后如果还没有收到支付结果,就主动调用后端查询接口。因为微信支付回调有时会延迟,但30秒内基本能到。如果30秒后还没结果,可能是网络问题或者用户取消了支付,此时应该提示用户“支付结果未知,请查看订单状态”,而不是直接显示支付失败。

最后说一个不知道的点:微信支付在安卓小程序里,不支持在模拟器上测试。所有涉及支付的功能,必须用真机调试,而且微信版本不能低于7.0.0。如果你用Genymotion或者Android Studio自带的模拟器,调起支付时会直接报“支付环境异常”,这不是代码问题,是微信的限制。所以开发阶段,建议准备一台专门的测试机,微信保持最新版本,并且关闭“微信支付”的免密支付功能,方便测试各种失败场景。

上一篇
“每月水电费对不上,老公总怀疑我藏私房钱,直到他看了我做的收支表……”
下一篇
“道理都懂,可情绪上来的时候,那些‘理’全变成了扎向自己的刀。”