目录

一、开发前的准备工作

二、接入微信企业付款到零钱API

1)接入「企业付款到零钱」API

2)接入「查询企业付款」API

三、开发过程的参数封装以及工具类封装

四、调试注意事项汇总


一、开发前的准备工作

  1. 前往商户平台开通「企业付款到零钱」。
  2. 配置好API密钥和生成API证书。

「企业付款到零钱」介绍:

开通注意事项见下图。详情请戳:企业付款场景介绍&操作指导

二、接入微信企业付款到零钱API

先附上微信官方文档:企业付款到零钱  & 查询企业付款

1)接入「企业付款到零钱」API

API 接入注意事项参见下方截图:

嗯,浏览官网文档后,相信应该已经找到感觉,哪怕一丁点都好!
接下来,总体梳理一下编码思路:

  • 读取对接必需配置项:appid & 商户号mch_id & 商户API密钥mchKey
  • 读取微信「企业付款到零钱」接口URL配置项
  • SSL加载API证书
  • 组装「企业付款到零钱」接口所需的请求参数
  • 按照既定规则生成商户订单号
  • 生成签名
  • 正式请求「企业付款到零钱」API
  • 接收API响应结果,处理相关业务逻辑

2)接入「查询企业付款」API

嗯,还是梳理编码步骤:

  • 读取对接必需配置项:appid & 商户号mch_id & 商户API密钥mchKey
  • 读取微信「查询企业付款」接口URL配置项
  • SSL加载API证书
  • 组装「查询企业付款」接口所需的请求参数(此处商户订单号跟付款API使用的商户订单号保持一致)
  • 生成签名
  • 正式请求「查询企业付款」API
  • 接收API响应结果,处理相关业务逻辑

   /*** @return java.util.Map<java.lang.String, java.lang.String>* @throws* @description 查询企业付款* @params [partnerTradeNo]*/@Overridepublic Map<String, String> getTransferInfo(String partnerTradeNo) throws IOException {// 加载API证书SSLContext sslContext = initSSLContext();SSLConnectionSocketFactory sslSkF = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1"},null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());SortedMap<String, Object> parameters = new TreeMap<>();// 组装请求参数parameters.put("partner_trade_no", partnerTradeNo);parameters.put("nonce_str", weChatUtils.gen32RandomString());parameters.put("appid", wxEpProperties.getAppid());parameters.put("mch_id", wxEpProperties.getMchId());// 生成签名String sign = weChatUtils.createSign(parameters, wxEpProperties.getMchKey());parameters.put("sign", sign);try {// 查询企业付款 响应结果=> Xml格式String entPaymentQueryRes = weChatUtils.executeHttpPost(wxEpProperties.getGetTransferInfoUrl(), parameters, sslSkF);log.info("WeChatEntPaymentServiceImpl.getTransferInfo ======== 查询企业付款响应结果:[{}] ======== ", entPaymentQueryRes);// XML => MapMap<String, String> resInfoMap = weChatUtils.transferXmlToMap(entPaymentQueryRes);log.info("WeChatEntPaymentServiceImpl.getTransferInfo ======== 查询企业付款响应结果Map结构:[{}] ======== ", resInfoMap.toString());return resInfoMap;} catch (Exception e) {log.error("WeChatEntPaymentServiceImpl.getTransferInfo ======== 查询企业付款发生异常 ======== ");throw new CommonBusinessException("查询企业付款失败!");}}

​​​​

三、开发过程的参数封装以及工具类封装

import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;/*** @Description 微信生态常用工具方法* @Author blake* @Date 2018/12/11 下午4:15* @Version 1.0*/
@Component
@Slf4j
public class WeChatUtils {public String getRemoteHost(javax.servlet.http.HttpServletRequest request) {String ip = request.getHeader("x-forwarded-for");if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");log.info("WeChatUtils.getRemoteHost ======= 第一处Value:[{}] ======== ",ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");log.info("WeChatUtils.getRemoteHost ======= 第二处Value:[{}] ======== ",ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();log.info("WeChatUtils.getRemoteHost ======= 第三处Value:[{}] ======== ",ip);}return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;}/*** 执行 POST 方法的 HTTP 请求** @param url* @param parameters* @param sslsf* @return* @throws IOException*/public String executeHttpPost(String url, SortedMap<String, Object> parameters, SSLConnectionSocketFactory sslsf)throws IOException {HttpClient client = null;if (Objects.isNull(sslsf)) {client = HttpClients.createDefault();} else {client = HttpClients.custom().setSSLSocketFactory(sslsf).build();}HttpPost request = new HttpPost(url);request.setHeader("Content-type", "application/xml");request.setHeader("Accept", "application/xml");request.setEntity(new StringEntity(transferMapToXml(parameters), "UTF-8"));HttpResponse response = client.execute(request);return readResponse(response);}/*** 执行 GET 方法的 HTTP 请求** @param url* @return* @throws IOException*/public String executeHttpGet(String url) throws IOException {HttpClient client = HttpClients.createDefault();HttpGet request = new HttpGet(url);request.setHeader("Content-type", "application/xml");request.setHeader("Accept", "application/xml");HttpResponse response = client.execute(request);return readResponse(response);}/*** 第一次签名** @param parameters 数据为服务器生成,下单时必须的字段排序签名* @param key* @return*/public String createSign(SortedMap<String, Object> parameters, String key) {StringBuffer sb = new StringBuffer();Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)Iterator it = es.iterator();while (it.hasNext()) {Map.Entry entry = (Map.Entry) it.next();String k = (String) entry.getKey();Object v = entry.getValue();if (null != v && !"".equals(v)&& !"sign".equals(k) && !"key".equals(k)) {sb.append(k + "=" + v + "&");}}sb.append("key=" + key);return encodeMD5(sb.toString());}/*** 第二次签名** @param result 数据为微信返回给服务器的数据(XML 的 String),再次签名后传回给客户端(APP)使用* @param key    密钥* @return* @throws IOException*/public Map<String, Object> createSign2(String result, String key) throws IOException {SortedMap<String, Object> map = new TreeMap<>(transferXmlToMap(result));Map<String, Object> app = new HashMap<>();app.put("signType", "MD5");app.put("appId", map.get("appid"));app.put("nonceStr", map.get("nonce_str"));// 统一下单接口返回的prepay_id参数值String packageStr = "prepay_id=" + map.get("prepay_id");app.put("package", packageStr);// 当前时间戳app.put("timeStamp", Long.toString(new Date().getTime() / 1000));// 微信支付正式签名app.put("paySign", createSign(new TreeMap<>(app), key));return app;}/*** 验证签名是否正确** @return boolean* @throws Exception*/public boolean checkSign(SortedMap<String, Object> parameters, String key) throws Exception {String signWx = parameters.get("sign").toString();if (signWx == null) return false;parameters.remove("sign"); // 需要去掉原 map 中包含的 sign 字段再进行签名String signMe = createSign(parameters, key);return signWx.equals(signMe);}/*** 读取 request body 内容作为字符串** @param request* @return* @throws IOException*/public String readRequest(HttpServletRequest request) throws IOException {InputStream inputStream;StringBuffer sb = new StringBuffer();inputStream = request.getInputStream();String str;BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));while ((str = in.readLine()) != null) {sb.append(str);}in.close();inputStream.close();return sb.toString();}/*** 读取 response body 内容为字符串*/public String readResponse(HttpResponse response) throws IOException {BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));String result = new String();String line;while ((line = in.readLine()) != null) {result += line;}return result;}/*** 将 Map 转化为 XML** @param map* @return*/public String transferMapToXml(SortedMap<String, Object> map) {StringBuffer sb = new StringBuffer();sb.append("<xml>");for (String key : map.keySet()) {sb.append("<").append(key).append(">").append(map.get(key)).append("</").append(key).append(">");}return sb.append("</xml>").toString();}/*** 将 XML 转化为 map** @param strxml* @return* @throws JDOMException* @throws IOException*/public Map transferXmlToMap(String strxml) throws IOException {strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");if (null == strxml || "".equals(strxml)) {return null;}Map m = new HashMap();InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));SAXBuilder builder = new SAXBuilder();Document doc = null;try {doc = builder.build(in);} catch (JDOMException e) {throw new IOException(e.getMessage()); // 统一转化为 IO 异常输出}// 解析 DOMElement root = doc.getRootElement();List list = root.getChildren();Iterator it = list.iterator();while (it.hasNext()) {Element e = (Element) it.next();String k = e.getName();String v = "";List children = e.getChildren();if (children.isEmpty()) {v = e.getTextNormalize();} else {v = getChildrenText(children);}m.put(k, v);}//关闭流in.close();return m;}// 辅助 transferXmlToMap 方法递归提取子节点数据private String getChildrenText(List<Element> children) {StringBuffer sb = new StringBuffer();if (!children.isEmpty()) {Iterator<Element> it = children.iterator();while (it.hasNext()) {Element e = (Element) it.next();String name = e.getName();String value = e.getTextNormalize();List<Element> list = e.getChildren();sb.append("<" + name + ">");if (!list.isEmpty()) {sb.append(getChildrenText(list));}sb.append(value);sb.append("</" + name + ">");}}return sb.toString();}/*** 生成 32 位随机字符串,包含:数字、字母大小写** @return*/public String gen32RandomString() {char[] dict = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z','a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};StringBuffer sb = new StringBuffer();for (int i = 0; i < 32; i++) {sb.append(String.valueOf(dict[(int) (Math.random() * 36)]));}return sb.toString();}/*** MD5 签名** @param str* @return 签名后的字符串信息*/public String encodeMD5(String str) {try {MessageDigest messageDigest = MessageDigest.getInstance("MD5");byte[] inputByteArray = (str).getBytes();messageDigest.update(inputByteArray);byte[] resultByteArray = messageDigest.digest();return byteArrayToHex(resultByteArray);} catch (NoSuchAlgorithmException e) {return null;}}// 辅助 encodeMD5 方法实现private String byteArrayToHex(byte[] byteArray) {char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};char[] resultCharArray = new char[byteArray.length * 2];int index = 0;for (byte b : byteArray) {resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];resultCharArray[index++] = hexDigits[b & 0xf];}// 字符数组组合成字符串返回return new String(resultCharArray);}public static void main(String[] args) {}}

