微信现金红包,首先必须申请一个商户号。本文采用的是服务商模式的商户号,给客户(子商户)开通。

但是开通现金红包条件不简单,我们先看一下官方说明。

单单是上面的入驻超过90天,联系交易30就很难满足(因为客户已开通微信支付,就需要使用)。

后来了解到只需要申请结算周期为T+7的商户号,就能够直接使用了。

于是乎,解决了最大的问题,接下来就是开发了,下面是部分代码,供参考。


1. 准备请求参数

PayRedPackBean.java

package com.pay.wechat.bo.redpack.bean;
 
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
 
import com.dlys.pay.wechat.util.Signature;
import com.tenet.util.uuid.UUIDUtil19;
 
/**
 * 现金红包实体
 * 
 * @author libaibai
 * @version 1.0 
 */
public class PayRedPackBean {
 
	public String nonce_str = UUIDUtil19.uuid();
	public String sign;
	public String mch_billno; // 商户订单号,接口根据商户订单号支持重入,如出现超时可再调用
	public String mch_id; // 商户号
	public String sub_mch_id; // 子商户号
 
	// 微信分配的公众账号ID(企业号corpid即为此appId)。接口传入的所有appid应该为公众号的appid
	public String wxappid;
 
	// 服务商模式下触达用户时的appid(可填服务商自己的appid)
	public String msgappid;
 
	public String send_name; // 红包发送者名称
	public String re_openid; // 接受红包的用户 ,服务商模式下可填入msgappid下的openid。
	public int total_amount; // 付款金额,单位份
	public int total_num = 1;// 发放人总数
 
	public String wishing; // 红包祝福语
	public String client_ip; // 调用接口的机器Ip地址
	public String act_name; // 活动名称
	public String remark; // 备注
 
	public String scene_id = "PRODUCT_1"; // 场景id,发放红包使用场景,红包金额大于200或者小于1元时必传
 
	public PayRedPackBean(String mch_billno, String mch_id, String sub_mch_id, String wxappid,
			String msgappid, String send_name, String re_openid, int total_amount, String wishing,
			String client_ip, String act_name, String remark, String key) {
		this.mch_billno = mch_billno;
		this.mch_id = mch_id;
		this.sub_mch_id = sub_mch_id;
		this.wxappid = wxappid;
		this.msgappid = msgappid;
		this.send_name = send_name;
		this.re_openid = re_openid;
		this.total_amount = total_amount;
		this.wishing = wishing;
		this.client_ip = client_ip;
		this.act_name = act_name;
		this.remark = remark;
		this.sign = Signature.getSign(toMap(), key);
	}
 
	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 getSub_mch_id() {
		return sub_mch_id;
	}
 
	public void setSub_mch_id(String sub_mch_id) {
		this.sub_mch_id = sub_mch_id;
	}
 
	public String getWxappid() {
		return wxappid;
	}
 
	public void setWxappid(String wxappid) {
		this.wxappid = wxappid;
	}
 
	public String getMsgappid() {
		return msgappid;
	}
 
	public void setMsgappid(String msgappid) {
		this.msgappid = msgappid;
	}
 
	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;
	}
 
	public String getScene_id() {
		return scene_id;
	}
 
	public void setScene_id(String scene_id) {
		this.scene_id = scene_id;
	}
 
	public Map<String, Object> toMap() {
		Map<String, Object> map = new HashMap<String, Object>();
		Field[] fields = this.getClass().getDeclaredFields();
		for (Field field : fields) {
			Object obj;
			try {
				obj = field.get(this);
				if (obj != null) {
					map.put(field.getName(), obj);
				}
			} catch (IllegalArgumentException e) {
			} catch (IllegalAccessException e) {
			}
		}
		return map;
	}
}


        public static String SENDREDPACK_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
 
	/**
	 * 发送现金红包
	 * 
	 * @param payRedPackBean 这个数据对象里面包含了API要求提交的各种数据字段
	 * @return API返回的数据
	 * @throws Exception
	 */
	public String requestRedPack(PayRedPackBean payRedPackBean) throws Exception {
		
		super.apiURL = Config.SENDREDPACK_URL;
		// --------------------------------------------------------------------
		// 发送HTTPS的Post请求到API地址
		// --------------------------------------------------------------------
		String responseString = sendPost(payRedPackBean);
		return responseString;
	}

