最近一个项目需要接入微信支付,所以在现有的项目中接入这个功能,但是最终发现,微信支付是个大坑,至少我认为微信支付文档的撰写者应该是小学水平,又或者是幼儿园水平,写文档的能力简直无语,很多关键性的细节居然是概括或者全局描述,一个简单的支付校验,整整花去我一天时间,真的是怀疑了自己的智商。
至于说怎么解决的,是因为我终于看到两字”假如“,原来参数举例是不对的,只是打了个比方,参数格式也不是蛇形的,也只是打了个比方,这个时候,真的想把微信支付的文档撰写者按在地上摩擦,以下是截图证据:
好了废话不多说了,下面开始说明:
一、准备工作:
1、微信公众号,appid和appsecret(这里不做赘述,相信大家都没有问题)
2、商户号,mchId,证书,申请授权目录,申请商户API密钥(基本没有问题,如果有问题,给我留言,我来补上)
3、下载微信支付JSAPI的demo,地址是https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1,下载完了以后不能直接使用,因为按照sdk中的readme,是无法正常使用的(对的,这个就是微信的sdk和demo,就是这么神奇,官方出的也不能用)。
二、下面是对这些不完整进行修改的地方修改如下:
1、WXPayConfig.java文件,所有的abstract前加上public,其中有一个处理doMain的需要具体实现,如下:
/**
* 获取WXPayDomain, 用于多域名容灾自动切换
*
* @return
*/
abstract IWXPayDomain getWXPayDomain();
改成
/**
* 获取WXPayDomain, 用于多域名容灾自动切换
*
* @return
*/
public IWXPayDomain getWXPayDomain() {
IWXPayDomain iwxPayDomain = new IWXPayDomain() {
public void report(String domain, long elapsedTimeMillis, Exception ex) {
}
public DomainInfo getDomain(WXPayConfig config) {
return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API, true);
}
};
return iwxPayDomain;
}
2、然后编译成jar包,在引用的过程,需要创建一个WXConfig.java,复制下面代码:
import com.github.wxpay.sdk.IWXPayDomain;
import com.github.wxpay.sdk.WXPayConfig;
import com.github.wxpay.sdk.WXPayConstants;
import java.io.*;
public class WXConfig extends WXPayConfig {
private byte[] certData;
public WXConfig() throws Exception {
//此处是微信支付安全证书存放的位置,证书需自行下载
//下载地址:登录微信商户平台-账户中心-API安全-API证书
//动态读取安全证书写法可参考我的另一篇文章:
//https://blog.csdn.net/qq_36928715/article/details/104656618
// String certPath = "F:/cert/apiclient_cert.p12";
String certPath = "/data/cert/apiclient_cert.p12";
File file = new File(certPath);
InputStream certStream = new FileInputStream(file);
this.certData = new byte[(int) file.length()];
certStream.read(this.certData);
certStream.close();
}
//微信公众号appid
public String getAppID() {
return "wx2323423423423423";
}
//商户号
public String getMchID() {
return "160923423423";
}
public String getKey() {
return "GHJs34223423423423423423423";//32位商户api密钥
}
public InputStream getCertStream() {
ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
return certBis;
}
public int getHttpConnectTimeoutMs() {
return 8000;
}
public int getHttpReadTimeoutMs() {
return 10000;
}
}
3、然后统一下单,且计算paySign,paySign签名的值是appId,nonceStr,timeStamp,package四个参数(跟官方文档提的那个一毛线关系都没有,因为他写了个假如,所以他也没说是真的,就让你自己猜吧,加密方式必须是MD5,不要问为什么,官方就是这么high),尽量调用微信sdk内置的签名方法,代码如下:
WXConfig config = new WXConfig();
WXPay wxpay = new WXPay(config);
Map<String, String> data = new HashMap<String, String>();
data.put("body", "腾讯充值中心-QQ会员充值");
data.put("out_trade_no", "2016090910595900000012");
data.put("device_info", "WEB");
data.put("fee_type", "CNY");
data.put("total_fee", "1");
data.put("spbill_create_ip", "123.12.12.123");
data.put("notify_url", "http://www.example.com/wxpay/notify");
data.put("trade_type", "JSAPI");
data.put("product_id", "12");
data.put("openid", "dhajkgdhjaghj13267816381");
data.put("sign_type", "MD5");
Map<String, String> resp = wxpay.unifiedOrder(data);
//后端生成paySign给前端,放置签名失败
Date date=new Date();
String sec=String.valueOf(date.getTime()/1000);
Map map = new HashMap();
map.put("appId", config.getAppID());// 公众号id
map.put("nonceStr",resp.get("nonce_str"));// 随机字符串
map.put("timeStamp",sec);
map.put("package", "prepay_id="+resp.get("prepay_id"));
map.put("signType", "MD5");
String sign = WXPayUtil.generateSignature(map,config.getKey());
map.put("paySign", sign);
4、前端调用:
WeixinJSBridge.invoke('getBrandWCPayRequest', {
"appId": wxObj.appid,//公众号ID,由商户传入
"timeStamp": wxObj.timeStamp,//不要自己生成,取后端传回的
"nonceStr": wxObj.nonceStr, //不要自己生成,取后端传回的随机串
"package": "prepay_id=" + wxObj.prepayId,//预付订单
"signType": "MD5",//微信签名方式:
"paySign": wxObj.signType //不要自己生成,取后端传回的微信签名
},
function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
console.log(res);
}
});
5、到此就OK了。下面我会把修好的sdk和调用的代码上传,提供下载: