欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-12-09 19:25:39
我们先来看看公众号发放现金红包的效果: 需要调用商户平台的接口,接口发放规则如下: 1.发送频率限制——默认1800/min 2.发送个数上限——按照默认...

我们先来看看公众号发放现金红包的效果:

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

需要调用商户平台的接口,接口发放规则如下:

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><![cdata[e1ee61a91c8e90f299de6ae075d60a2d]]></sign>

<mch_billno><![cdata[0010010404201411170000046545]]></mch_billno>

<mch_id><![cdata[888]]></mch_id>

<wxappid><![cdata[wxcbda96de0b165486]]></wxappid>
<send_name><![cdata[send_name]]></send_name>
<re_openid><![cdata[onqojjmm1tad-3ropncn-yufa6ui]]></re_openid>
<total_amount><![cdata[200]]></total_amount>
<total_num><![cdata[1]]></total_num>
<wishing><![cdata[恭喜发财]]></wishing>

<client_ip><![cdata[127.0.0.1]]></client_ip>
<act_name><![cdata[新年红包]]></act_name>
<remark><![cdata[新年红包]]></remark>
<scene_id><![cdata[product_2]]></scene_id>
<consume_mch_id><![cdata[10000097]]></consume_mch_id>
<nonce_str><![cdata[50780e0cca98c8c8e814883e5caa672e]]></nonce_str>

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

</xml>

接口需要调用商户平台的证书,证书需要去商户平台下载:

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

然后在接口中使用证书,首先我们新建一个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<string, string> parsexml(string xml)throws exception {
    map<string,string> map = new hashmap<string,string>(); 
    document doc = null; 
    try { 
      doc = documenthelper.parsetext(xml); // 将字符串转为xml 
      element rootelt = doc.getrootelement(); // 获取根节点 
      list<element> 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("<![cdata[");
              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 方法公众号就能向用户发送红包了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。