微信开发-发放普通红包(java代码实例)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cl11992/article/details/86620444

一、介绍

前一阵子做了个微信发放普通红包的功能,在这记录下开发思路

二、微信官网

https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3

三、发放规则与注意事项

发放规则:

  1. 发送频率限制------默认1800/min
  2. 发送个数上限------按照默认1800/min算
  3. 金额限制------默认红包金额为1-200元,如有需要,可前往商户平台进行设置和申请
  4. 其他其他限制吗?------单个用户可领取红包上线为10个/天,如有需要,可前往商户平台进行设置和申请
  5. 如果量上满足不了我们的需求,如何提高各个上限?------金额上限和用户当天领取次数上限可以在商户平台进行设置

注意事项:

◆ 红包金额大于200或者小于1元时,请求参数scene_id必传,参数说明见官网。 
◆ 根据监管要求,新申请商户号使用现金红包需要满足两个条件:1、入驻时间超过90天 2、连续正常交易30天。 
◆ 移动应用的appid无法使用红包接口。 
◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复发放红包等资金风险。 
◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认红包发放情况。如果有新回包字段,会更新到此API文档中。 
◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认红包发放情况。如果有新的错误码,会更新到此API文档中。 
◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。

四、代码应用技术

  1. 反射机制
  2. JAXB

五、代码实例

发放红包方法

/**
 * 普通红包发送
 */
public boolean addRedPack(SendredpackRequestBo sendredpackRequestBo) throws Exception {
	//发放状态
	boolean status = false;
	// 微信红包地址
	String requestUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
	//key为商户平台设置的密钥key
	String paykey = "XXXXXXXX";
	//微信商户证书地址
	String certPath = "XXXXXXXX";
	//配置参数
	SendredpackRequestBo param = new SendredpackRequestBo();
	param.setNonce_str(UUID.randomUUID().toString().replaceAll("-", ""));
	param.setMch_billno(param.getMch_billno());//商户订单号
	param.setMch_id(param.getMch_id());//商户号
	param.setWxappid("XXXXXXXXXX");//公众账号appid	
	param.setSend_name(sendredpackRequestBo.getSend_name());//商户名称
	param.setRe_openid(sendredpackRequestBo.getRe_openid());//用户openid
	param.setTotal_amount(sendredpackRequestBo.getTotal_amount());// 付款金额,单位:分
	param.setTotal_num(1);//红包发放总人数
	param.setWishing(sendredpackRequestBo.getWishing());//红包祝福语
	param.setClient_ip(sendredpackRequestBo.getClient_ip());//Ip地址
	param.setAct_name(sendredpackRequestBo.getAct_name());//活动名称
	param.setRemark(sendredpackRequestBo.getRemark());//备注
	param.setScene_id("PRODUCT_2");//场景id:红包金额大于200或者小于1元时,请求参数scene_id必传
	param.setSign(WechatUtils.getWechatPaySign(param, paykey));//签名
	// 发送请求
	SendredpackResponseBo resp = null;
	try {
		resp = (SendredpackResponseBo) WechatUtils.httpsXMLPostPay(requestUrl, param, certPath, param.getMch_id());
		status = true;
	} catch (Exception e) {
		logger.error("微信红包异常,请稍后再试!");
	}
	if (resp == null || "FAIL".equals(resp.getResult_code()) || "FAIL".equals(resp.getReturn_code())) {
		String errCode = resp.getErr_code();
		String tips = "";
		switch (errCode) {
		case "NO_AUTH":
			tips = "微信账号异常,请联系客服";
			break;
		case "SENDNUM_LIMIT":
			tips = "今日领取已达上限,请明天再试";
			break;
		case "NOTENOUGH":
			tips = "红包当前领取人数过多,请稍后再试";
			break;
		case "FREQ_LIMIT":
			tips = "红包当前领取人数过多,请稍后再试";
			break;
		case "PROCESSING":
			tips = "红包正在发放中";
			break;
		case "SYSTEMERROR":
			tips = "红包正在发放中";
			break;
		default:
			tips = "系统繁忙,请稍后再试";
			break;
		}
		logger.error(tips);
	}
	return status;
}

WechatUtils.java

下面所需要的XmlParseUtil:https://blog.csdn.net/cl11992/article/details/86623313

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Field;
import java.security.KeyStore;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.net.ssl.SSLContext;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.panpass.bo.SendredpackRequestBo;
import com.panpass.bo.SendredpackResponseBo;

public class WechatUtils {
	
	private static Logger logger = LoggerFactory.getLogger(WechatUtils.class);
	
