小程序 微信支付官方文档
https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_11&index=2
https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_0.shtml
前提
在进行对接微信支付之前,我们首先需要将以下几点准备好:
申请APPID申请商户号小程序开通微信支付,绑定已经申请好的商户号。登录小程序后台(mp.weixin.qq.com)。点击左侧导航栏的微信支付,在页面中进行开通。(开通申请要求小程序已发布上线)注意事项
appid必须为最后拉起收银台的小程序appid;mch_id为和appid成对绑定的支付商户号,收款资金会进入该商户号;trade_type请填写JSAPI;openid为appid对应的用户标识,即使用wx.login接口获得的openid。本文主要记录后端步骤,前端步骤无非就是获取后端数据然后调用提供的API进行支付,大家可自行查看官方文档。
1. 整体流程
小程序支付的交互图如下:清晰图片可以到官网查看.

商户系统和微信支付系统主要交互:
1、小程序内调用登录接口,获取到用户的openid,api参见公共api【小程序登录API】
2、商户server调用支付统一下单,api参见公共api【统一下单API】
3、商户server调用再次签名,api参见公共api【再次签名】
4、商户server接收支付通知,api参见公共api【支付结果通知API】
5、商户server查询支付结果,如未收到支付通知的情况,商户后台系统可调用【查询订单API】 (查单实现可参考:支付回调和查单实现指引)
2. 后端项目搭建
大致了解流程之后,我们就可以进行代码的编写了,首先大家自行创建一个Spring Boot项目即可。这里就不演示了,大家自行根据自己的需求搭建即可,需要注意的是我们需要添加微信支付的sdk以及相关依赖。
 <!--微信支付请求-->        <dependency>            <groupId>com.github.wxpay</groupId>            <artifactId>wxpay-sdk</artifactId>            <version>0.0.3</version>        </dependency>        <dependency>            <groupId>commons-codec</groupId>            <artifactId>commons-codec</artifactId>            <version>1.13</version>        </dependency>        <dependency>            <groupId>org.jdom</groupId>            <artifactId>jdom</artifactId>            <version>1.1.3</version>        </dependency>        <dependency>            <groupId>com.thoughtworks.xstream</groupId>            <artifactId>xstream</artifactId>            <version>1.4.10</version>        </dependency>        <dependency>            <groupId>com.google.zxing</groupId>            <artifactId>core</artifactId>            <version>3.1.0</version>        </dependency>        <dependency>            <groupId>com.fasterxml.jackson.dataformat</groupId>            <artifactId>jackson-dataformat-avro</artifactId>        </dependency>        <dependency>            <groupId>org.apache.commons</groupId>            <artifactId>commons-lang3</artifactId>            <version>3.6</version>        </dependency>        <dependency>            <groupId>org.apache.httpcomponents</groupId>            <artifactId>httpclient</artifactId>            <version>4.5.13</version>        </dependency>        <dependency>            <groupId>commons-httpclient</groupId>            <artifactId>commons-httpclient</artifactId>            <version>3.1</version>        </dependency> 3. 获取open_id
