小程序 微信支付官方文档
https://pay./wiki/doc/api/wxa/wxa_api.php?chapter=7_11&index=2
https://pay./wiki/doc/apiv3/open/pay/chapter2_8_0.shtml
前提
在进行对接微信支付之前,我们首先需要将以下几点准备好:
申请APPID申请商户号小程序开通微信支付,绑定已经申请好的商户号。登录小程序后台(mp.)。点击左侧导航栏的微信支付,在页面中进行开通。(开通申请要求小程序已发布上线)
注意事项
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>mons</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: '/onLogin',data: {code: res.code}})} else {console.log('登录失败!' + res.errMsg)}}})
4. 小程序调起支付API
小程序调起支付数据签名字段列表:
举例如下:
paySign = MD5(appId=wxd678efh567hg6787&nonceStr=5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=wx033010242291fcfe0db70013231072&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());//这里直接使用当前用户的openidorder.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为微信返回的xmlString 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. 项目地址
微信支付案例
具体代码大家自行查看,相关商户号没有给出,大家自行申请。