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

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

浏览量:3977 时间: 来源:宇霖

目录

一、开发前的准备工作

二、接入微信企业付款到零钱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.Mapjava.lang.String, java.lang.String     * @throws     * @description 查询企业付款     * @params [partnerTradeNo]     */    @Override    public MapString, String getTransferInfo(String partnerTradeNo) throws IOException {        // 加载API证书        SSLContext sslContext = initSSLContext();        SSLConnectionSocketFactory sslSkF = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1"},                null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());        SortedMapString, 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 = Map            MapString, 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@Slf4jpublic 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, SortedMapString, 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(SortedMapString, 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 MapString, Object createSign2(String result, String key) throws IOException {        SortedMapString, Object map = new TreeMap(transferXmlToMap(result));        MapString, 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(SortedMapString, 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(SortedMapString, 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 异常输出        }        // 解析 DOM        Element 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(ListElement children) {        StringBuffer sb = new StringBuffer();        if (!children.isEmpty()) {            IteratorElement it = children.iterator();            while (it.hasNext()) {                Element e = (Element) it.next();                String name = e.getName();                String value = e.getTextNormalize();                ListElement 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,单位:元

 

版权声明

即速应用倡导尊重与保护知识产权。如发现本站文章存在版权问题,烦请提供版权疑问、身份证明、版权证明、联系方式等发邮件至197452366@qq.com ,我们将及时处理。本站文章仅作分享交流用途,作者观点不等同于即速应用观点。用户与作者的任何交易与本站无关,请知悉。

产品经理

手机 : 13312967497

擅长 : 小程序流量变现

扫码领取礼包

热门模板

  • 头条
  • 搜狐
  • 微博
  • 百家
  • 一点资讯
  • 知乎