接入微信公众号支付

官方文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1

官方的一张图
这里写图片描述

我们只需要开发图中红色的部分。

1.生成图文消息链接或二维码

网上随便找个网站就行,例如草料

2.生成商户订单,调用统一下单API

调用统一下单API必须传用户的openid,openid是每个用户之于每个公众号的唯一标志。以下为获取的openid的步骤。获取openid的官方文档

a.设置网页授权回调域名

在以下路径”开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”进行配置。

b.构造授权链接给用户去访问

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
各个参数的顺序不能改变
- APPID 公众号唯一标识
- REDIRECT_URI 授权后重定向的回调链接地址,请使用urlEncode对链接进行处理(请不要直接再此地址直接调起微信支付)。
- SCOPE 应用授权作用域,有两种方式
- snsapi_base 不会弹出要求用户授权页面,静默的获得code(只能获得openid)
- snsapi_userinfo 会弹出验证页面,可以获得用户公开信息
- STATE 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节

如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。

c.根据code获得access_token,openid,refresh_token等

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

  • APPID 公众号唯一标识
  • SECRET 公众号的唯一凭证密钥(此密钥不应给用户接触,所有此链接应在服务器发起请求
  • CODE 上一步中获得的code(注意,每个code只能用一次

微信返回数据

  • access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
  • expires_in access_token接口调用凭证超时时间,单位(秒)
  • refresh_token 用户刷新access_token
  • openid 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
  • scope 用户授权的作用域,使用逗号(,)分隔

如果只要获得用户的openid的,当这一步就已经结束了。

d.调用统一下单API

官方文档

调用这个API可以自己根据文档要求构造请求,也可以直接只用微信封装好的sdk,以下介绍使用sdk的方法。

通过maven安装sdk

<dependency>
    <groupId>com.github.wxpay</groupId>
    <artifactId>wxpay-sdk</artifactId>
    <version>0.0.3</version>
</dependency>
//这个官方demo的代码,调用统一下单接口
public static void main(String[] args) throws Exception {
        MyConfig config = new MyConfig();
        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", "");
        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", "NATIVE");  // 此处指定为扫码支付
        data.put("product_id", "12");

        try {
            Map<String, String> resp = wxpay.unifiedOrder(data);
            System.out.println(resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
 }

把接口返回的数据取出需要的,然后进行签名,签名的Key为之前配置过的。

    Map<String, String> result = new HashMap<>(6);
    result.put("appId",config.getAppID());
    result.put("timeStamp",String.valueOf(new Date().getTime()));
    result.put("nonceStr",resp.get("nonce_str"));
    result.put("package","prepay_id="+resp.get("prepay_id"));
    result.put("signType","MD5");
    result.put("paySign",WXPayUtil.generateSignature(result,config.getKey()));  

签完名将数据传递给需要调起支付的前端页面。

JS调起微信支付

注意,调起支付的页面必须是在配置的支付授权目录的路径下的,不然调起支付失败。

 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(result);
 }
 function onBridgeReady(t) {
 //t为后台传过来的参数
     WeixinJSBridge.invoke(
         'getBrandWCPayRequest', t,
         function (res) {
             if (res.err_msg == "get_brand_wcpay_request:ok") {
                 window.location.href="${pageContext.request.contextPath}/result.htm?msg=success"
             }     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。
        });
 }

后台接收微信的异步回调

官方文档

一定要返回给微信已经收到通知的回复,一定要避免重复通知带来的业务逻辑错误。
微信通知是xml格式的数据。所以要先读出来,然后用密钥验证一下签名。

        InputStreamReader isr=new InputStreamReader(request.getInputStream());
        BufferedReader br=new BufferedReader(isr);
        StringBuilder notifyData=new StringBuilder();
        String line = null;
        while ((line = br.readLine()) != null) {
            notifyData.append(line);
        }
         Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData.toString());  // 转换成map
        if(wxPay.isPayResultNotifySignatureValid(notifyMap)) {
            // 签名正确
            // 进行处理。
            // 注意特殊情况:订单已经退款,但收到了支付结果成功的通知,不应把商户侧订单状态从退款改成支付成功
            if("SUCCESS".equals(notifyMap.get("result_code"))){

            }
        }else {
            // 签名错误,如果数据里没有sign字段,也认为是签名错误

        }

如果要展示支付结果的时候微信的通知还没来,可以主动的进行查询

 public static void main(String[] args) throws Exception {

        MyConfig config = new MyConfig();
        WXPay wxpay = new WXPay(config);

        Map<String, String> data = new HashMap<String, String>();
        data.put("out_trade_no", "2016090910595900000012");

        try {
            Map<String, String> resp = wxpay.orderQuery(data);
            System.out.println(resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
posted @ 2017-08-07 15:07  A_yes  阅读(285)  评论(0编辑  收藏  举报