微信发放红包接口(java)

微信官方文档:发放红包接口

代码前的准备:

1.开通现金红包功能

◆ 根据监管要求,新申请商户号使用现金红包需要满足两个条件:1、入驻时间超过90天 2、连续正常交易30天。 

2.拿到用户的openid

3.登录微信商户平台拿到:商户号+商户秘钥+商户证书 

接下来可以敲代码了,把文档中需要的请求参数 全部准备好。

4.随机字符串


    /**
     * 随机字符串的生成
     *
     * @return
     */
    public static String getNonce_str() {
        return UUID.randomUUID().toString().replace("-", "").toUpperCase();
    }

5.生成商户订单号:商户号+时间戳+4位随机数 我的商户号10位 时间戳 14  加上4位随机数 刚好符合28位

//商户订单号默认用商户号+时间戳+4位随机数
String mchBillno = model.getMchId()
                 + new SimpleDateFormat("yyyyMMddHHmmss").format(date)
                 + (int) ((Math.random() * 9 + 1) * 1000);

 因为 Math.random() 是随机生成0.0~1.0 前闭后开的随机数,为了保证是4位数,所以(Math.random() * 9 + 1) * 1000。

6.签名 签名算法 

签名生成的通用步骤如下:

第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。

特别注意以下重要规则:

  1. ◆ 参数名ASCII码从小到大排序(字典序);
  2. ◆ 如果参数的值为空不参与签名;
  3. ◆ 参数名区分大小写;
  4. ◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
  5. ◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段

第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。

以上是官方给出的步骤,然后我们一步一步来

6.1 设所有发送或者接收到的数据为集合M  这里最好单独写一个方法装参数集合,因为后面的签名步骤是一样的,调用查询接口,企业付款到零钱等需要签名的接口用得上。

    /**
     * 签名算法
     * 算法说明:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3
     * @param model
     * @return
     */
    private static String getSign(TransfersDto model) {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        //随机字符串
        paramMap.put("nonce_str", model.getNonceStr());
        //商户订单号
        paramMap.put("mch_billno", model.getMchBillno());
        //商户号
        paramMap.put("mch_id", model.getMchId());
        //公众账号appid
        paramMap.put("wxappid", model.getWxAppId());
        //商户名称
        paramMap.put("send_name", model.getSendName());
        //用户openid
        paramMap.put("re_openid", model.getReOpenId());
        //付款金额
        paramMap.put("total_amount", model.getTotalAmount());
        //红包发放总人数
        paramMap.put("total_num", model.getTotalNum());
        //红包祝福语
        paramMap.put("wishing", model.getWishing());
        //Ip地址
        paramMap.put("client_ip", model.getClientIp());
        //活动名称
        paramMap.put("act_name", model.getActName());
        //备注
        paramMap.put("remark", model.getRemark());
        //场景id 发放红包使用场景,红包金额大于200或者小于1元时必传,以外非必须。
        paramMap.put("scene_id", model.getSceneId());
        //活动信息 非必须参数。
        paramMap.put("risk_info", model.getRiskInfo());
        
        return WXUtil.getSign(paramMap,KEY);
    }

