java微信开发之微信支付
来源:互联网 发布:搜狗微信 php 爬虫 编辑:程序博客网 时间:2024/05/23 18:55
**写在前面:
本人一直“奉行授人以鱼不如授人以渔”,本文主要是起一个引导的作用,注意一些很坑的地方。
微信支付,本人菜鸟花了2天时间弄出来,也算是有点成就感,所以特此做个记录
**
分割线—————————————————————————
正文开始:
先来看看微信官方流程图:
总结起来就是四个步骤:
- 点击支付按钮
- 微信拉起支付,输入密码
- 完成支付
- 通知微信服务器
下面就是我做测试支付的图:
点击支付按钮(后台一大堆业务逻辑处理,后面会讲):
微信拉起支付(这个调用微信自动完成的):
完成支付,通知微信(这个需要我们自己完成业务逻辑,后面会讲到)
流程就是这样,其实总结出来,我们需要做的就是2步:
流程图中
4:统一下单
5:调用统一下单api
6:生成下单签名
(4 5 6)可以并为一步
11:告知微信通知处理结果
开始编码实现:
查阅文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3
获取到统一下单接口:
统一下单接口:https://api.mch.weixin.qq.com/pay/unifiedorder
文档中说明,统一下单必须获取openid,各位客官别急,待我带你们一起查阅官方文档:
https://mp.weixin.qq.com/wiki?action=doc&id=mp1421140842&t=0.003036260317902828#1
这里一大堆说的就是获取openid,文档讲的十分清楚。
先放下prepay_id,我们来说说如何获取open_id(因为没有open_id,我们也没法去获取prepay_id)
获取这个分为4步:
- 用户同意授权,获取code
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE
我来解释下后面附带参数的意思:
appid: 用户唯一凭证,就是微信公众号的应用id
redirect_uri: 这个就是回调url,注意这个url 会附带着 code过去,就是第一步中的code。
scope: 有两种 snsapi_base 和 snsapi_userinfo 具体请参照文档
state: 随意填写
其他参数 照写 - 通过code换取网页授权access_token
请求以下链接获取access_token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
参数说明:
appid:这个参数使用都是一样的
secret: 这个参数在位置支付基本配置中可以查看
code: 第一步获取的code
grant_type:不变
请求这个就能返回如下json
{ "access_token":"ACCESS_TOKEN", "expires_in":7200, "refresh_token":"REFRESH_TOKEN", "openid":"OPENID", "scope":"SCOPE" }
解析上述json字符就可以获取到openid了。各位客官,可以喝杯茶休息休息啦!
接着来:有了openid,开始获取prepay_id
继续文档:
查阅文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3
获取到统一下单接口:
统一下单接口:https://api.mch.weixin.qq.com/pay/unifiedorder
我们把那些必须的参数一一列出来,并给大家做出解释:
appid:公众号id
mch_id: 商户号
nonce_str: 随机字符串,长度32以内
sign :签名,最坑的地方,玩微信支付不遇到这个签名错误,都不好意思说自己开发过支付
body:商品描述
out_trade_no: 商户订单号 和随机字符串一样,长度32以内。
total_fee:总价,单位 分
spbill_create_ip : 终端IP
notify_url:回调url 这个就是我们总结出来2步中11的那个步骤,通知微信的处理结果的作用。
trade_type:交易类型,我们是公众号,传 JSAPI 就好了
openid 这里注意,网页支付一定要传这个,我当时就忘记传了,一直导致签名错误。
最后把这些东西组装成xml就好了:
给大家贴代码吧:
String openid = open_id; String appid = APP_ID; String mch_id = MCH_ID; String nonce_str = RandomStringGenerator.getRandomStringByLength(32); String bodyDesc = body; String out_trade_no = RandomStringGenerator.getRandomNumber(18); String total_fee = totalFee; String spbill_create_ip = spbillCreateIp; String notify_url = notifyUrl; String trade_type = "JSAPI"; Map<String, Object> map = new HashMap<String, Object>(); map.put("openid", openid); map.put("appid", appid); map.put("mch_id", mch_id); map.put("nonce_str", nonce_str); map.put("body", bodyDesc); map.put("out_trade_no", out_trade_no); map.put("total_fee", total_fee); map.put("spbill_create_ip", spbill_create_ip); map.put("notify_url", notify_url); map.put("trade_type", trade_type);
把需要的参数放到map中去,因为sign这个需要用到签名算法:微信文档中有,给出链接https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
还是贴代码:
public static String getSign(Map<String,Object> map){ ArrayList<String> list = new ArrayList<String>(); for(Map.Entry<String,Object> entry:map.entrySet()){ if(entry.getValue()!=""){ list.add(entry.getKey() + "=" + entry.getValue() + "&"); } } int size = list.size(); String [] arrayToSort = list.toArray(new String[size]); Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER); StringBuilder sb = new StringBuilder(); for(int i = 0; i < size; i ++) { sb.append(arrayToSort[i]); } String result = sb.toString(); result += "key=" + key; Log.error(SignUtil.class, "getSign", result); result = MD5.MD5Encode(result).toUpperCase(); return result; }
注意这里用了一个key,这个key非常非常重要,key设置路径:微信商户平台(pay.weixin.qq.com)–>账户设置–>API安全–>密钥设置
这个key必须参与签名。
把上面参数封装成PrePay对象
public class XmlUtil { public static String toXml(PrePay pay){ XStream stream = new XStream(new Xpp3Driver(new NoNameCoder())); stream.alias("xml", pay.getClass()); return stream.toXML(pay); }
最终得到的上传的参数是这样的:以下包含信息参数全用xxxxxx代替
<xml> <openid>xxxxxxxx</openid> <appid>xxxxxxxx</appid> <mch_id>xxxxxxxx</mch_id> <nonce_str>xxxxxxxx</nonce_str> <body>testpay</body> <out_trade_no>xxxxxxxx</out_trade_no> <total_fee>1</total_fee> <spbill_create_ip>xxxxxxx</spbill_create_ip> <notify_url>xxxxxxxxxxx</notify_url> <trade_type>JSAPI</trade_type> <sign>xxxxxxxxxxx</sign></xml>
好了post提交这个参数到 统一下单接口:https://api.mch.weixin.qq.com/pay/unifiedorder
如果签名没有错误,服务器就会返回:
<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg><appid><![CDATA[xxxxxxxx]]></appid><mch_id><![CDATA[xxxxxxxxxx]]></mch_id><nonce_str><![CDATA[ClB4m3CRcfg9cS7v]]></nonce_str><sign><![CDATA[xxxxxxxxxxx]]></sign><result_code><![CDATA[SUCCESS]]></result_code><prepay_id><![CDATA[xxxxxxxx]]></prepay_id><trade_type><![CDATA[JSAPI]]></trade_type></xml>
大家擦亮亮眼睛,仔细看 prepay_id 好啦!!!解析xml获取prepay_id就算完事了,各位看官又到了喝茶的时间了,请各位前去喝茶。
好啦!差不多回来,开始最重要的一步,去前台调用支付:
以下就是微信需要我们传入的参数了:
appId :这个不解释了,全局都是同一个
timeStamp: 这个时间戳自己传过来就好了
nonceStr : 这个随机字符 自己传过来好了
package : 注意注意注意 这里 prepay_id= 一定要加这个
signType : MD5
paySign :这里把上面的参数按照获取prepay_id 一样的流程去签名就好了!
function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId":"wx2421b1c4370ec43b", //公众号名称,由商户传入 "timeStamp":"1395712654", //时间戳,自1970年以来的秒数 "nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串 "package":"prepay_id=u802345jgfjsdfgsdg888", "signType":"MD5", //微信签名方式: "paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 }, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。 } ); }if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); }}else{ onBridgeReady();}
至此微信支付结束。。。
认为结束了???太年轻,微信还要我们对处理结果进行确认呢!!不然之前那个notify_url 不是很鸡肋,白传了么!!看文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
微信返回的是流对象,我们转成map就可以了:
public static Map<String,Object> requestToXml(HttpServletRequest request) throws DocumentException, IOException{ SAXReader reader = new SAXReader(); Document doc = reader.read(request.getInputStream()); Element root = doc.getRootElement(); Map<String,Object> map = new HashMap<String,Object>(); List<Element> elements = root.elements(); for(Element e:elements){ map.put(e.getName(), e.getData()); } return map; }
这里就获取到了微信传给我们的支付包含的所有数据:我这里把他放到map里面了。
(本人做法不推荐,只是用于测试:)
public static String replyWx(HttpServletRequest request) throws DocumentException, IOException { Map<String, Object> map = XmlUtil.requestToXml(request); String xml = ""; if (map != null && "SUCCESS" .equalsIgnoreCase(map.get("return_code").toString())) { Map<String, Object> replyMap = new HashMap<String, Object>(); replyMap.put("return_code", "SUCCESS"); replyMap.put("return_msg", "OK"); xml = XmlUtil.mapToXml(replyMap); } else { Map<String, Object> replyMap = new HashMap<String, Object>(); replyMap.put("return_code", "FAIL"); replyMap.put("return_msg", "签名失败"); xml = XmlUtil.mapToXml(replyMap); } return xml; }
这里我只是简单的返回成功和失败,大家做的时候一定一定要根据微信推荐的做法来,我推荐一个思路吧,先把sign取出来,然后置sign为空,让剩下的参数去签名,得到的签名和sign对比,如果一致,通过,如果不一致,那么sign被篡改,返回失败。
最后把这个xml返回给wx就好了!!
至此,支付结束,谢谢大家拜读!!!!
- java微信开发之微信支付
- iOS开发支付之微信支付
- 微信支付之认识微信支付开发
- 微信支付开发
- 微信支付开发
- 微信支付开发
- 微信支付开发
- 微信支付开发
- 微信支付开发
- 微信公众号开发之微信支付开发
- 支付开发(一)----微信支付开发之H5非微信浏览器支付JAVA版
- Java微信开发之公众号支付接口
- Java微信支付开发之查询订单
- Java微信支付开发之关闭订单
- Android开发之微信支付,支付宝支付
- java --微信支付
- java微信支付
- java 微信支付
- Android使用网络技术实现新闻客户端实验
- vue.js的基础学习
- Java IO学习笔记四
- 最少步数
- vim使用点滴记录
- java微信开发之微信支付
- Postgers
- linux平台查看CPU、内存消耗降序的进程列表
- mysql 索引与联合索引详解
- 判断一棵二叉树是否为另一棵二叉树的子树,Python实现
- 希尔排序(Java实现)
- PostgreSQL 连接问题 FATAL: no pg_hba.conf entry for host
- CSS 创建水平菜单
- 第六讲 静态代理