微信公支付开发之现金红包

我们先来看看公众号发放现金红包的效果:
微信公支付开发之现金红包_第1张图片

需要调用商户平台的接口,接口发放规则如下:
1.发送频率限制——默认1800/min
2.发送个数上限——按照默认1800/min算
3.金额上限——根据传入场景id不同默认上限不同,可以在商户平台产品设置进行设置和申请,最大不大于4999元/个
4.其他的“量”上的限制还有哪些?——用户当天的领取上限次数,默认是10
5.如果量上满足不了我们的需求,如何提高各个上限?——金额上限和用户当天领取次数上限可以在商户平台进行设置
注意-红包金额大于200时,请求参数scene_id必传,参数说明见下文。
注意2-根据监管要求,新申请商户号使用现金红包需要满足两个条件:1、入驻时间超过90天 2、连续正常交易30天。

请求Url https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack
是否需要证书 证书及使用说明详见商户证书
请求方式 POST

请求数据示例:

<xml> 

<sign>sign> 

<mch_billno>mch_billno> 

<mch_id>mch_id> 

<wxappid>wxappid> 

<send_name>send_name> 

<re_openid>re_openid> 

<total_amount>total_amount> 

<total_num>total_num> 

<wishing>wishing> 

<client_ip>client_ip> 

<act_name>act_name> 

<remark>remark> 

<scene_id>scene_id> 

<consume_mch_id>consume_mch_id> 

<nonce_str>nonce_str> 

<risk_info>posttime%3d123123412%26clientversion%3d234134%26mobile%3d122344545%26deviceid%3dIOSrisk_info> 

xml> 

接口需要调用商户平台的证书,证书需要去商户平台下载:
微信公支付开发之现金红包_第2张图片

然后在接口中使用证书,首先我们新建一个WeixinSSL

@Component
public class WeiXinSSL {

    /**
     * 证书类型
     */
    @Value("${werchant.storekey}")
    private String storekey;

    /**
     * 文件路径
     */
    @Value("${werchant.ssLfile}")
    private String ssLfile;

    /**
     * 商户号
     */
    @Value("${werchant.merchantNumber}")
    private String merchantNumber;


    public String getStorekey() {
        return storekey;
    }

    public void setStorekey(String storekey) {
        this.storekey = storekey;
    }

    public String getSsLfile() {
        return ssLfile;
    }

    public void setSsLfile(String ssLfile) {
        this.ssLfile = ssLfile;
    }

    public String getMerchantNumber() {
        return merchantNumber;
    }

    public void setMerchantNumber(String merchantNumber) {
        this.merchantNumber = merchantNumber;
    }   
}

封装HttpClientSSL 类实现 https 请求加证书:

@Component
public class HttpClientSSL {

    @Autowired
    private WeiXinSSL weiXinSSL; 

    // 请求超时时间(毫秒) 5秒
    public static RequestConfig requestConfig;

    // 响应超时时间(毫秒) 60秒
    public static int HTTP_RESPONSE_TIMEOUT = 60 * 1000;

    // httpClient字符编码
    public static String encoding = "UTF-8";


    public static RequestConfig getRequestConfig() {
        return RequestConfig.custom().setConnectTimeout(5 * 1000)
                .setConnectionRequestTimeout(HTTP_RESPONSE_TIMEOUT).build();
    }


    public static void setRequestConfig(RequestConfig requestConfig) {
        HttpClientSSL.requestConfig = requestConfig;
    }

