java微信开发之微信支付

来源:互联网 发布:搜狗微信 php 爬虫 编辑:程序博客网 时间:2024/05/23 18:55

**写在前面:
本人一直“奉行授人以鱼不如授人以渔”,本文主要是起一个引导的作用,注意一些很坑的地方。
微信支付,本人菜鸟花了2天时间弄出来,也算是有点成就感,所以特此做个记录
**
分割线—————————————————————————
正文开始:
先来看看微信官方流程图:
这里写图片描述
总结起来就是四个步骤:

  1. 点击支付按钮
  2. 微信拉起支付,输入密码
  3. 完成支付
  4. 通知微信服务器
    下面就是我做测试支付的图:
    点击支付按钮(后台一大堆业务逻辑处理,后面会讲):
    这里写图片描述
    微信拉起支付(这个调用微信自动完成的):
    这里写图片描述
    完成支付,通知微信(这个需要我们自己完成业务逻辑,后面会讲到)
    这里写图片描述

流程就是这样,其实总结出来,我们需要做的就是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步:

  1. 用户同意授权,获取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: 随意填写
    其他参数 照写
  2. 通过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就好了!!
至此,支付结束,谢谢大家拜读!!!!

原创粉丝点击