	/**
	 * 获得支付签名(微信支付,现金红包)
	 */
	@SuppressWarnings("rawtypes")
	public static String getWechatPaySign(SendredpackRequestBo bo, String payKey) throws IllegalArgumentException, IllegalAccessException {
		SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
		for (Field f : bo.getClass().getDeclaredFields()) {
			// 这里设置访问权限为true
			// field.setAccessible(true)的设置是很关键的,它把权限设置为true,从而让对象能够操作那个类的私有字段
			f.setAccessible(true);
			parameters.put(f.getName(), f.get(bo));
		}

		StringBuffer sb = new StringBuffer();
		// 所有参与传参的参数按照ACCSII排序(升序)
		Set es = parameters.entrySet();
		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) && !"serialVersionUID".equals(k)) {
				sb.append(k + "=" + v + "&");
				System.err.println(k + "\t" + v);
			}
		}
		sb.append("key=" + payKey);
		return MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();

	}
	
	/**
	 * 微信支付,现金红包接口,将参数以xml格式转为流数据传递给微信的支付接口 将微信返回数据转成javabean
	 */
	public static Object httpsXMLPostPay(String url, Object param, String certPath, String mchId) throws Exception {
		SendredpackResponseBo resp = null;
		KeyStore keyStore = KeyStore.getInstance("PKCS12");
		// 读取证书
		FileInputStream instream = new FileInputStream(new File(certPath));
		try {
			// password是商户号
			keyStore.load(instream, mchId.toCharArray());
		} catch (Exception e) {
			logger.debug(e.getMessage(), e);
			System.err.println(e);
		} finally {
			instream.close();
		}
		SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build();
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
				SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
		CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

		try {
			HttpPost httpPost = new HttpPost(url);
			logger.info("executing request:" + httpPost.getRequestLine());

			String xml = XmlParseUtil.beanToXml(param, SendredpackRequestBo.class);
			logger.info(xml);

			httpPost.setEntity(new ByteArrayEntity(xml.getBytes("UTF-8")));

			CloseableHttpResponse response = httpclient.execute(httpPost);

			try {
				HttpEntity entity = response.getEntity();
				System.err.println(response.getStatusLine());
				if (entity != null) {
					logger.info("Response content length: " + entity.getContentLength());
					
					String result = EntityUtils.toString(entity);
					
					logger.info(result);
					
					resp = (SendredpackResponseBo) XmlParseUtil.xmlToBean(result, SendredpackResponseBo.class);
					// if ("FAIL".equals(resp.getResult_code()) ||
					// "FAIL".equals(resp.getReturn_code())) {
					// throw new SysException("发放失败,请稍后再试!");
					// }
				}
				EntityUtils.consume(entity);
			} finally {
				response.close();
			}
		} finally {
			httpclient.close();
		}
		return resp;
	}

}

SendredpackRequestBo.java 

import java.io.Serializable;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * 发红包的参数
 * @author cailong
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "xml")
public class SendredpackRequestBo implements Serializable {
	private static final long serialVersionUID = 1L;
	/**
	 * 签名 必填 String(32) 详见签名生成算法
	 */
	private String sign;
	/**
	 * 商户订单号 必填 String(28) 商户订单号(每个订单号必须唯一。
	 * 取值范围:0~9,a~z,A~Z)接口根据商户订单号支持重入,如出现超时可再调用。
	 */
	private String mch_billno;
	/**
	 * 商户号 必填 String(32) 微信支付分配的商户号
	 */
	private String mch_id;
	/**
	 * 公众账号appid 必填 String(32) 微信分配的公众账号ID(企业号corpid即为此appId)。
	 * 接口传入的所有appid应该为公众号的appid
	 * (在mp.weixin.qq.com申请的),不能为APP的appid(在open.weixin.qq.com申请的)。
	 */
	private String wxappid;
	/**
	 * 商户名称 必填 String(32) 红包发送者名称
	 */
	private String send_name;
	/**
	 * 用户openid 必填 String(32) 接受红包的用户,用户在wxappid下的openid
	 */
	private String re_openid;
	/**
	 * 付款金额 必填 int 付款金额,单位分
	 */
	private Integer total_amount;
	/**
	 * 红包发放总人数 必填 int 红包发放总人数total_num=1
	 */
	private Integer total_num;
	/**
	 * 红包祝福语 必填 String(128) 红包祝福语
	 */
	private String wishing;
	/**
	 * Ip地址 必填 String(15) 调用接口的机器Ip地址
	 */
	private String client_ip;
	/**
	 * 活动名称 必填 String(32) 活动名称
	 */
	private String act_name;
	/**
	 * 备注 必填 String(25) 备注
	 */
	private String remark;
	/**
	 * 场景id 非必填 String(32) 发放红包使用场景,红包金额大于200时必传 PRODUCT_1:商品促销 PRODUCT_2:抽奖
	 * PRODUCT_3:虚拟物品兑奖 PRODUCT_4:企业内部福利 PRODUCT_5:渠道分润 PRODUCT_6:保险回馈
	 * PRODUCT_7:彩票派奖 PRODUCT_8:税务刮奖
	 */
	private String scene_id;
	/**
	 * 资金授权商户号 非必填 String(32) 服务商替特约商户发放时使用
	 */
	private String consume_mch_id;

