官方文档
微信官方提供的SDK和demo,下载地址
springboot整合微信支付(完整)
springboot 使用 RestTemplate 携带 微信API证书发送请求实现企业付款到零钱/提现/微信退款等场景
这个是别人把官方的SDK放到了Maven仓库里,也可以使用
<dependency><groupId>com.github.tedzhdz</groupId><artifactId>wxpay-sdk</artifactId><version>3.0.10</version></dependency>
第一步:把官方提供的SDK复制到自己的项目中

这个时候,里面有地方会缺少依赖,我们需要引入对应的依赖。一般就缺少httpclient依赖,
<!--微信支付请求里需要用到的依赖,是http请求相关的--><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId></dependency>
第二步,根据要求生成XML的请求体参数
1.定义一个JAVA实体类
package com.tj.qywx.domain;import lombok.Data;import java.io.Serializable;/*** 向员工付款的XML数据字段*/@Datapublic class WxPayManXml implements Serializable {//商户订单号private String partner_trade_no;//用户openidprivate String userid;//金额private Integer amount;//向员工付款说明信息。必填private String desc;//审批编号private String approval_number;//项目名称private String act_name;private static final long serialVersionUID = 1L;}
2、定义map填入请求需要的数据
//1.定义MapString mchId = baseNumvarService.getMchId();HashMap<String, String> xmlMap = MapUtils.newHashMap();xmlMap.put("appid", baseNumvarService.getCorpid());xmlMap.put("mch_id", mchId);xmlMap.put("nonce_str", WXPayUtil.generateNonceStr());xmlMap.put("partner_trade_no", wxPayManXml.getPartner_trade_no());xmlMap.put("openid", wxUserlistService.getOpenidByUserid(wxPayManXml.getUserid()));xmlMap.put("amount", String.valueOf(wxPayManXml.getAmount()));xmlMap.put("desc", wxPayManXml.getDesc());xmlMap.put("ww_msg_type", "APPROVAL_MSG");//进行企业微信签名(不是所有字段都需要参与签名,根据文档要求)log.info("企业微信签名的原始数据:{}", xmlMap);//微信官方工具默认最后拼接的是“key”,但是企业微信签名算法最后需要拼接的是“secret”,这个bug有点坑,这个地方自己要特殊处理一下String workwxSign = MyWXPayUtils.getSignByMD5(xmlMap, baseNumvarService.getSecretPay(), "secret");xmlMap.put("workwx_sign", workwxSign);xmlMap.put("act_name", wxPayManXml.getAct_name());xmlMap.put("approval_number", wxPayManXml.getApproval_number());xmlMap.put("spbill_create_ip", "192.168.0.1");//进行微信支付签名log.info("微信支付签名的原始数据:{}", xmlMap);String sign = WXPayUtil.generateSignature(xmlMap, baseNumvarService.getAPIv2());xmlMap.put("sign", sign);
处理签名的方法(微信官方提供的sdk里面有些小坑,要注意)
/*** 签名算法,* 第一步: 对参数按照key=value的格式,并按照参数名ASCII字典序排序如下* 第二步:拼接企业微信支付应用secret(参见企业微信管理端支付应用页面):** @param data* @param keyValue* @param key* @return*/public static String getSignByMD5(final Map<String, String> data, String keyValue, String key) {if (key == null) {key = "key";}//排序Set<String> keySet = data.keySet();String[] keyArray = keySet.toArray(new String[keySet.size()]);Arrays.sort(keyArray);StringBuilder sb = new StringBuilder();for (String k : keyArray) {if (k.equals(WXPayConstants.FIELD_SIGN)) {continue;}if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名sb.append(k).append("=").append(data.get(k).trim()).append("&");}sb.append(key).append("=").append(keyValue);System.out.println("微信签名组合sb = " + sb);//md5加密转大写return DigestUtils.md5DigestAsHex(sb.toString().getBytes()).toUpperCase();}
3、注意,这个里面WXPayUtil是官方提供的SDK里工具类,
//进行企业微信签名(不是所有字段都需要参与签名,根据文档要求)log.info("企业微信签名的原始数据:{}", xmlMap);//微信官方工具默认最后拼接的是“key”,但是企业微信签名算法最后需要拼接的是“secret”,这个bug有点坑,这个地方自己要特殊处理一下String workwxSign = MyWXPayUtils.getSignByMD5(xmlMap, baseNumvarService.getSecretPay(), "secret");xmlMap.put("workwx_sign", workwxSign);//进行微信支付签名log.info("微信支付签名的原始数据:{}", xmlMap);String sign = WXPayUtil.generateSignature(xmlMap, baseNumvarService.getAPIv2());xmlMap.put("sign", sign);
4、把Map转成XML字符串,使用的还是微信官方提供的工具类
//2.Map转成XMLString PostXml = WXPayUtil.mapToXml(xmlMap);
5、执行请求,再吧请求结果转换成map,方便使用
//3.请求地址String url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/paywwsptrans2pocket";// 执行请求String xmlString = MyWXPayUtils.requestWithCert(mchId, this.path, url, PostXml);log.info("付款后返回的Xml信息:{}", xmlString);//4.xml转mapMap<String, String> resultMap = WXPayUtil.xmlToMap(xmlString);log.info("付款后返回的转换后的map信息:{}", resultMap);
附录:
http请求封装
/*** 微信小程序退款订单(需要双向证书)--微信支付需要双向证书* @param mchId 商户号* @param certPath 证书文件夹路径,全路径* @param url 请求地址* @param data xml请求参数数据* @return* @throws Exception*/public static String requestWithCert(String mchId, String certPath, String url, String data) throws Exception {//小程序退款需要调用双向证书的认证CloseableHttpClient httpClient = readCertificate(mchId, certPath);try {HttpPost httpost = new HttpPost(url); // 设置响应头信息httpost.addHeader("Connection", "keep-alive");httpost.addHeader("Accept", "*/*");httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");httpost.addHeader("Host", "api.mch.weixin.qq.com");httpost.addHeader("X-Requested-With", "XMLHttpRequest");httpost.addHeader("Cache-Control", "max-age=0");httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");httpost.setEntity(new StringEntity(data, "UTF-8"));CloseableHttpResponse response = httpClient.execute(httpost);try {HttpEntity entity = response.getEntity();String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");EntityUtils.consume(entity);return jsonStr;} finally {response.close();}} catch (Exception e) {throw new RuntimeException(e);} finally {httpClient.close();}}
读取P12双向证书
/*** 读取p12双向证书** @param mchId 商户号,主要作为密码使用* @param certPath 证书文件位置* @return* @throws Exception*/@PostConstructprivate static CloseableHttpClient readCertificate(String mchId, String certPath) throws Exception {/*** 注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的*/KeyStore keyStore = KeyStore.getInstance("PKCS12");//P12文件目录 证书路径,这里需要你自己修改,linux下还是windows下的根路径String filepath = certPath;System.out.println("filepath->" + filepath);FileInputStream instream = new FileInputStream(filepath + "apiclient_cert.p12");try {keyStore.load(instream, mchId.toCharArray());//这里写密码..默认是你的MCHID(商户号)} finally {instream.close();}// Trust own CA and all self-signed certsSSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build();//这里也是写密码的// Allow TLSv1 protocol onlySSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, SSLConnectionSocketFactory.getDefaultHostnameVerifier());return HttpClients.custom().setSSLSocketFactory(sslsf).build();}