   /*** 初始化ssl.** @return the ssl context* @throws CommonBusinessException the wx pay exception*/public SSLContext initSSLContext() throws CommonBusinessException, IOException {if (StringUtils.isBlank(wxEpProperties.getMchId())) {throw new CommonBusinessException("请确保商户号mchId已设置");}// SpringBoot项目,API证书可直接放至类路径resources下InputStream resourceAsStream = new ClassPathResource(wxEpProperties.getCertPath()).getInputStream();byte[] bytes = IOUtils.toByteArray(resourceAsStream);ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);try {KeyStore keystore = KeyStore.getInstance("PKCS12");char[] partnerId2charArray = wxEpProperties.getMchId().toCharArray();keystore.load(byteArrayInputStream, partnerId2charArray);return SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build();} catch (Exception e) {throw new CommonBusinessException("商户密钥不匹配或证书文件有问题,请核实!");} finally {byteArrayInputStream.close();resourceAsStream.close();}}

/*** @Description 微信企业付款到零钱 基础配置数据项* @Author blake* @Date 2019-05-16 14:48* @Version 1.0*/
@Configuration  // SpringBoot读取配置项其中一种用法
public class WxEntPaymentProperties {/*** 设置商户号关联的appid*/@Value("${wechat.ent.payment.appid}")private String appid;/*** 商户号*/@Value("${wechat.ent.payment.mchid}")private String mchId;/*** 商户密钥*/@Value("${wechat.ent.payment.mchKey}")private String mchKey;/*** API证书存放路径*/@Value("${wechat.ent.payment.certPath}")private String certPath;/*** 企业付款到零钱接口Url*/@Value("${wechat.ent.payment.transfers}")private String transfersUrl;/*** 查询企业付款接口Url*/@Value("${wechat.ent.payment.gettransferinfo}")private String getTransferInfoUrl;
}

四、调试注意事项汇总

  • 想保证调试过程顺利的话,强烈建议将项目部署至linux服务器,且保证外网可访问
  • 调试金额amount必须保证:1 <= amount <= 5000,单位:元

企业付款到零钱「微信小程序别样发放红包」相关推荐

  1. 企业付款到零钱(微信小程序提现,用户提现到零钱)

    原创路径 : https://blog.csdn.net/qq_38439885/article/details/90677850 pom 增加 <dependency><group ...

  2. 「微信小程序免费辅导教程」24,基础内容组件icon的使用探索与7月26日微信公众平台的更新解读...

    转载于:https://www.cnblogs.com/sban/p/7242627.html

  3. 零钱模拟器微信小程序源码下载

    这是一款模拟器小程序 特点就是只要你插上充电器然后里面的数值就会自动往上涨 也就是相当于是零钱充值的一个模拟器 小编个人感觉还是挺好玩的 安装方法: 使用微信开发者工具打开源码 然后上传提交审核就可以 ...

  4. 微信小程序:微信零钱模拟器微信小程序源码下载查收充电器自动充钱

    这是一款模拟器小程序 特点就是只要你插上充电器然后里面的数值就会自动往上涨 也就是相当于是零钱充值的一个模拟器 小编个人感觉还是挺好玩的 安装方法: 使用微信开发者工具打开源码 然后上传提交审核就可以 ...

  5. 关于「微信小程序」背后的故事

    前天晚上,小编的朋友圈被一则消息刷屏. 微信要推出应用号,我们暂且叫它小程序. 2021年年初张小龙在微信公开课上的的只言片语引发了大家对Web应用的无限憧憬, 9月21日,它终于诞生了! 作为新媒体 ...

  6. 「前端开发者」如何把握住「微信小程序」这波红利?

    由于前两周一直在老家处理重要事情,虽然朋友圈被「微信小程序」刷爆了,但并没有时间深入了解. 昨天回广州之后,第一件事情就是把「微信小程序」相关的文章.开发文档.设计规范全部看了一遍,基本上明白了「微信 ...

  7. android红包功能程序,微信小程序实现发红包功能

    本文实例为大家分享了微信小程序实现发红包的具体代码,供大家参考,具体内容如下 目前此功能尚在内测,无法申请.此博文仅示例. 流程效果图: 图片1触发wx.sendBizRedPacket({})吊起图 ...

  8. 企业付款到零钱 java_微信支付商户如何开通企业付款到零钱?

    1.功能介绍 简介 企业付款提供由商户直接付钱至用户微信零钱的能力,支持平台操作及接口调用两种方式.具有免费.快速到账.灵活.安全等优点.商户可以使用企业付款,用于如:费用报销.员工福利.用户奖励等. ...

  9. 企业付款到零钱(微信)

    <?php /*代码部分*///企业付款到微信零钱,PHP接口调用方法 define("APPID", "wxe062425f740c30d8"); // ...

最新文章

  1. HDU-6290_奢侈的旅行(Dijstra+堆优化)
  2. idea分支如何刷新显示最新
  3. js解析json数组+java对象转json字符串
  4. Apache Ignite本机持久性,简要概述
  5. 字体Times New Roman
  6. 2018诺贝尔奖预测:美国11人,日本1人,中国无人入围
  7. 信息学奥赛一本通(1106:年龄与疾病)
  8. python anaconda安装不上_Anaconda3 2.4与python 3.5安装错误(程序条目未找到; Windows 10)...
  9. python3 super_python3的super详解
  10. [2013.8.29]马甲去重复 c++源码
  11. php伪协议漏洞_php伪协议利用文件包含漏洞
  12. Bailian2692 假币问题【暴力】
  13. UML类图几种关系的总结,泛化 = 实现 组合 聚合 关联 依赖
  14. 甘特图控件VARCHART XGantt如何开始使用
  15. 汇川技术小型PLC梯形图编程系列教程(1)小型PLC型号H123U简介
  16. qq发送消息时变成表情
  17. 微信公众号授权登录配置
  18. ins图片视频批量下载
  19. MathType安装时遇到不能删除xx字体时的解决方法
  20. 题解 UVA1449 【Dominating Patterns】

热门文章

  1. chatgpt赋能Python-python_pareto
  2. JAVA特殊容器JTablePane
  3. git安装、初次配置及密钥生成
  4. matlab面向对象编程之类的继承
  5. Python数据处理库pandas中的DataFrame数据结构简介
  6. 2021.2.28blog补录
  7. html中表单的action属性作了什么?
  8. Stata:短期事件研究法(Event_Study)教程
  9. FeignClient Get请求参数问题
  10. 我的十年编程路 2019年篇