	/**
	 * 随机字符串 必填 String(32) 随机字符串,不长于32位
	 */
	private String nonce_str;
	/**
	 * 活动信息 非必填 String(128) posttime:用户操作的时间戳 mobile:业务系统账号的手机号,国家代码-手机号。不需要+号
	 * deviceid :mac 地址或者设备唯一标识 clientversion
	 * :用户操作的客户端版本把值为非空的信息用key=value进行拼接,再进行urlencode urlencode(posttime=xx&
	 * mobile =xx&deviceid=xx)
	 */
	private String risk_info;

	/**
	 * 随机字符串必填String(32)随机字符串,不长于32位
	 * @return the nonce_str
	 */
	public String getNonce_str() {
		return nonce_str;
	}

	/**
	 * @param 随机字符串必填String(32)随机字符串,不长于32位 the nonce_str to set
	 */
	public void setNonce_str(String nonce_str) {
		this.nonce_str = nonce_str;
	}

	/**
	 * 签名必填String(32)详见签名生成算法
	 * @return the sign
	 */
	public String getSign() {
		return sign;
	}

	/**
	 * @param 签名必填String(32)详见签名生成算法 the sign to set
	 */
	public void setSign(String sign) {
		this.sign = sign;
	}

	/**
	 * 商户订单号必填String(28)商户订单号(每个订单号必须唯一。取值范围:0~9,a~z,A~Z)接口根据商户订单号支持重入,如出现超时可再调用。
	 * @return the mch_billno
	 */
	public String getMch_billno() {
		return mch_billno;
	}

	/**
	 * @param 商户订单号必填String(28)商户订单号(每个订单号必须唯一。取值范围:0~9,a~z,A~Z)接口根据商户订单号支持重入,如出现超时可再调用。
	 */
	public void setMch_billno(String mch_billno) {
		this.mch_billno = mch_billno;
	}

	/**
	 * 商户号必填String(32)微信支付分配的商户号
	 * @return the mch_id
	 */
	public String getMch_id() {
		return mch_id;
	}

	/**
	 * @param 商户号必填String(32)微信支付分配的商户号 the mch_id to set
	 */
	public void setMch_id(String mch_id) {
		this.mch_id = mch_id;
	}

	/**
	 * 公众账号appid必填String(32)微信分配的公众账号ID(企业号corpid即为此appId)。
	 * 接口传入的所有appid应该为公众号的appid
	 * (在mp.weixin.qq.com申请的),不能为APP的appid(在open.weixin.qq.com申请的)。
	 * @return the wxappid
	 */
	public String getWxappid() {
		return wxappid;
	}

	/**
	 * @param 公众账号appid必填String(32)微信分配的公众账号ID(企业号corpid即为此appId)。接口传入的所有appid应该为公众号的appid(
	 * 在mp.weixin.qq.com申请的),不能为APP的appid(在open.weixin.qq.com申请的)。
	 */
	public void setWxappid(String wxappid) {
		this.wxappid = wxappid;
	}

	/**
	 * 商户名称必填String(32)红包发送者名称
	 * @return the send_name
	 */
	public String getSend_name() {
		return send_name;
	}

	/**
	 * @param 商户名称必填String(32)红包发送者名称 the send_name to set
	 */
	public void setSend_name(String send_name) {
		this.send_name = send_name;
	}

	/**
	 * 用户openid必填String(32)接受红包的用户用户在wxappid下的openid
	 * @return the re_openid
	 */
	public String getRe_openid() {
		return re_openid;
	}

	/**
	 * @param 用户openid必填String(32)接受红包的用户用户在wxappid下的openid the re_openid to set
	 */
	public void setRe_openid(String re_openid) {
		this.re_openid = re_openid;
	}

	/**
	 * 付款金额必填int付款金额,单位分
	 * @return the total_amount
	 */
	public Integer getTotal_amount() {
		return total_amount;
	}

