今日推荐
经常阅读博客是个好习惯
推荐鸿洋CSDN的博客
微信支付相关文章
详细介绍Android开发集成微信支付(一) --- 仅客户端版
详细介绍Android开发集成微信支付(二) --- 完整版本
引言
本篇文章目的在于缩短集成微信支付的时间,让您更快的接入微信支付,躲坑
我的CSDN博客地址 (由于csdn的图片经常出现不显示问题,以后就在简书写博客了)
GitHub由于近期较忙,正加急整理中,敬请期待...
常用链接
微信开放平台 -- 注册并创建应用
微信支付SDK下载集成 -- 如何集成sdk
微信开放平台资源中心 -- 所有的资源
微信支付App开发文档 -- 微信支付的开发文档
微信支付业务流程 -- 里面有完成的支付流程
微信支付官方实例sample -- 下载官方示例
签名验证 -- 用于验证一下你代码生成的签名是否正确
这里有一个获取app签名工具的链接(签名工具下载地址) -- 用于获取打包后App的应用签名填写到开放平台上
目录
开始前准备
1.1 注册微信开放平台理清业务流程
开始集成
3.1 下载SDK和官方实例
3.2 开始集成
开启微信支付
这篇文章是一步一步的详细介绍了如何集成微信支付(包括完整的客户端和服务端该做的事情)如果你只是完成客户端开发,请看详细介绍Android开发集成微信支付(一) --- 仅客户端版
ok ,Let's go!(这篇文章不只是代码放在这,因为微信支付一直在改动,所以具体的每一步思路都在这,如果变动根据每一步去微信用最新的方式替换即可)
一: 开发前准备
1.1 注册微信开放平台
这一步的目的是在微信开放平台注册并创建应用,然后获取微信支付需要用到的 APP_ID , Mch_Id(商户号) , Mch_Key(商户key,签名的时候用)
首先我们点击上面的常用链接中的微信开放平台进入微信开放平台的官网,也可以百度搜索微信开放平台
接着点击移动应用开发
然后按照上图的流程一步一步的创建好应用然后通过审核,然后获取我们需要的各种ID
二:理清业务流程
首先微信开发文档已经有了简单的步骤介绍点击查看
点击常用链接的微信开放平台资源中心,如下图 包含了我们所有需要的东西
首先按照逻辑,我们应该先搞清楚微信支付的总体流程然后根据自己APP的业务需求制定适合自己的业务流程,然后才是下载SDK和集成代码
首先我们 点击进入开发文档 查看业务流程 如下图:
商户系统和微信支付系统主要交互说明:
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay
步骤4:商户APP调起微信支付。api参见本章节【app端开发步骤说明】
步骤5:商户后台接收支付通知。api参见【支付结果通知API】
步骤6:商户后台查询支付结果。,api参见【查询订单API】
总结一下就是:(商品类购买):
第一步:用户在APP中选择相应的商品和数量,提交给自己的后台,然后后台根据选择的商品调用微信的统一下单API(这里需要第一次生成签名),获取prepay_id
第二步:后台获取到prepay_id之后需要再次生成一次签名,(上面有详细的签名需要的字段)
第三步:后台将签名好的字段和客户端需要调起微信需要的字段传给客户端
第四步:客户端调起微信支付,并接收支付结果(本地回调,不可信),然后去后台查询支付结果,拿到微信支付的真实结果(后台会收到微信的支付结果通知,也可以去调用微信的支付结果)
三:开始集成
具体的流程搞清楚了,现在我们开始一步一步来集成微信支付
3.1 下载微信支付SDK和官方实例代码
首先我们先去下载SDK和官方实例 (记得之前微信的sdk还是要打包下载的,现在微信支付改版成为了托管到了JCenter上了,直接可以在gradle中添加依赖就可以了)
在build.gradle文件中,添加如下依赖即可:
dependencies {
compile 'com.tencent.mm.opensdk:wechat-sdk-android-with-mta:+'
}
或
dependencies {
compile 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+'
}
(其中,前者包含统计功能)
添加好依赖,就ok了
接着我们去下载官方实例代码,1.是可以看里面的具体流程2.是可以直接拷贝里面的工具类和代码
下载好之后解压,并在AndroidStudio中open,打开之后发现只包含微信分享和一些其他的例子,并不包含微信支付(最新版的已经将微信支付的实例代码去除,需要到微信APP支付Android开发文档 里面下载)
打开之后可能会有报错,我们不用管,因为我们不用运行,只是看一下里面有用的代码,最新版微信的实例代码已经将微信支付的实例去除,需要下载的话需要到微信APP支付Android开发文档 -- SDK下载里面去Download
下载完成 打开是这样的:
这里 wxapi/WXPayEntryActivity 是微信支付的回调页面 ,这个页面必须在 包名/wxapi 之下 所以直接把这个文件复制到我们工程中
MD5是一个工具类也复制到自己的项目中,需要用
public class MD5 {
private MD5() {}
public final static String getMessageDigest(byte[] buffer) {
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
try {
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(buffer);
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
return null;
}
}
}
PayActivity中有调起微信支付的代码可以参考
3.2 开始集成(写代码)
改集成也集成好了,流程也弄清楚了,实例代码也复制好了,这样就可以按照微信支付Android开发文档一步一步的开始写代码就ok了
微信支付开发文档
按照这个流程(因为这篇文章是所有的流程,所以全部都写一遍):
商户系统和微信支付系统主要交互说明:
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay
步骤4:商户APP调起微信支付。api参见本章节【app端开发步骤说明】
步骤5:商户后台接收支付通知。api参见【支付结果通知API】
步骤6:商户后台查询支付结果。,api参见【查询订单API】
3.2.1 统一下单
首先初始化微信
/**
* 初始化微信支付
*/
private void initWX() {
if (wxApi == null) {
wxApi = WXAPIFactory.createWXAPI(this, "你的微信APP_ID");
wxApi.registerApp("你的微信APP_ID");
}
}
查看统一下单API的文档,查看具体的统一下单的链接地址,需要的参数
统一下单的链接地址:URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
post请求
参数是文档里需要的,请求参数需要转换为xml
举例如下:
<xml>
<appid>wx2421b1c4370ec43b</appid>
<attach>支付测试</attach>
<body>APP支付测试</body>
<mch_id>10000100</mch_id>
<nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
<notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
<out_trade_no>1415659990</out_trade_no>
<spbill_create_ip>14.23.150.211</spbill_create_ip>
<total_fee>1</total_fee>
<trade_type>APP</trade_type>
<sign>0CB01533B8C1EF103065174F50BCA001</sign>
</xml>
定义一个统一下单JavaBean
public class WXPrePost {
//必须带的参数
public String appid;//微信开放平台审核通过的应用APPID
public String mch_id;//微信支付分配的商户号
public String nonce_str;//随机字符串,不长于32位。推荐随机数生成算法
public String sign;//签名,详见签名生成算法
public String body;//商品描述交易字段格式根据不同的应用场景按照以下格式:APP——需传入应用市场上的APP名字-实际商品名称,天天爱消除-游戏充值。
public String out_trade_no;//商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
public int total_fee;//订单总金额,单位为分,详见支付金额
public String spbill_create_ip;//用户端实际ip
public String notify_url;//接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
public String trade_type;//支付类型
//非必须携带的参数
public String device_info;//终端设备号(门店号或收银设备ID),默认请传"WEB"
public String detail;//商品名称明细列表
public String attach;//附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
public String fee_type;//符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
public String time_start;//订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
public String time_expire;//订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则 注意:最短失效时间间隔必须大于5分钟
public String goods_tag;//商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
public String limit_pay;//no_credit--指定不能使用信用卡支付
}
初始化统一下单的参数
// 统一下单
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
WXPrePost post = new WXPrePost();
post.appid = "你的APP_ID";
post.mch_id = "你的商户号";
post.nonce_str = genNonceStr();//随机字符串 **1
post.body = "介绍";
post.detail = "打赏";
post.out_trade_no = getTradeNo(); //商户订单号 **2
post.total_fee = 1;//单位是分
post.spbill_create_ip = getLocalIpAddress();//ip地址 **3
post.notify_url = "http://www.weixin.qq.com/wxpay/pay.php";//这里是后台接受支付结果通知的url地址
post.trade_type = "APP";
post.sign = genPackageSign(post);//签名 **4
**1 : 随机字符串,不长于32位。推荐随机数生成算法
微信支付API接口协议中包含字段nonce_str,主要保证签名不可预测。我们推荐生成随机数算法如下:调用随机数函数生成,将得到的值转换为字符串。
private String genNonceStr() {
Random random = new Random();
//这里就用到了微信实例代码中的MD5那个类
return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
*2 商户订单号
商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|@ ,且在同一个商户号下唯一。详见商户订单号
商户支付的订单号由商户自定义生成,微信支付要求商户订单号保持唯一性(建议根据当前系统时间加随机序列来生成订单号)。重新发起一笔支付要使用原订单号,避免重复支付;已支付过或已调用关单、撤销(请见后文的API列表)的订单号不能重新发起支付。
public String getTradeNo() {
return "这里可以是app名字" + genTimeStamp();
}
private long genTimeStamp() {
return System.currentTimeMillis() / 1000;
}
**3 ip地址 (可以不获取,直接给个默认,如果项目不要求的话)
public String getLocalIpAddress() {
StringBuilder IFCONFIG = new StringBuilder();
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress() && inetAddress.isSiteLocalAddress()) {
IFCONFIG.append(inetAddress.getHostAddress().toString() + ",");
break;
}
}
}
String[] split = IFCONFIG.toString().split(",");
if (split != null && split.length > 0) {
String ip = split[0];
if (ip != null && ip.length() > 0) {
return ip;
} else {
return "127.0.0.1";
}
} else {
return "127.0.0.1";
}
} catch (SocketException ex) {
return "127.0.0.1";
}
}
**4 获取签名,详见签名生成算法 生成完之后 点击这里验证签名 生成的签名是否正确
签名生成的通用步骤如下:
第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
特别注意以下重要规则:
◆ 参数名ASCII码从小到大排序(字典序);
◆ 如果参数的值为空不参与签名;
◆ 参数名区分大小写;
◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段
第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
/**
* 生成签名
*
* @param params
*/
private String genPackageSign(WXPrePost params) {
//拼接排序list
List<NameValuePair> packageParams = new LinkedList<>();
packageParams.add(new BasicNameValuePair("appid", "你的APP_ID"));
packageParams.add(new BasicNameValuePair("body", params.body));
packageParams.add(new BasicNameValuePair("detail", params.detail));
packageParams.add(new BasicNameValuePair("mch_id", "你的商户号"));
packageParams.add(new BasicNameValuePair("nonce_str", params.nonce_str));
packageParams.add(new BasicNameValuePair("notify_url", params.notify_url));
packageParams.add(new BasicNameValuePair("out_trade_no", params.out_trade_no));
packageParams.add(new BasicNameValuePair("spbill_create_ip", params.spbill_create_ip));
packageParams.add(new BasicNameValuePair("total_fee", params.total_fee + ""));
packageParams.add(new BasicNameValuePair("trade_type", params.trade_type));
StringBuilder sb = new StringBuilder();
for (int i = 0; i < packageParams.size(); i++) {
sb.append(packageParams.get(i).getName());
sb.append('=');
sb.append(packageParams.get(i).getValue());
sb.append('&');
}
sb.append("key=");
sb.append("商户key");//key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
//这里又用到了从实例代码复制的MD5 可以去上面copy
String packageSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
return packageSign;
}
ps:
**如上已经生成了统一下单需要的签名,生成完成之后可以去点击这里验证签名 生成的签名是否正确
**
上面可能会遇到问题:由于代码写完的时候是支持NameValuePair,前几天升级之后,发现Android已经去掉了Apache的相关类,其中就有NameValuePair,这里解决方式有两种:
一是 : 在Gradle中添加这两个就可以接着使用NameValuePair
compile 'org.apache.httpcomponents:httpcore:4.4.1'
compile 'org.apache.httpcomponents:httpclient:4.5'
二是: 可以通过ContentValues 简单实现一个类似的类,然后提供一个查询的方法可以看这里
上面列表中参数的名称像appid,body,mch_id一定要按照上面写,大小写也需要一样,和官方文档中一致才可以
将参数转换为xml 并post请求统一下单
这里给一个将参数转换为xml的方法,内部的参数用List<NameValuePair>封装
private String toXml(List<NameValuePair> params) {
StringBuilder sb = new StringBuilder();
sb.append("<xml>");
for (int i = 0; i < params.size(); i++) {
sb.append("<" + params.get(i).getName() + ">");
sb.append(params.get(i).getValue());
sb.append("</" + params.get(i).getName() + ">");
}
sb.append("</xml>");
return sb.toString();
}
参数列表在这里获取
@NonNull
private List<NameValuePair> getFirstSignParams(WXPrePost params) {
List<NameValuePair> packageParams = new LinkedList<>();
packageParams.add(new BasicNameValuePair("appid", "你的APP_ID"));
packageParams.add(new BasicNameValuePair("body", params.body));
packageParams.add(new BasicNameValuePair("detail", params.detail));
packageParams.add(new BasicNameValuePair("mch_id", "你的商户号"));
packageParams.add(new BasicNameValuePair("nonce_str", params.nonce_str));
packageParams.add(new BasicNameValuePair("notify_url", params.notify_url));
packageParams.add(new BasicNameValuePair("out_trade_no", params.out_trade_no));
packageParams.add(new BasicNameValuePair("spbill_create_ip", params.spbill_create_ip));
packageParams.add(new BasicNameValuePair("total_fee", params.total_fee + ""));
packageParams.add(new BasicNameValuePair("trade_type", params.trade_type));
packageParams.add(new BasicNameValuePair("sign", params.sign));
return packageParams;
}
然后我们将上面初始化完成的WxPrePost 传给这个方法然后调用toXml就可以获取到统一下单的xml参数了
Next:有了统一下单的xml参数,我们就可以直接post请求url 将xml参数传过去,返回一个String类型的字符串也是xml,解析即可获取prepay_id,返回结果如下:
只有return_code和result_code都为SUCCESS的时候才会有prepay_id,简单的判断一下然后解析xml
获取我们需要的参数
public Map<String, String> decodeXml(String content) {
try {
Map<String, String> xml = new HashMap<>();
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new StringReader(content));
int event = parser.getEventType();
while (event != XmlPullParser.END_DOCUMENT) {
String nodeName = parser.getName();
switch (event) {
case XmlPullParser.START_DOCUMENT:
break;
case XmlPullParser.START_TAG:
if (!"xml".equals(nodeName)) {
xml.put(nodeName, parser.nextText());
}
break;
case XmlPullParser.END_TAG:
break;
}
event = parser.next();
}
return xml;
} catch (Exception e) {
}
return null;
}
我们将解析出来的xml放入map中,然后遍历一下获取我们再次签名需要的prepay_id , nonce_str , appid
//判断是否成功
//...
//再次签名(参与签名的字段有 :Appid partnerId prepayId nonceStr TimeStamp package)
String appId = "";
String prepayId = "";
String nonceStr = "";
for (Map.Entry<String, String> entry : stringMap.entrySet()) {
if ("appid".equals(entry.getKey())) {
appId = entry.getValue();
} else if ("prepay_id".equals(entry.getKey())) {
prepayId = entry.getValue();
} else if ("nonce_str".equals(entry.getKey())) {
nonceStr = entry.getValue();
}
}
//有可能会有解析失败的情况 ,需要判断
//...
到这里统一下单就结束了,接下来就是再次签名获取调起微信支付的参数
3.2.2 第二次签名
上一步已经获取到了再次签名的参数,接下来再次签名,按照相同的签名规则
参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay
看一下上面的参数appId partnerId(商户号),prepayId,nonceStr ,package 都已经有了 就差一个时间戳timeStamp
String TimeStamp = String.valueOf(genTimeStamp());
private long genTimeStamp() {
return System.currentTimeMillis() / 1000;
}
获取到所有参数之后,接着封装到列表之后,进行第二次签名
//获取参数列表
private List<NameValuePair> getSecondSignParams(String appId, String prepayId, String nonceStr, String timeStamp) {
//appId,partnerId,prepayId,nonceStr,timeStamp,package
List<NameValuePair> packageParams = new LinkedList<>();
packageParams.add(new BasicNameValuePair("appid", appId));
packageParams.add(new BasicNameValuePair("noncestr", nonceStr));
packageParams.add(new BasicNameValuePair("package", "Sign=WXPay"));
packageParams.add(new BasicNameValuePair("partnerid", "你的商户号"));
packageParams.add(new BasicNameValuePair("prepayid", prepayId));
packageParams.add(new BasicNameValuePair("timestamp", timeStamp));
return packageParams;
}
//第二次签名
private String genSecondPackageSign(List<NameValuePair> params) {
//拼接排序list
StringBuilder sb = new StringBuilder();
for (int i = 0; i < params.size(); i++) {
sb.append(params.get(i).getName());
sb.append('=');
sb.append(params.get(i).getValue());
sb.append('&');
}
sb.append("key=");
sb.append("商户密钥");//上面已经获取
String packageSign = getMessageDigest(sb.toString().getBytes()).toUpperCase();
return packageSign;
}
//ok 获取签名
String secondPackageSign = genSecondPackageSign(getSecondSignParams(appId, prepayId, nonceStr, TimeStamp));
别忘了获取签名之后去验证一下(验证链接在上面),到这里调起微信需要的所有参数都已经获取到了
3.2.3 调起微信 *
已经最后一步了,我们马上就可以去验证是不是能够成功的调起微信并接收到微信支付的本地回调
调起微信支付,需要在开放平台设置包名和签名(这也是微信支付最大的一个坑,大部分人调不起微信基本都是这里的问题)
首先先看官方文档 的详细步骤
这里有一个获取签名工具的链接(签名工具下载地址 安装到手机上获取自己app的签名(打包之后安装到手机上)
这里只能是你在开放平台上填的签名的app才可以调起微信,比如我打包之后用上面的签名工具获取app的签名填入开放平台,所以想要测试微信支付需要打包安装到手机上才可以调起微信,如果发现没有调起微信就这样操作,虽然麻烦点,但是可以解决
//调起支付
try {
//一下所有的参数上面均获取到了
PayReq req = new PayReq();
req.appId = "你的APP_ID";
req.partnerId = "你的商户号";
req.prepayId = "你的prepayId";
req.nonceStr = "你的nonceStr";
req.timeStamp = "你的TimeStamp";
req.packageValue = "Sign=WXPay";
req.sign = "你的签名";
req.extData = "app data"; // optional
// 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信
wxApi.sendReq(req);
} catch (Exception e) {
Log.e("PAY_GET", "异常:" + e.getMessage());
}
到这里正常情况下肯定可以调起微信支付 ,如果出现了问题 1.签名问题(去上面验证下自己的签名) 2.应用签名问题请参考(3.2.3 调起微信 也就是上面)
3.3.4 接受微信支付的回调
到这一步说明你的微信已经成功调起,这里接受微信支付的回调
我们需要在应用的包名下建立wxapi的包名然后将实例中的WXPayEntryActivity.java复制过来
记住一定是项目的包名下建立wxapi包然后复制过来
然后再AndroidManifest中添加
<activity
android:name=".wxapi.WXPayEntryActivity"
android:exported="true"
android:launchMode="singleTop"/>
在WXPayEntryActivity中就可以接受回调了
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//这里可以不给布局,也可以给个透明或者loading框 根据项目的需求
setContentView(R.layout.act_pay);
api = WXAPIFactory.createWXAPI(this, 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) {
Log.d(TAG, "onPayFinish, errCode = " + resp.errCode);
if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
//根据下面的errCode表中 switch一下即可
//如果是0的话,就是成功,然后这里去服务器查询具体的支付结果,注意服务器查询的支付结果才是可靠地支付结果
}
}
支付回调可能遇到的问题:
1.如果项目中存在两种即以上的支付(比如:商城购买 和 普通的打赏支付 或者其他),两种支付完成后都会回调onResp但是之后的逻辑不一样,所以我们需要手动的区分两种支付(比如在支付前可以存一个变量标识)
2.支付成功回调之后,也就是errCode = 0 的时候,我们需要去服务器查询结果,一般会需要给他们订单号去查询,但是微信回调之后不会给你订单号,所以也需要你去本地存一下(或者其他更好的方式)
到这里微信支付就集成完了,当然代码很粗糙可以在具体的细节细化,当然有其他更好的方式也可以提意见优化
希望这篇文章可以帮助到需要的人,如果还有其他问题或者补充可以联系我~~~