    /**
     * https请求伪造证书
     * @return
     */
    public CloseableHttpClient defaultSSLClient() {
        SSLContext sslContext = null;
        try {
            new SSLContextBuilder().loadTrustMaterial(null,new TrustStrategy(){
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType)
                        throws java.security.cert.CertificateException {
                    return false;
                }
            });
        } catch (NoSuchAlgorithmException | KeyStoreException e) {
            e.printStackTrace();
        }
        SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslContext);
        return HttpClients.custom().setSSLSocketFactory(factory).build();
    }



    /**
     * https请求加证书
     * @return
     */
    public CloseableHttpClient defaultSSLClientFile() {
        if (this.weiXinSSL == null){
            return this.defaultSSLClient();
        }
        FileInputStream inputStream = null;
        KeyStore keyStore = null;
        try {
            // ssl类型
            keyStore = KeyStore.getInstance(weiXinSSL.getStorekey());
            // ssl文件
            inputStream = new FileInputStream(weiXinSSL.getSsLfile());
            // 设置ssl密码
            keyStore.load(inputStream,weiXinSSL.getMerchantNumber().toCharArray());
        } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e1) {
            e1.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        SSLContext sslContext = null;
        try {
            sslContext = SSLContexts.custom().loadKeyMaterial(keyStore,weiXinSSL.getMerchantNumber().toCharArray()).build();
        } catch (UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
            e.printStackTrace();
        }

        SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslContext, new String[] { "TLSv1" }, null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        return HttpClients.custom().setSSLSocketFactory(factory).build();
    }

    /**
     * 封装发送请求的方法
     * @throws UnsupportedEncodingException
     */
    public String send(String url, String data, CloseableHttpClient closeableHttpClient)
            throws UnsupportedEncodingException {

        CloseableHttpClient client = closeableHttpClient;
        HttpPost httpPost = new HttpPost(URLDecoder.decode(url, encoding));
        httpPost.addHeader("Connection", "keep-alive");
        httpPost.addHeader("Accept", "*/*");
        httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        httpPost.addHeader("Host", "api.mch.weixin.qq.com");
        httpPost.addHeader("X-Requested-With", "XMLHttpRequest");
        httpPost.addHeader("Cache-Control", "max-age=0");
        httpPost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
        httpPost.setConfig(this.getRequestConfig());// 设置超时时间
        CloseableHttpResponse response = null;

        // 参数放入
        StringEntity entity = new StringEntity(data, encoding);
        entity.setContentEncoding(encoding);
        entity.setContentType("application/xml");
        httpPost.setEntity(entity);

        try {
            response = client.execute(httpPost);
            if (response.getStatusLine().getStatusCode() == 200) {
                HttpEntity httpEntity = (HttpEntity) response.getEntity();
                if (response != null) {
                    return EntityUtils.toString(httpEntity,encoding);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }



}   

这样我们就封装了一个https请求加证书的实体类,接下来我们生成请求微信红包接口:
https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack 的参数签名:

/**
*  红包参数实体类
 * @throws UnsupportedEncodingException
 */
@Component
public class SendRedPack implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -1000489228099916099L;

    private String nonce_str;// 随机字符串
    private String sign;// 签名
    private String mch_billno;// 商户订单号
    private String mch_id;// 商户号
    private String wxappid;// 公众账号
    private String send_name;// 商户名称
    private String re_openid;// 用户
    private int total_amount;// 付款金额 单位:分
    private int total_num;// 红包发放总人数
    private String wishing;// 红包祝福语
    private String client_ip;// Ip地址
    private String act_name;// 活动名称
    private String remark;// 备注
    public String getNonce_str() {
        return nonce_str;
    }
    public void setNonce_str(String nonce_str) {
        this.nonce_str = nonce_str;
    }
    public String getSign() {
        return sign;
    }
    public void setSign(String sign) {
        this.sign = sign;
    }
    public String getMch_billno() {
        return mch_billno;
    }
    public void setMch_billno(String mch_billno) {
        this.mch_billno = mch_billno;
    }
    public String getMch_id() {
        return mch_id;
    }
    public void setMch_id(String mch_id) {
        this.mch_id = mch_id;
    }
    public String getWxappid() {
        return wxappid;
    }
    public void setWxappid(String wxappid) {
        this.wxappid = wxappid;
    }
    public String getSend_name() {
        return send_name;
    }
    public void setSend_name(String send_name) {
        this.send_name = send_name;
    }
    public String getRe_openid() {
        return re_openid;
    }
    public void setRe_openid(String re_openid) {
        this.re_openid = re_openid;
    }
    public int getTotal_amount() {
        return total_amount;
    }
    public void setTotal_amount(int total_amount) {
        this.total_amount = total_amount;
    }
    public int getTotal_num() {
        return total_num;
    }
    public void setTotal_num(int total_num) {
        this.total_num = total_num;
    }
    public String getWishing() {
        return wishing;
    }
    public void setWishing(String wishing) {
        this.wishing = wishing;
    }
    public String getClient_ip() {
        return client_ip;
    }
    public void setClient_ip(String client_ip) {
        this.client_ip = client_ip;
    }
    public String getAct_name() {
        return act_name;
    }
    public void setAct_name(String act_name) {
        this.act_name = act_name;
    }
    public String getRemark() {
        return remark;
    }
    public void setRemark(String remark) {
        this.remark = remark;
    }
}

接下来是发送红包的控制器:

/**
 * 领红包控制器
 * @author zengliang
 */
@Controller
@RequestMapping(value="/redenveLopesReceive")
public class RedEnvelopesReceiveController {

    //微信唯一标识
    @Value("${weixin.appid}")
    private String appid;

    //微信开发者密码标识
    @Value("${weixin.appsecret}")
    public String appsecret;

    @Autowired
    private SendRedPack sendredpack;

    @Autowired
    private HttpClientSSL httpclientssl;

    /**
     * 发送XML参数
     * @author zengliang
     */
    @ResponseBody
    @RequestMapping(value="/sendXml")
    public String sendXml(String openid,Long redenveLopes_id
            ,String mch_billno){
        RedenveLopes redenve = redenveLopesService.findOne(redenveLopes_id);

        XMLUtil xmlUtil= new XMLUtil();
        sendredpack.setAct_name(redenve.getAct_name());
        sendredpack.setNonce_str(xmlUtil.random());
        sendredpack.setRe_openid(openid);
        sendredpack.setClient_ip(redenve.getClient_ip());
        sendredpack.setMch_billno(mch_billno);
        sendredpack.setMch_id(redenve.getMch_id());
        String xx = redenve.getRemark();
        sendredpack.setRemark(StringUtils.isEmpty(xx) == false?xx:"空");
        sendredpack.setSend_name(redenve.getSend_name());
        sendredpack.setTotal_amount(redenve.getTotal_amount());
        sendredpack.setTotal_num(redenve.getTotal_num());
        sendredpack.setWishing(redenve.getWishing());
        sendredpack.setWxappid(redenve.getWxappidxx());
        //生成签名
        String params = this.createSendRedPackOrderSign(sendredpack,redenve.getStore_key());
        sendredpack.setSign(params);

        xmlUtil.xstream().alias("xml",sendredpack.getClass());
        //扩展xstream,使其支持CDATA块
        String requestXml = xmlUtil.xstream().toXML(sendredpack);

        String result;
        try {
            result = httpclientssl.send("https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack",requestXml,httpclientssl.defaultSSLClientFile());
            System.out.println("成功返回值"+result);
            return result;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

/**
     * 生成签名
     * @param redPack
     * @return
     */
    public String createSendRedPackOrderSign(SendRedPack redPack,String storekey){
        StringBuffer sign = new StringBuffer();
        sign.append("act_name=").append(redPack.getAct_name());
        sign.append("&client_ip=").append(redPack.getClient_ip());
        sign.append("&mch_billno=").append(redPack.getMch_billno());
        sign.append("&mch_id=").append(redPack.getMch_id());
        sign.append("&nonce_str=").append(redPack.getNonce_str());
        sign.append("&re_openid=").append(redPack.getRe_openid());
        sign.append("&remark=").append(redPack.getRemark());
        sign.append("&send_name=").append(redPack.getSend_name());
        sign.append("&total_amount=").append(redPack.getTotal_amount());
        sign.append("&total_num=").append(redPack.getTotal_num());
        sign.append("&wishing=").append(redPack.getWishing());
        sign.append("&wxappid=").append(redPack.getWxappid());
        sign.append("&key=").append(storekey);
        return DigestUtils.md5Hex(sign.toString()).toUpperCase();
   }

}

然后我们需要用一个解析XML的工具类实现解析微信返回的XML

/**
 * 解析XML工具类
 * @author zengliang
 */
@Component
public class XMLUtil {

    /**
     * 解析微信返回的XML
     * @param xml
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public Map parseXml(String xml)throws Exception {
        Map map = new HashMap();  
        Document doc = null;  
        try {  
            doc = DocumentHelper.parseText(xml); // 将字符串转为XML  
            Element rootElt = doc.getRootElement(); // 获取根节点  
            List list = rootElt.elements();//获取根节点下所有节点  
            for (Element element : list) {  //遍历节点  
                map.put(element.getName(), element.getText()); //节点的name为map的key,text为map的value  
            }  
        } catch (DocumentException e) {  
            e.printStackTrace();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }
        return map;  
    }

    /**
     * 扩展xstream,使其支持CDATA块
     */
      private XStream xstream = new XStream(new XppDriver(new NoNameCoder()) {
            @Override
            public HierarchicalStreamWriter createWriter(Writer out) {
                    return new PrettyPrintWriter(out) {
                    // 对所有xml节点的转换都增加CDATA标记
                    boolean cdata = true;
                    @Override
                    @SuppressWarnings("rawtypes")
                    public void startNode(String name, Class clazz) {
                        super.startNode(name, clazz);
                    }
                    @Override
                    public String encodeNode(String name) {
                        return name;
                    }
                    @Override
                    protected void writeText(QuickWriter writer, String text) {
                        if (cdata) {
                            writer.write(");
                            writer.write(text);
                            writer.write("]]>");
                        } else {
                            writer.write(text);
                        }
                    }
                    };
            }
      });
      private XStream inclueUnderlineXstream = new XStream(new DomDriver(null,new XmlFriendlyNameCoder("_-", "_")));
      public XStream getXstreamInclueUnderline() {
          return inclueUnderlineXstream;
      }

      public XStream xstream() {
          return xstream;
     }

     /**
     * 生成随机数
     * @return
     */
    public String random(){
        String random = UUID.randomUUID().toString().replace("-", "");
        return random;
    }
}

然后我们调用 sendXML 方法公众号就能向用户发送红包了。

你可能感兴趣的:(JAVAEE技术博客)