	/**
	 * @param 付款金额必填int付款金额,单位分 the total_amount to set
	 */
	public void setTotal_amount(Integer total_amount) {
		this.total_amount = total_amount;
	}

	/**
	 * 红包发放总人数必填int红包发放总人数total_num=1
	 */
	public Integer getTotal_num() {
		return total_num;
	}

	/**
	 * @param 红包发放总人数必填int红包发放总人数total_num =1 the 红包发放总人数必填int红包发放总人数total_num=1 to set
	 */
	public void setTotal_num(Integer total_num) {
		this.total_num = total_num;
	}

	/**
	 * 红包祝福语必填String(128)红包祝福语
	 * @return the wishing
	 */
	public String getWishing() {
		return wishing;
	}

	/**
	 * @param 红包祝福语必填String(128)红包祝福语 the wishing to set
	 */
	public void setWishing(String wishing) {
		this.wishing = wishing;
	}

	/**
	 * Ip地址必填String(15)调用接口的机器Ip地址
	 * @return the client_ip
	 */
	public String getClient_ip() {
		return client_ip;
	}

	/**
	 * @param Ip地址必填String(15)调用接口的机器Ip地址 the client_ip to set
	 */
	public void setClient_ip(String client_ip) {
		this.client_ip = client_ip;
	}

	/**
	 * 活动名称必填String(32)活动名称
	 * @return the act_name
	 */
	public String getAct_name() {
		return act_name;
	}

	/**
	 * @param 活动名称必填String(32)活动名称 the act_name to set
	 */
	public void setAct_name(String act_name) {
		this.act_name = act_name;
	}

	/**
	 * 备注必填String(25)备注
	 * @return the remark
	 */
	public String getRemark() {
		return remark;
	}

	/**
	 * @param 备注必填String(25)备注 the remark to set
	 */
	public void setRemark(String remark) {
		this.remark = remark;
	}

	/**
	 * 场景id非必填String(32)发放红包使用场景,红包金额大于200时必传PRODUCT_1:商品促销PRODUCT_2:抽奖PRODUCT_3
	 * :虚拟物品兑奖PRODUCT_4:企业内部福利PRODUCT_5:渠道分润PRODUCT_6:保险回馈PRODUCT_7:
	 * 彩票派奖PRODUCT_8:税务刮奖
	 * @return the scene_id
	 */
	public String getScene_id() {
		return scene_id;
	}

	/**
	 * @param 场景id非必填String(32)发放红包使用场景,红包金额大于200时必传
	 * PRODUCT_1:商品促销
	 * PRODUCT_2:抽奖
	 * PRODUCT_3:虚拟物品兑奖
	 * PRODUCT_4企业内部福利
	 * PRODUCT_5:渠道分润
	 * PRODUCT_6:保险回馈
	 * PRODUCT_7:彩票派奖
	 * PRODUCT_8:税务刮奖 the scene_id to set
	 */
	public void setScene_id(String scene_id) {
		this.scene_id = scene_id;
	}

	/**
	 * 活动信息非必填String(128)posttime:用户操作的时间戳mobile:业务系统账号的手机号,国家代码-手机号。不需要+
	 * 号deviceid:mac地址或者设备唯一标识clientversion:用户操作的客户端版本把值为非空的信息用key=value进行拼接,
	 * 再进行urlencodeurlencode(posttime=xx&mobile=xx&deviceid=xx)
	 * @return the risk_info
	 */
	public String getRisk_info() {
		return risk_info;
	}

	/**
	 * @param 活动信息非必填String(128)
	 * posttime:用户操作的时间戳
	 * mobile:业务系统账号的手机号,国家代码-手机号。不需要+号
	 * deviceid:mac地址或者设备唯一标识
	 * clientversion:用户操作的客户端版本把值为非空的信息用key=value进行拼接,再进行urlencodeurlencode(posttime=xx&mobile=xx&deviceid=xx)
	 */
	public void setRisk_info(String risk_info) {
		this.risk_info = risk_info;
	}

	/**
	 * 资金授权商户号非必填String(32)服务商替特约商户发放时使用
	 * @return the consume_mch_id
	 */
	public String getConsume_mch_id() {
		return consume_mch_id;
	}

	/**
	 * @param 资金授权商户号非必填String(32)服务商替特约商户发放时使用 the consume_mch_id to set
	 */
	public void setConsume_mch_id(String consume_mch_id) {
		this.consume_mch_id = consume_mch_id;
	}
}

猜你喜欢

转载自blog.csdn.net/cl11992/article/details/86620444