java微信公眾號支付開發(fā)之現(xiàn)金紅包
我們先來看看公眾號發(fā)放現(xiàn)金紅包的效果:

需要調(diào)用商戶平臺的接口,接口發(fā)放規(guī)則如下:
1.發(fā)送頻率限制——默認(rèn)1800/min
2.發(fā)送個數(shù)上限——按照默認(rèn)1800/min算
3.金額上限——根據(jù)傳入場景id不同默認(rèn)上限不同,可以在商戶平臺產(chǎn)品設(shè)置進行設(shè)置和申請,最大不大于4999元/個
4.其他的“量”上的限制還有哪些?——用戶當(dāng)天的領(lǐng)取上限次數(shù),默認(rèn)是10
5.如果量上滿足不了我們的需求,如何提高各個上限?——金額上限和用戶當(dāng)天領(lǐng)取次數(shù)上限可以在商戶平臺進行設(shè)置
注意-紅包金額大于200時,請求參數(shù)scene_id必傳,參數(shù)說明見下文。
注意2-根據(jù)監(jiān)管要求,新申請商戶號使用現(xiàn)金紅包需要滿足兩個條件:1、入駐時間超過90天 2、連續(xù)正常交易30天。
請求Url https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack
是否需要證書 是(證書及使用說明詳見商戶證書)
請求方式 POST
請求數(shù)據(jù)示例:
<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[恭喜發(fā)財]]></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>
接口需要調(diào)用商戶平臺的證書,證書需要去商戶平臺下載:

然后在接口中使用證書,首先我們新建一個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 類實現(xiàn) https 請求加證書:
@Component
public class HttpClientSSL {
@Autowired
private WeiXinSSL weiXinSSL;
// 請求超時時間(毫秒) 5秒
public static RequestConfig requestConfig;
// 響應(yīng)超時時間(毫秒) 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());
// 設(shè)置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();
}
/**
* 封裝發(fā)送請求的方法
* @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());// 設(shè)置超時時間
CloseableHttpResponse response = null;
// 參數(shù)放入
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 的參數(shù)簽名:
/**
* 紅包參數(shù)實體類
* @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;// 紅包發(fā)放總?cè)藬?shù)
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;
}
}
接下來是發(fā)送紅包的控制器:
/**
* 領(lǐng)紅包控制器
* @author zengliang
*/
@Controller
@RequestMapping(value="/redenveLopesReceive")
public class RedEnvelopesReceiveController {
//微信唯一標(biāo)識
@Value("${weixin.appid}")
private String appid;
//微信開發(fā)者密碼標(biāo)識
@Value("${weixin.appsecret}")
public String appsecret;
@Autowired
private SendRedPack sendredpack;
@Autowired
private HttpClientSSL httpclientssl;
/**
* 發(fā)送XML參數(shù)
* @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的工具類實現(xiàn)解析微信返回的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); // 將字符串轉(zhuǎn)為XML
Element rootElt = doc.getRootElement(); // 獲取根節(jié)點
List<Element> list = rootElt.elements();//獲取根節(jié)點下所有節(jié)點
for (Element element : list) { //遍歷節(jié)點
map.put(element.getName(), element.getText()); //節(jié)點的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節(jié)點的轉(zhuǎn)換都增加CDATA標(biāo)記
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;
}
/**
* 生成隨機數(shù)
* @return
*/
public String random(){
String random = UUID.randomUUID().toString().replace("-", "");
return random;
}
}
然后我們調(diào)用 sendXML 方法公眾號就能向用戶發(fā)送紅包了。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- java微信公眾號企業(yè)付款開發(fā)
- Java版微信公眾號支付開發(fā)全過程
- 微信公眾號開發(fā)之設(shè)置自定義菜單實例代碼【java版】
- 微信公眾號開發(fā)之回復(fù)圖文消息java代碼
- Java微信公眾號開發(fā)之通過微信公眾號獲取用戶信息
- java微信公眾號開發(fā)案例
- 用Java設(shè)計模式中的觀察者模式開發(fā)微信公眾號的例子
- java微信公眾號開發(fā)第一步 公眾號接入和access_token管理
- Java開發(fā)微信公眾號接收和被動回復(fù)普通消息
- java微信公眾號開發(fā)(搭建本地測試環(huán)境)
- java開發(fā)微信公眾號支付
- Java 微信公眾號開發(fā)相關(guān)總結(jié)
相關(guān)文章
SpringBoot+Redis+Lua分布式限流的實現(xiàn)
本文主要介紹了SpringBoot+Redis+Lua分布式限流的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
SpringSecurity 默認(rèn)表單登錄頁展示流程源碼
本篇主要講解 SpringSecurity提供的默認(rèn)表單登錄頁 它是如何展示流程,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友參考下吧2020-01-01
Java中List與數(shù)組之間的相互轉(zhuǎn)換
在日常Java學(xué)習(xí)或項目開發(fā)中,經(jīng)常會遇到需要int[]數(shù)組和List列表相互轉(zhuǎn)換的場景,然而往往一時難以想到有哪些方法,最后可能會使用暴力逐個轉(zhuǎn)換法,往往不是我們所滿意的,下面這篇文章主要給大家介紹了關(guān)于Java中List與數(shù)組之間的相互轉(zhuǎn)換,需要的朋友可以參考下2023-05-05
Jvm調(diào)優(yōu)和SpringBoot項目優(yōu)化的詳細(xì)教程
這篇文章主要介紹了Jvm調(diào)優(yōu)和SpringBoot項目優(yōu)化,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09
微信公眾號 網(wǎng)頁授權(quán)登錄及code been used解決詳解
這篇文章主要介紹了微信公眾號 網(wǎng)頁授權(quán)登錄及code been used解決詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-07-07

