微信支付踩坑记

  • 作者:彭老师
  • 日期:2020-07-03
  • 类型:Android
  • 说明:本文源于彭老师手写摘要,如需转载请带上链接或注明出处!

上次做微信支付模糊记得是2015年,虽然阔别这么久至少心里有底。但没想到这么坑!

无论你看与不看这篇文章,相信你都见过下面4个页面。并且逐字阅读很多遍了
1、https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=11_1
2、https://developers.weixin.qq.com/doc/oplatform/Downloads/Android_Resource.html
3、https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5
4、https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12&index=2

然并卵,根本无从下手!

  • 总结下:
  • WeChatOpenSdkSample这个项目所谓的Demo根本没有支付模块
  • wechat_sdk_sample_android这个项目还是Eclipse的

请注意:以上两个项目都有用!!!后面会说到

介绍下思路:能不能弄到一个Jar包,然后调用Api不就得了

1、参考WeChatOpenSdkSample项目中的build.gradle,在自己项目对应加入:

implementation 'com.tencent.mm.opensdk:wechat-sdk-android-with-mta:5.3.1'
  • 可能会编译不通过,亲猜发现是集成了友盟Umeng。所以改下:
    compileOnly 'com.tencent.mm.opensdk:wechat-sdk-android-with-mta:5.3.1'

2、参考wechat_sdk_sample_android项目中的WXPayEntryActivity,复制到自己项目对应目录(切记:项目包名.wxapi.WXPayEntryActivity)

package com.xyz.xyz.wxapi;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.os.Bundle;

import com.cmonbaby.utils.show.LogUtils;
import com.cmonbaby.utils.show.ToastUtils;
import com.xyz.xyz.R;
import com.xyz.xyz.common.utils.Constance;
import com.tencent.mm.opensdk.constants.ConstantsAPI;
import com.tencent.mm.opensdk.modelbase.BaseReq;
import com.tencent.mm.opensdk.modelbase.BaseResp;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;

public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {

private static final String TAG = "MicroMsg.SDKSample.WXPayEntryActivity";

private IWXAPI api;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 界面自由发挥,跟支付流程无关
setContentView(R.layout.pay_result);

api = WXAPIFactory.createWXAPI(this, Constance.APP_ID);
api.handleIntent(getIntent(), this);
}

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

@Override
public void onReq(BaseReq req) {
}

@Override
public void onResp(BaseResp resp) {
LogUtils.e(TAG, "onPayFinish, errCode = " + resp.errCode);

if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("提示");
builder.setMessage(getString(R.string.pay_result_callback_msg, String.valueOf(resp.errCode)));
builder.show();

// 判断resultStatus 为“0”则代表支付成功,具体状态码代表含义可参考接口文档
if (resp.errCode == 0) {
ToastUtils.show(this, "支付成功");
} else if (resp.errCode == -1) {
// -1为支付失败,包括用户主动取消支付,或者系统返回的错误
ToastUtils.show(this, "支付失败");
} else if (resp.errCode == -2) {
// -2为取消支付,或者系统返回的错误
ToastUtils.show(this, "取消支付");
} else {
// 其他为系统返回的错误
ToastUtils.show(this, "支付错误");
}
}
}
}

然后很天真,后面的代码简直不要太简单,分分钟打爆它!!!

看到微信有那么一段介绍:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5

  • 调起支付
  • 商户服务器生成支付订单,先调用统一下单API(详见第7节)生成预付单,获取到prepay_id后将参数再次签名传输给APP发起支付。以下是调起微信支付的关键代码:
IWXAPI api;
PayReq request = new PayReq();
request.appId = "wxd930ea5d5a258f4f";
request.partnerId = "1900000109";
request.prepayId= "1101000000140415649af9fc314aa427",;
request.packageValue = "Sign=WXPay";
request.nonceStr= "1101000000140429eb40476f8896f4c9";
request.timeStamp= "1398746574";
request.sign= "7FFECB600D7157C5AA49810D2D8F28BC2811827B";
api.sendReq(request);

随便找个Button植入上述代码,结果怎么玩都是返回 -1(支付失败)

怎么办,看文档呗。这里我给大家走下捷径:

1、确认你是debug还是release环境,跑在真机上的。怎么验证呢?

点击:https://developers.weixin.qq.com/doc/oplatform/Downloads/Android_Resource.html
该页面最底部有一个:签名生成工具
用于获取安装到手机的第三方应用签名的apk包。点击下载 签名生成工具

安装APK到真机上 - 打开app - 输入项目包名 - 得到签名结果

微信开放平台 - 登录 - 管理中心 - 开发信息(匹配两值是否一致)

点击:https://open.weixin.qq.com/

后台给出接口,安卓传参:totalFee付款金额,userId识别某用户ID。得到返回结果,赋值给上述appIdpartnerIdprepayId等7个属性

结果发现还是返回 -1(支付失败)

这个阶段是最难熬的,给点提示:

1、如果持续返回 -1(支付失败)可以断言后台签名有误!
2、如果提示:支付验证签名失败。则说明后台签名正确!接近答案了!

改动上述代码:

// 得到后台接口返回对象
private void pay(WechatPayEntity pay) {
// 商户APP工程中引入微信JAR包,调用API前,需要先向微信注册您的APPID,代码如下:
IWXAPI api = WXAPIFactory.createWXAPI(this, Constance.APP_ID);
// 移动端时间戳,不用后台的。注意:微信支付的时间戳是10位非毫秒
String temp = String.valueOf(System.currentTimeMillis() / 1000);

PayReq request = new PayReq();
request.appId = pay.getAppid(); // 应用ID(微信开放平台审核通过的应用APPID)
request.partnerId = pay.getPartnerid(); // 商户号(微信支付分配的商户号)
request.prepayId= pay.getPrepayid(); // 预支付交易会话ID(微信返回的支付交易会话ID)
request.packageValue = "Sign=WXPay"; // 扩展字段,暂填写固定值Sign=WXPay
request.nonceStr= pay.getNoncestr(); // 随机字符串(随机字符串,不长于32位。推荐随机数生成算法)
request.timeStamp= temp; // 时间戳

// 将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),最末尾加上商户Key
Map<String, String> map = new HashMap<>();
map.put("appid", pay.getAppid());
map.put("partnerid", pay.getPartnerid());
map.put("prepayid", pay.getPrepayid());
map.put("package", "Sign=WXPay");
map.put("noncestr", pay.getNoncestr());
map.put("timestamp", temp);

// 最终拉起支付时,request对象中的sign属性值为安卓自定义签名的值。而不能用后台返回的任何sign签名值,切记!!!
request.sign= getSign(map); // 签名

api.sendReq(request);
}

private String getSign(Map<String, String> data) {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
// 此处商户Key为:自定义
sb.append("key=").append("11hKHB6OIJG4I32J038JH34IG234548I");
MessageDigest md = null;
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] array = new byte[0];
try {
array = md.digest(sb.toString().getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
StringBuilder sb2 = new StringBuilder();
for (byte item : array) {
sb2.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb2.toString().toUpperCase();
}