发送红包工具类

	/**
	 * 发送红包工具类
	 * 
	 * @param mch_billno 订单号UUID
	 * @param appid
	 * @param mch_id
	 * @param sub_mch_id
	 * @param openId 接受人openId
	 * @param key
	 * @param send_name 发红包人名称
	 * @param total_amount
	 * @param client_ip
	 * @return
	 */
	public Map<String, Object> send(String mch_billno, String appid, String mch_id,
			String sub_mch_id, String openId, String key, String send_name, int total_amount) {
 
		String wishing = "现金红包";
		String act_name = "现金红包";
		String remark = "现金红包";
		String client_ip = "127.0.0.1";
		PayRedPackBean data = new PayRedPackBean(mch_billno, mch_id, sub_mch_id, appid, appid,
				send_name, openId, total_amount, wishing, client_ip, act_name, remark, key);
		try {
			String xmlMsg = scanPayService.requestRedPack(data);
			LOG.info("PayPackUtil-发送现金红包微信响应,xmlMsg=" + xmlMsg + ",send_name=" + send_name);
			return XMLParser.getMapFromXML(xmlMsg);
 
		} catch (Exception e) {
			LOG.error("请求微信红包时出错!", e);
			return null;
		}
	}
public class BaseService {
 
	// API的地址
	public String apiURL;
 
	// 发请求的HTTPS请求器
 
	@Resource
	private HttpsRequest httpsRequest;
 
	protected String sendPost(Object xmlObj) throws UnrecoverableKeyException, IOException,
			NoSuchAlgorithmException, KeyStoreException, KeyManagementException,
			ClassNotFoundException, InstantiationException, IllegalAccessException {
		return httpsRequest.sendPost(apiURL, xmlObj);
	}
 
}
/**
 * 支付请求
 *
 * @author libaibai
 * @version 1.0 2015年8月31日
 */
@Component
public class ScanPayService extends BaseService {
 
	/**
	 * 发送现金红包
	 * @param data 这个数据对象里面包含了API要求提交的各种数据字段
	 * @return API返回的数据
	 * @throws Exception
	 */
    public String requestRedPack(PayRedPackBean data) throws UnrecoverableKeyException, IOException, NoSuchAlgorithmException, KeyStoreException, ClassNotFoundException, KeyManagementException, InstantiationException, IllegalAccessException {
		super.apiURL = Config.SENDREDPACK_URL;
		return sendPost(data);
    }
}


public class HttpsRequest {
	public interface ResultListener {
		public void onConnectionPoolTimeoutError();
	}
 
	private static Logger LOG = LogManager.getLogger(HttpsRequest.class);
 
	private boolean hasInit = false;
 
	// 连接超时时间,默认10秒
	private int socketTimeout = 10000;
 
	// 传输超时时间,默认30秒
	private int connectTimeout = 30000;
 
	// 请求器的配置
	private RequestConfig requestConfig;
 
	// HTTP请求器
	private CloseableHttpClient httpClient;
 
	private String CERTLOCAL_PATHx = Configure.CERTLOCAL_PATHx;
	private String CERTPASSWORD = Configure.CERTPASSWORD;
 
	/**
	 * 证书初始化
	 * 
	 * @throws UnrecoverableKeyException
	 * @throws KeyManagementException
	 * @throws NoSuchAlgorithmException
	 * @throws KeyStoreException
	 * @throws IOException
	 */
	public HttpsRequest() throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException {
		init();
	}
	public HttpsRequest(Byte payModel) throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException {
		if (Const.getByte(payModel) == 1) {
			CERTLOCAL_PATHx = Configure.CERTLOCAL_PATHx_DLY;
			CERTPASSWORD = Configure.CERTPASSWORD_DLY;
		}
		init();
	}
 
	private void init() throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
 
