04.16 微信支付開發之現金紅包

我們先來看看公眾號發放現金紅包的效果:

微信支付開發之現金紅包

需要調用商戶平臺的接口,接口發放規則如下:

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

請求數據示例:

 <sign>   <wxappid> <send>  <total> <total> <wishing> <client>  <remark> <scene> <consume> <nonce> <risk>posttime%3d123123412%26clientversion%3d234134%26mobile%3d122344545%26deviceid%3dIOS/<risk>  12345678910111213141516171819202122232425262728293031323334353637

接口需要調用商戶平臺的證書,證書需要去商戶平臺下載:

微信支付開發之現金紅包

然後在接口中使用證書,首先我們新建一個WeixinSSL 類

@Componentpublic 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;
}
}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647

封裝HttpClientSSL 類實現 https 請求加證書:

@Componentpublic 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;
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130

這樣我們就封裝了一個https請求加證書的實體類,接下來我們生成請求微信紅包接口:

https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack 的參數簽名:

/**
* 紅包參數實體類
* @throws UnsupportedEncodingException
*/@Componentpublic 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;
}
}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104

接下來是發送紅包的控制器:

/**
* 領紅包控制器
* @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();
}
}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091

然後我們需要用一個解析XML的工具類實現解析微信返回的XML

/**
* 解析XML工具類
* @author zengliang
*/@Componentpublic class XMLUtil {
/**
* 解析微信返回的XML
* @param xml
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked") public Map<string> parseXml(String xml)throws Exception {
Map<string> map = new HashMap<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(" 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;
}
}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182/<element>/<string>/<string>/<string>

然後我們調用 sendXML 方法公眾號就能向用戶發送紅包了。


分享到:


相關文章: