踩过很多坑,希望这篇文可以为大家开发公众号领取红包,做个引导。更重要的是,我发现我有点忘了,所以为了下次再用到这方面的功能,特此,来做个记录
PS:可以多看看api文档,吐槽一下,微信开发api文档很坑,相比之下,支付宝api看着就舒服多了。
前期准备,公众号+有红包功能的商户号
因为没有可用的域名,所有我用了菜单点击事件
简单直接上代码,可以自己下载一个官方提供的源码对比着看。
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public void weChatConnect(HttpServletRequest req, HttpServletResponse resp, HttpSession session) throws IOException{
req.getInputStream();
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
PrintWriter out=resp.getWriter();
Map<String ,String> map= MessageUtil.xmlToMap(req);
String fromUserName = map.get("FromUserName");
String toUserName = map.get("ToUserName");
String msgType = map.get("MsgType");
String content = map.get("Content");
session.setAttribute("requestMap",map);
String message=null;
System.out.println(msgType+",,,,"+map.get("Event"));
try{
//事件,消息自动推送
if(Const.MsgType.MESSAGE_EVNET.equals(msgType)){
//获取推送事件
String eventType=map.get("Event");
//菜单点击
if(Const.MsgType.MESSAGE_CLICK.equals(eventType)){
String key=map.get("EventKey");
if(key.equals("sendredpack")){
//红包事件
sendRedPackController.sendRedPack(req,resp,map);
}
}
}
out.print(message);
}catch (Exception e){
e.printStackTrace();
}finally {
out.close();
}
}
红包领取controller
@Controller
@RequestMapping("/redhandle/")
public class SendRedPackController {
private Logger logger = LoggerFactory.getLogger(SendRedPackController.class);
@Autowired
private ISendRedPackService iSendRedPackService;
@RequestMapping(value = "sendredpack.do")
@ResponseBody
public void sendRedPack(HttpServletRequest req,HttpServletResponse resp,Map map) throws IOException {
//开始发送红包
System.out.println("红包发送开始");
req.getInputStream();
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
String message=null;
try {
String openId = map.get("FromUserName").toString();
message = iSendRedPackService.sendRedPackSub(openId);
} catch (Exception e) {
logger.error("红包Controller异常",e);
}
out.print(message);
}
}
ServiceImpl业务层
@Service("iSendRedPackService")
public class SendRedPackServiceImpl implements ISendRedPackService {
public static final Logger logger = LoggerFactory.getLogger(SendRedPackServiceImpl.class);
public static final String SENDREDPACK_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
@Autowired
private IUserService iUserService;
//String content
public String sendRedPackSub(String openId) throws Exception {
String respResult = null;
//查询该用户是否关注公众号
ServerResponse<JSONObject> response = iUserService.getUserInfo(openId);
JSONObject jsonObject = response.getData();
if(Integer.valueOf(jsonObject.get("subscribe").toString()) == Const.UserStatus.UN_SUBSCRIBE ){
return respResult = "先关注后领取哦!";
}
SortedMap sortedMap = this.assembleSendRedPack(openId);
String reuqestXml = SendRedPackUtil.getRequestXML(sortedMap);
System.out.println(reuqestXml);
String jsonStr = this.connect(reuqestXml);
System.out.println(jsonStr);
try {
Document document = DocumentHelper.parseText(jsonStr);
Element root = document.getRootElement();
//只有当returnCode和resultCode都为SUCCESS时,红包才为发送成功
String returnCode = root.element("return_code").getText();
if (returnCode.equals("SUCCESS")) {
String result_code = root.element("result_code").getText();
GiveRedPack giveRedPack = null;
if (result_code.equals("SUCCESS")) {
//自己的后续操作
return respResult = "红包领取成功,注意查收!";
}
return respResult = "怎么没领到呢,再来一次!";
}
} catch (Exception e) {
logger.error("document数据读取异常", e);
}
return null;
}
private String connect(String reuqestXml){
FileInputStream instream = null;
BasicHttpClientConnectionManager connManager;
try {
String apiUrl= "//var//tomcat//tomcat-7//webapps//mdspirit//WEB-INF//classes//cert//wxpay//yichen_cert.p12";
File file = new File(apiUrl);
instream = new FileInputStream(file);
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(instream, PropertiesUtil.getProperty("mch_id").toCharArray());
// 实例化密钥库 & 初始化密钥工厂
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, PropertiesUtil.getProperty("mch_id").toCharArray());
// 创建 SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
sslContext,
new String[]{"TLSv1"},
null,
new DefaultHostnameVerifier());
connManager = new BasicHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslConnectionSocketFactory)
.build(),
null,
null,
null
);
HttpClient httpClient = null;
try{
httpClient = HttpClientBuilder.create()
.setConnectionManager(connManager)
.build();
HttpPost httpPost = new HttpPost(SENDREDPACK_URL);
StringEntity postEntity = new StringEntity(new String(reuqestXml.getBytes(), "UTF-8"));
httpPost.addHeader("Content-Type", "text/xml");
httpPost.addHeader("User-Agent", USER_AGENT + " " + PropertiesUtil.getProperty("mch_id"));
httpPost.setEntity(postEntity);
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
return EntityUtils.toString(httpEntity, "utf-8");
}catch (Exception e){
logger.error("红包发送连接错误",e);
}finally {
connManager.close();
((CloseableHttpClient) httpClient).close();
}
}catch (Exception e){
logger.error("红包链接初始化错误",e);
}finally {
try {
instream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
private SortedMap assembleSendRedPack(String openId) throws Exception{
SortedMap sortedMap = new TreeMap();
sortedMap.put("nonce_str", UUIDUtil.getUUId(PropertiesUtil.getProperty("mch_id"))); //随机字符串生成
sortedMap.put("mch_billno", String.valueOf(generateOrderNo())); //商户订单号生成,唯一
sortedMap.put("mch_id", PropertiesUtil.getProperty("mch_id")); //商户号,微信支付分配的商户号,请不要填写APPID号
// sortedMap.put("sub_mch_id", PropertiesUtil.getProperty("sub_mch_id")); //子商户号
sortedMap.put("wxappid", PropertiesUtil.getProperty("wxappid")); //代理商公众号
// sortedMap.put("msgappid", PropertiesUtil.getProperty("msgappid")); //子商户公众号
sortedMap.put("send_name", PropertiesUtil.getProperty("send_name")); //商户名称
sortedMap.put("re_openid", openId); //用户openid
// List<RedPackType> redPackTypeList = iRedPackTypeService.selectAll().getData();
// RedPackType redPackType = SendRedPackUtil.getPercent(redPackTypeList);
// sortedMap.put("total_amount", BigDecimalUtil.mul(Double.valueOf(redPackType.getAmount().intValue()),Double.valueOf(100)));
sortedMap.put("total_amount", PropertiesUtil.getProperty("total_amount"));
sortedMap.put("total_num", PropertiesUtil.getProperty("total_num"));
sortedMap.put("wishing", PropertiesUtil.getProperty("wishing"));
sortedMap.put("client_ip", PropertiesUtil.getProperty("client_ip"));
sortedMap.put("act_name", PropertiesUtil.getProperty("act_name"));
sortedMap.put("remark", PropertiesUtil.getProperty("remark"));
Integer totalAmount = Integer.valueOf(sortedMap.get("total_amount").toString());
if(totalAmount > 20000){
sortedMap.put("scene_id", Const.SendRedPackSceneIdEnum.PRODUCT_1.getCode()); //用户场景id,红包金额不在普通红包范围内为必填
}
String sign = SendRedPackUtil.generateSignature(sortedMap,SendRedPackUtil.SignType.MD5);
sortedMap.put("sign", sign);
return sortedMap;
}
private long generateOrderNo(){
long currentTime =System.currentTimeMillis();
return currentTime+new Random().nextInt(100);
}
主要的工具类:
public class SendRedPackUtil {
public static final String FIELD_SIGN = "sign";
public enum SignType {
MD5, HMACSHA256
}
public static String createMD5Sign(SortedMap pmap){
StringBuilder sb = new StringBuilder();
String stringA = "";
Iterator<Map.Entry<String, Object>> entries = pmap.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<String, Object> entry = entries.next();
stringA += ((entry.getValue() == null || entry.getValue().equals("")) ? stringA + "" : stringA + "Key = " + entry.getKey() + "&Value = " + entry.getValue());
}
//拼接,key为商户平台设置的密钥key
String stringSignTemp = stringA + PropertiesUtil.getProperty("api_key");
String sign= MD5Util.MD5Encode(stringSignTemp,"utf-8").toUpperCase();
return sign;
}
public static String getRequestXML(Map<String,String> map)throws Exception{
StringBuilder sb = new StringBuilder("<xml>");
for(Map.Entry<String,String> en:map.entrySet()){
String key = en.getKey();
sb.append("<"+key+"><![CDATA[");
sb.append(en.getValue());
sb.append("]]></"+key+">");
}
return sb.append("</xml>").toString();
}
/**
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
*
* @param data 待签名数据
* @param signType 签名方式
* @return 签名
*/
public static String generateSignature(final Map<String, String> data,SignType signType) throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(FIELD_SIGN)) {
continue;
}
if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(PropertiesUtil.getProperty("api_key"));
if (SignType.MD5.equals(signType)) {
return MD5(sb.toString()).toUpperCase();
}
else {
throw new Exception(String.format("Invalid sign_type: %s", signType));
}
}
/**
* 生成 MD5
*
* @param data 待处理数据
* @return MD5结果
*/
public static String MD5(String data) throws Exception {
java.security.MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
}