		KeyStore keyStore = KeyStore.getInstance("PKCS12");
		ClassPathResource classPathResource = new ClassPathResource(CERTLOCAL_PATHx);
		InputStream instream = classPathResource.getInputStream();
//		InputStream InputStreamm = this.getClass().getResourceAsStream(Configure.CERTLOCAL_PATHx);// 加载本地的证书进行https加密传输
		try {
			keyStore.load(instream, CERTPASSWORD.toCharArray());// 设置证书密码
		} catch (Exception e) {
			LOG.error("加载证书异常", e);
		} finally {
			instream.close();
		}
		SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, CERTPASSWORD.toCharArray()).build();
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
				SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
 
		httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
 
		// 根据默认超时限制初始化requestConfig
		requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
		hasInit = true;
	}
 
	/**
	 * 通过Https往API GET
	 *
	 * @param url API地址
	 * @return API回包的实际数据
	 * @throws IOException
	 * @throws KeyStoreException
	 * @throws UnrecoverableKeyException
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 */
 
	public String sendGET(String url)
			throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
 
		if (!hasInit) {
			init();
		}
 
		String result = null;
 
		HttpGet httpGet = new HttpGet(url);
 
		// 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
		httpGet.addHeader("Content-Type", "text/xml");
 
		// 设置请求器的配置
		httpGet.setConfig(requestConfig);
 
		try {
			HttpResponse response = httpClient.execute(httpGet);
 
			HttpEntity entity = response.getEntity();
 
			result = EntityUtils.toString(entity, "UTF-8");
 
		} catch (Exception e) {
			LOG.error("HTTP Get请示异常", e);
		} finally {
			httpGet.abort();
		}
		return result;
	}
 
	/**
	 * 通过Https往API post
	 *
	 * @param url API地址
	 * @param postEntity 要提交的Object数据对象
	 * @return API回包的实际数据
	 * @throws IOException
	 * @throws KeyStoreException
	 * @throws UnrecoverableKeyException
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 */
 
	public String sendPostObject(String url, HttpEntity postEntity)
			throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
 
		if (!hasInit) {
			init();
		}
 
		String result = null;
		HttpPost httpPost = new HttpPost(url);
		httpPost.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.MULTIPART_FORM_DATA.getMimeType());
		httpPost.setEntity(postEntity);
 
		// 设置请求器的配置
		httpPost.setConfig(requestConfig);
		try {
			HttpResponse response = httpClient.execute(httpPost);
			HttpEntity entity = response.getEntity();
 
			result = EntityUtils.toString(entity, "UTF-8");
 
		} catch (Exception e) {
			LOG.error("HTTP POST 请求异常", e);
		} finally {
			httpPost.abort();
		}
 
		return result;
	}
 
	/**
	 * 通过Https往API post xml数据
	 *
	 * @param url API地址
	 * @param xmlObj 要提交的XML数据对象
	 * @return API回包的实际数据
	 * @throws IOException
	 * @throws KeyStoreException
	 * @throws UnrecoverableKeyException
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 */
 
	public String sendPost(String url, Object xmlObj)
			throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
 
		if (!hasInit) {
			init();
		}
 
		String result = null;
		HttpPost httpPost = new HttpPost(url);
 
		// 解决XStream对出现双下划线的bug
		// XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8",
		// new XmlFriendlyNameCoder("-_", "_")));
 
		XStream xStream = XStreamFactory.getXStream(new XmlFriendlyNameCoder("_-", "_"));
 
		// 将要提交给API的数据对象转换成XML格式数据Post给API
		String postDataXML = xStream.toXML(xmlObj);
//		LOG.info("请求微信接口->url=" + url + ",data=" + postDataXML);
		// LOG.info("data="+StringEscapeUtils.unescapeXml(postDataXML));// 转义字符
 
		// 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
		StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
		httpPost.addHeader("Content-Type", "text/xml");
		httpPost.setEntity(postEntity);
 
		// 设置请求器的配置
		httpPost.setConfig(requestConfig);
 
		try {
			HttpResponse response = httpClient.execute(httpPost);
 
			HttpEntity entity = response.getEntity();
 
			result = EntityUtils.toString(entity, "UTF-8");
 
		} catch (Exception e) {
			LOG.error("HTTP POST 请求异常", e);
 
		} finally {
			// httpPost.abort();
			httpPost.releaseConnection();
		}
 
		return result;
	}
 
	/**
	 * 设置连接超时时间
	 *
	 * @param socketTimeout 连接时长,默认10秒
	 */
	public void setSocketTimeout(int socketTimeout) {
		this.socketTimeout = socketTimeout;
		resetRequestConfig();
	}
 
	/**
	 * 设置传输超时时间
	 *
	 * @param connectTimeout 传输时长,默认30秒
	 */
	public void setConnectTimeout(int connectTimeout) {
		this.connectTimeout = connectTimeout;
		resetRequestConfig();
	}
 
	private void resetRequestConfig() {
		requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
	}
 
	/**
	 * 允许商户自己做更高级更复杂的请求器配置
	 *
	 * @param requestConfig 设置HttpsRequest的请求器配置
	 */
	public void setRequestConfig(RequestConfig requestConfig) {
		this.requestConfig = requestConfig;
	}