1.首先做微信方面的开发,一定需要详细的阅读微信支付的开发文档。因为里面列举了比如消息的触达规则,接口调用的请求说明,请求的参数,错误返回示例以及成功返回示例,阅读这些,在出错了我们能够快速定位并且解决问题。
https://pay.weixin.qq.com/wiki/doc/api/index.html
2.在开发微信红包之前,你首先肯定得准备好微信商户平台,微信公众号(服务号)(在这里假设你已经全部准备好了)。
3.我这个发送红包是大转盘抽奖,抽到了红包奖品之后,通知后台去发送红包。废话不多说,前端ajax代码:
//前端发送ajax请求,请求后台去发送红包,前后端交互采用的是json格式的数据进行传输。 var redPacketRequestJson = { "appid":$("#appid").val(),//该公众号的appid的值 "openid":$("#openid").val(),//该用户的openid的值 "redValue":$("#redValue").val()//红包的大小值,单位为分,最低为100分也就是一元 };
$.ajax({ type: "post", url: "http://localhost/wechat/sendRedPacket", contentType: "application/json; charset=utf-8", data: JSON.stringify(redPacketRequestJson), dataType: "json", success: function (data) { if(data.statusCode==100){ console.log("红包发送成功") } }, error: function (XMLHttpRequest, textStatus, errorThrown) { alert(XMLHttpRequest.status); alert(XMLHttpRequest.readyState); alert(textStatus); } });
后台的代码如下:
@ResponseBody @RequestMapping("/sendRedPacket") public String sendRedPacket(@RequestBody(required = false) String requestJson, HttpServletRequest request){ try{ logger.info("发送红包的时候,前端传输过来的信息是:"+requestJson); JSONObject jsonObject = JSONObject.parseObject(requestJson); //公众号的appid String appid = jsonObject.getString("appid"); /** * 根据APPID获取access_token * 我的access_token是做了一个定时器,每隔两个小时刷新一次access_token的值, * 并且保存在redis当中(需要详情的话请留言)。 */ String access_token = RedisUtil.getJustOneMapValueFromRedis(jedisPool,appid,"access_token"); //发给谁,该用户的openid String openid = jsonObject.getString("openid"); //红包的值,最低100分 Integer redValue = jsonObject.getInteger("redValue"); //开始发送红包 logger.info("++++++++++++++开始发送红包++++++++++++++++++"); SortedMap<Object, Object> parameters = new TreeMap<Object, Object>(); /** 当前时间 yyyyMMddHHmmss */ String currTime = CommonUtil.getCurrTime(); /** 8位日期 */ String strTime = currTime.substring(8, currTime.length()); /** 四位随机数 */ String strRandom = CommonUtil.buildRandom(4) + ""; //商户订单号 parameters.put("mch_billno",strTime + strRandom); /** 商户号 */ String mch_id = Constants.mch_id; parameters.put("mch_id", mch_id); /** 随机字符串 */ parameters.put("nonce_str", CommonUtil.getNonceStr()); /** 公众号APPID */ parameters.put("wxappid", appid); /** 商户名称 */ String mch_name = "这个填你们申请微信支付的公众号的名称"; parameters.put("send_name",mch_name); /** 用户openid */ parameters.put("re_openid",openid); /** 付款金额 */ parameters.put("total_amount",redValue); /** 红包发放总人数 */ parameters.put("total_num",1); /** 红包祝福语 */ parameters.put("wishing","感谢您参加抽奖活动,希望您再次中奖"); /** 调用接口的机器Ip地址 */ parameters.put("client_ip",request.getRemoteAddr()); /** 活动名称 */ String activityName = "这个填你们这个红包活动的名称"; parameters.put("act_name",activityName); /** 备注 */ parameters.put("remark","抽的多中的多,欢迎下次再来"); /** 场景id 发放红包使用场景,红包金额大于200时必传 * PRODUCT_1:商品促销 PRODUCT_2:抽奖 PRODUCT_4:企业内部福利 PRODUCT_5:渠道分润 */ parameters.put("scene_id","PRODUCT_2"); /** 资金授权商户号 */ //parameters.put("consume_mch_id",""); /** 活动信息 资金授权商户号,服务商替特约商户发放时使用*/ //parameters.put("risk_info",""); /** MD5进行签名,必须为UTF-8编码,注意上面几个参数名称的大小写 */ String api_key = "这个填你们商户平台的支付密钥的信息"; String sign = CommonUtil.createSign("UTF-8", parameters,api_key); String requestJsonStr = JSON.toJSONString(parameters); logger.info("发送的信息是"+requestJsonStr); parameters.put("sign", sign);// /** 生成xml结构的数据,用于统一下单接口的请求 */ String requestXML = CommonUtil.getRequestXml(parameters); /** * 读取证书 * */ CloseableHttpClient httpclient = null; Map<String,String> result = new HashMap<String,String>(); try { KeyStore keyStore = KeyStore.getInstance("PKCS12"); String pathname = "/usr/apiclient_cert.p12";//这里填你们的证书的地址,我这里放在linux服务器的/usr下面 FileInputStream instream = new FileInputStream(new File(pathname)); //此处为证书所放的绝对路径 try { keyStore.load(instream, mch_id.toCharArray()); } finally { instream.close(); } // Trust own CA and all self-signed certs SSLContext sslcontext = SSLContexts.custom() .loadKeyMaterial(keyStore, mch_id.toCharArray()) .build(); // Allow TLSv1 protocol only SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());//SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER httpclient = HttpClients.custom() .setSSLSocketFactory(sslsf) .build(); } catch (Exception e){ logger.info("读取证书信息的时候发生异常异常信息是:"+e.getMessage()); e.printStackTrace(); } try { String requestUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack"; HttpPost httpPost = new HttpPost(requestUrl); StringEntity reqEntity = new StringEntity(requestXML, "utf-8"); // 设置类型 reqEntity.setContentType("application/x-www-form-urlencoded"); httpPost.setEntity(reqEntity); logger.info("executing request" + httpPost.getRequestLine()); CloseableHttpResponse response = httpclient.execute(httpPost); try { HttpEntity entity = response.getEntity(); System.out.println(response.getStatusLine()); if (entity != null) { // 从request中取得输入流 InputStream inputStream = entity.getContent(); // 读取输入流 SAXReader reader = new SAXReader(); Document document = reader.read(inputStream); // 得到xml根元素 Element root = document.getRootElement(); // 得到根元素的所有子节点 List<Element> elementList = root.elements(); // 遍历所有子节点 for (Element e : elementList) { result.put(e.getName(), e.getText()); } // 释放资源 inputStream.close(); } EntityUtils.consume(entity); } finally { if(response!=null) { response.close(); } } } catch (Exception e){ e.printStackTrace(); }finally { try { httpclient.close(); } catch (Exception e){ e.printStackTrace(); } } logger.info("------------------发送红包结束---------------"); logger.info("发送红包微信返回的信息是:"+JSON.toJSONString(result)); //假如发送成功的话,保存发送的信息 if(result.get("return_msg").equals("发放成功")) { logger.info("红包发放成功openid="+openid+",发送时间是:"+CommonUtil.getPreDay(new Date(),0)); return ApiResponse.buildSuccessResponse(ResultConstant.OPERATOR_SUCCESS,ResultConstant.MESSAGE_OPERATE_SUCCESS,result); } else { logger.info("红包发放失败,openid="+openid+",发送时间是:"+CommonUtil.getPreDay(new Date(),0)); return ApiResponse.buildFailResponse(ResultConstant.OPERATOR_FAIL,ResultConstant.MESSAGE_OPERATE_FAIL); } } catch (Exception e){ logger.info("发送红包异常,异常信息是:"+e.getMessage()); return ApiResponse.buildFailResponse(ResultConstant.OPERATOR_FAIL,ResultConstant.MESSAGE_SYSTEM_EXCEPTION); } }
其中,由于学习微信开发的时候看的参考书籍是柳峰老师的微信开发教程,所以工具类的名称是一样的,在这里把这里需要的一些方法罗列在下面(代码中需要的从上往下排列,在开发微信的其他模块也可能用的上的):
/** * 获取当前时间 yyyyMMddHHmmss * @return String */ public static String getCurrTime() { Date now = new Date(); SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss"); String s = outFormat.format(now); return s; }
/** * 取出一个指定长度大小的随机正整数. * * @param length int 设定所取出随机数的长度。length小于11 * @return int 返回生成的随机数。 */ public static int buildRandom(int length) { int num = 1; double random = Math.random(); if (random < 0.1) { random = random + 0.1; } for (int i = 0; i < length; i++) { num = num * 10; } return (int) ((random * num)); }
/** * 获取32位随机字符串 * * @return */ public static String getNonceStr() { Random random = new Random(); return MD5Util.MD5Encode(String.valueOf(random.nextInt(10000)), "UTF-8"); }
/** * 微信支付sign签名 * @param characterEncoding * @param parameters * @return */ public static String createSign(String characterEncoding, SortedMap<Object, Object> parameters,String api_key) { StringBuffer sb = new StringBuffer(); Set<Map.Entry<Object, Object>> es = parameters.entrySet(); Iterator<Map.Entry<Object, Object>> it = es.iterator(); while (it.hasNext()) { Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) it.next(); String k = (String) entry.getKey(); Object v = entry.getValue(); /** 如果参数为key或者sign,则不参与加密签名 */ if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } System.out.println("传过来的支付密钥api_key="+api_key); /** 支付密钥必须参与加密,放在字符串最后面 */ sb.append("key=" + api_key); /** 记得最后一定要转换为大写 */ String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; }
/** * 将请求参数转换为xml格式的string * @param parameters * @return */ public static String getRequestXml(SortedMap<Object, Object> parameters) { StringBuffer sb = new StringBuffer(); sb.append("<xml>"); Set<Map.Entry<Object, Object>> es = parameters.entrySet(); Iterator<Map.Entry<Object, Object>> it = es.iterator(); while (it.hasNext()) { Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) it.next(); String k = (String) entry.getKey(); String v = entry.getValue() + ""; if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) { sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">"); } else { sb.append("<" + k + ">" + v + "</" + k + ">"); } } sb.append("</xml>"); return sb.toString(); }
/** * 获取系统前一天的信息(amount=-1),当前时间(amount=0),后一天(amount=1) * @param date * @return */ public static String getPreDay(Date date,Integer amount) { SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Calendar calendar = Calendar.getInstance(); calendar.setTime(date); calendar.add(Calendar.DAY_OF_MONTH, amount); date = calendar.getTime(); String value = sdf.format(date); return value.split(" ")[0]; }
其中一些需要注意的,比如api_key(即下图当中的API密钥),商户支付证书(下图中的下载证书)在你们的商户平台去设置和下载。
去产品中心-我的产品看下你的现金红包是否已经开通了,假如未开通有未开通的提示信息,然后去开通就行了。
点击这个现金红包,可以给现金红包进行一些设置,比如只允许某个IP调用现金红包API(感觉这个很重要)。
以上这些都弄好了,设置好了,去给自己发个红包吧!!!