6.2  传入需要进行的签名的参数,注意官网对于签名的要求,都要满足,然后拿到签名。(此处用了商户秘钥)+md5对于参数进行了加密,保证数据安全。

 /**
     * 签名算法
     * 算法说明:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3
     * @param paramMap
     * @return
     */
    public static String getSign( Map<String, Object> paramMap,String key) {

        List<String> keys = new ArrayList(paramMap.keySet());
        //参数名ASCII码从小到大排序(字典序)
        Collections.sort(keys);
        String sortString = "";
        for (int i = 0; i < keys.size(); i++ )
        {
            String mapKey = keys.get(i);
            Object value = paramMap.get(mapKey);
            //参数的值为空不参与签名
            if (value != null) {
                sortString = sortString + mapKey + "=" + value + "&";
            }
        }
        //去掉最后一个 "&" 符号
        sortString = sortString.substring(0, sortString.length() -1);
        //拼接API密钥
        String mysign = sortString + "&key=" + key;
        //进行MD5运算,再将得到的字符串所有字符转换为大写
        String sign = DigestUtils.md5Hex(mysign).toUpperCase();
        return sign;
    }

 7.封装请求参数,下面的参数中上面介绍没有用到的属性,就是需要你手动设置的,如多少钱啊,openid,商户号,红包祝福语等等。

 //封装请求参数
            StringBuilder reqXmlStr = new StringBuilder();
            reqXmlStr.append("<xml>");
            reqXmlStr.append("<sign><![CDATA[" + model.getSign() + "]]></sign>");
            reqXmlStr.append("<mch_billno><![CDATA[" + model.getMchBillno() + "]]></mch_billno>");
            reqXmlStr.append("<mch_id><![CDATA[" + model.getMchId() + "]]></mch_id>");
            reqXmlStr.append("<wxappid><![CDATA[" + model.getWxAppId() + "]]></wxappid>");
            reqXmlStr.append("<send_name><![CDATA[" + model.getSendName() + "]]></send_name>");
            reqXmlStr.append("<re_openid><![CDATA[" + model.getReOpenId() + "]]></re_openid>");
            reqXmlStr.append("<total_amount><![CDATA[" + model.getTotalAmount() + "]]></total_amount>");
            reqXmlStr.append("<total_num><![CDATA[" + model.getTotalNum() + "]]></total_num>");
            reqXmlStr.append("<wishing><![CDATA[" + model.getWishing() + "]]></wishing>");
            reqXmlStr.append("<client_ip><![CDATA[" + model.getClientIp() + "]]></client_ip>");
            reqXmlStr.append("<act_name><![CDATA[" + model.getActName() + "]]></act_name>");
            reqXmlStr.append("<remark><![CDATA[" + model.getRemark() + "]]></remark>");
            reqXmlStr.append("<nonce_str><![CDATA[" + model.getNonceStr() + "]]></nonce_str>");
            if (model.getNonceStr() != null) {
                reqXmlStr.append("<scene_id><![CDATA[" + model.getSceneId() + "]]></scene_id>");
            }
            if (model.getRiskInfo() != null) {
                reqXmlStr.append("<risk_info>" + model.getRiskInfo() + "</risk_info>");
            }
            reqXmlStr.append("</xml>");
8.加载证书 证书的位置在测试时可以放在本地,路径给绝对路径,如果要正式使用前面的商户秘钥+商户证书 都是不能直接暴露在代码里面的,这涉及商户号金额,权限等操作,十分重要,十分重要,十分重要,切记找个安全的地方。
    /**
     * 加载证书
     *
     * @throws KeyStoreException
     * @throws IOException
     * @throws CertificateException
     * @throws NoSuchAlgorithmException
     * @throws UnrecoverableKeyException
     * @throws KeyManagementException
     */
    private static void loadCertificate(String mchId, String certificatePath) throws KeyStoreException, IOException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {

        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        // 读取证书
        FileInputStream fileInputStream = new FileInputStream(new File("apiclient_cert.p12"));

        try {
            // 加载证书密码,默认为商户ID
            keyStore.load(fileInputStream, mchId.toCharArray());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            fileInputStream.close();
        }

        //信任自签名证书
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore,
                mchId.toCharArray()).build();
        SSLConnectionSocketFactory sslcsf = new SSLConnectionSocketFactory(sslcontext,
                new String[] {"TLSv1"}, null,new DefaultHostnameVerifier());

        httpClient = HttpClients.custom().setSSLSocketFactory(sslcsf).build();
    }

9.请求接口

 TRANS_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack"; 

reqXmlStr:步骤7封装好的请求数据

   HttpPost httpPost = new HttpPost(transUrl);
        try {
            // 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
            StringEntity postEntity = new StringEntity(reqXmlStr, "UTF-8");
            httpPost.addHeader("Content-Type", "text/xml");
            httpPost.setEntity(postEntity);

            //根初始化requestConfig 连接超时时间,默认10秒 传输超时时间,默认30秒
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(30000).build();
            httpPost.setConfig(requestConfig);
            HttpResponse response = httpClient.execute(httpPost);
            try {
                HttpEntity entity = response.getEntity();
                result = EntityUtils.toString(entity, "UTF-8");
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            httpPost.abort();
        }
10.返回的结果是string型的xml数据

怎么把xml文件的内容拿出来存储到数据库,请移步这里:怎么把微信红包返回的xml字符串值取出来

 

 

 

 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值