我们在小程序端通过login方法获取code,然后调用我们编写的接口请求微信服务端获取open_id。
该步骤具体可以查看 微信小程序对接微信登录 。
wx.login({  success (res) {    if (res.code) {      //发起网络请求      wx.request({        url: 'https://example.com/onLogin',        data: {          code: res.code        }      })    } else {      console.log('登录失败!' + res.errMsg)    }  }}) 4. 小程序调起支付API
小程序调起支付数据签名字段列表:
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 | 
|---|---|---|---|---|---|
| 小程序ID | appId | 是 | String | wxd678efh567hg6787 | 微信分配的小程序ID | 
| 时间戳 | timeStamp | 是 | String | 1490840662 | 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间 | 
| 随机串 | nonceStr | 是 | String | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | 随机字符串,不长于32位。推荐随机数生成算法 | 
| 数据包 | package | 是 | String | prepay_id=wx2017033010242291fcfe0db70013231072 | 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=wx2017033010242291fcfe0db70013231072 | 
| 签名方式 | signType | 是 | String | MD5 | 签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致 | 
举例如下:
paySign = MD5(appId=wxd678efh567hg6787&nonceStr=5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=wx2017033010242291fcfe0db70013231072&signType=MD5&timeStamp=1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111) = 22D9B4E54AB1950F51E0649E8810ACD6
从官方文档给出的参数可以看出,小程序调用支付API,有一些参数需要我们从后端接口返回,所以我们来进行后端支付接口的编写。
5. 下单及回调接口
实现步骤
定义配置类 保存支付相关参数定义支付接口,组装数据发起请求小程序调用支付接口相关配置类,签名,随机字符串的代码在文末给出,本文就不贴出来了。
@RestController@RequestMapping(value = "/api/v1")public class WXPayController {    /**     * 下单     */    @RequestMapping(            value = "/weixin/payment",            method = RequestMethod.POST    )    public Map payment() {        Map<String, Object> map = new HashMap<>();        String money = "10";        String title = "商品名字";        try {            OrderInfo order = new OrderInfo();            order.setAppid(Configure.getAppID());            order.setMch_id(Configure.getMch_id());            order.setNonce_str(RandomStringGenerator.getRandomStringByLength(32));            order.setBody(title);            order.setOut_trade_no(RandomStringGenerator.getRandomStringByLength(32));            order.setTotal_fee(Integer.parseInt(money));     // 该金钱其实10 是 0.1元            order.setSpbill_create_ip("127.0.0.1");            order.setNotify_url(Configure.getNotify_url());            order.setTrade_type(Configure.getTrade_type());            //这里直接使用当前用户的openid            order.setOpenid("ohfYG5O2mdXZLoszDAWwqtodOZRM");            order.setSign_type("MD5");            //生成签名            String sign = Signature.getSign(order);            order.setSign(sign);            String result = HttpRequest.sendPost(Configure.getUrl(), order);            System.out.println(result);            XStream xStream = new XStream();            xStream.alias("xml", OrderReturnInfo.class);            OrderReturnInfo returnInfo = (OrderReturnInfo) xStream.fromXML(result);            // 二次签名            if ("SUCCESS".equals(returnInfo.getReturn_code()) && returnInfo.getReturn_code().equals(returnInfo.getResult_code())) {                SignInfo signInfo = new SignInfo();                signInfo.setAppId(Configure.getAppID());                long time = System.currentTimeMillis() / 1000;                signInfo.setTimeStamp(String.valueOf(time));                signInfo.setNonceStr(RandomStringGenerator.getRandomStringByLength(32));                signInfo.setRepay_id("prepay_id=" + returnInfo.getPrepay_id());                signInfo.setSignType("MD5");                //生成签名                String sign1 = Signature.getSign(signInfo);                Map<String, String> payInfo = new HashMap<>();                payInfo.put("timeStamp", signInfo.getTimeStamp());                payInfo.put("nonceStr", signInfo.getNonceStr());                payInfo.put("package", signInfo.getRepay_id());                payInfo.put("signType", signInfo.getSignType());                payInfo.put("paySign", sign1);                map.put("status", 200);                map.put("msg", "统一下单成功!");                map.put("data", payInfo);                // 此处可以写唤起支付前的业务逻辑                // 业务逻辑结束 回传给小程序端唤起支付                return map;            }            map.put("status", 500);            map.put("msg", "统一下单失败!");            map.put("data", null);            return map;        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    /**     * 微信小程序支付成功回调函数     */    @RequestMapping(value = "/weixin/callback")    public void wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {        BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream()));        String line = null;        StringBuilder sb = new StringBuilder();        while ((line = br.readLine()) != null) {            sb.append(line);        }        br.close();        //sb为微信返回的xml        String notityXml = sb.toString();        String resXml = "";        System.out.println("接收到的报文:" + notityXml);        Map map = PayUtil.doXMLParse(notityXml);        String returnCode = (String) map.get("return_code");        if ("SUCCESS".equals(returnCode)) {            //验证签名是否正确            Map<String, String> validParams = PayUtil.paraFilter(map);  //回调验签时需要去除sign和空值参数            String validStr = PayUtil.createLinkString(validParams);//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串            String sign = PayUtil.sign(validStr, Configure.getKey(), "utf-8").toUpperCase();//拼装生成服务器端验证的签名            // 因为微信回调会有八次之多,所以当第一次回调成功了,那么我们就不再执行逻辑了            //根据微信官网的介绍,此处不仅对回调的参数进行验签,还需要对返回的金额与系统订单的金额进行比对等            if (sign.equals(map.get("sign"))) {                /**此处添加自己的业务逻辑代码start**/                // bla bla bla....                /**此处添加自己的业务逻辑代码end**/                //通知微信服务器已经支付成功                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";            } else {                System.out.println("微信支付回调失败!签名不一致");            }        } else {            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"                    + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";        }        System.out.println(resXml);        System.out.println("微信支付回调数据结束");        BufferedOutputStream out = new BufferedOutputStream(                response.getOutputStream());        out.write(resXml.getBytes());        out.flush();        out.close();    }} 6. 项目地址
微信支付案例
具体代码大家自行查看,相关商户号没有给出,大家自行申请。