微信红包接口实现

应用场景:

       网站某一类型注册类型会员通过微信公众账号进入其账号中心时,可以获取一次领取红包资格,红包资格在某一个特定范围内。

微信红包调用流程:

       后台API调用:待进入联调过程时与开发进行详细沟通;

      告知服务器:告知服务器接收微信红包的用户openID,告知服务器该用户获得的金额;

      从商务号扣款:服务器获取信息后从对应的商务号扣取对应的金额;

      调用失败:因不符合发送规则,商务号余额不足等原因造成调用失败,反馈至调用方;

      发送成功:以微信红包公众账号发送对应红包至对应用户;

实现过程:

       当用户点击领取红包按钮时,通过微信提供的 网页授权获取用户基本信息 不弹出授权页面方式获取用户的openid,根据获取到的openid判断用户是否有资格获取红包,针对有资格的用户,随机生成一个随机范围内的红包金额,并锁定该金额,通过微信提供的接口与微信服务器交互,根据微信服务器的返回结果释放锁定的金额。

      数据库方面涉及两个表,一个表(hongbao)用来记录每个用户领取过的红包详情,一个表(c_hongbao)用来保存本次活动发放的红包总金额,已经发放的红包金额和剩余的红包金额。当对hongbao表操作时,通过触发器修改c_hongbao的值。 

hongbao表

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Entity  
  2. @Table(name = "hongbao")  
  3. @Proxy(lazy = false)  
  4. @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)  
  5. public class Hongbao implements Serializable {  
  6.     private Long hongBaoId;    // 红包表主键  
  7.     private String billNo;     // 红包订单号  
  8.     private String openid;     // 领取红包的用户ID  
  9.     private Integer amount;    // 领取红包金额 单位分  
  10.     private Date addTime;      // 添加时间  
  11.     private Integer result;    // 领取红包结果 0失败 1成功 2锁定  
  12.     private String remark;     // 备注  用于保存微信返回的json  
  13. }  
c_hongbao表

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Entity  
  2. @Table(name = "c_hongbao")  
  3. @Proxy(lazy = false)  
  4. @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)  
  5. public class CHongbao implements Serializable {  
  6.     private Long cHongbaoId;   
  7.     private Integer total;     //   红包总金额  单位分  
  8.     private Integer balance;   //   红包余额 单位分  
  9.     private Integer send;      //   已经发送红包金额 单位分  
  10. }  
触发器

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. DELIMITER $$  
  2. CREATE  
  3.     TRIGGER insert_hongbao AFTER INSERT  
  4.     ON hongbao  
  5.     FOR EACH ROW BEGIN  
  6.            update c_hongbao set balance = balance - new.amount , send = send + new.amount ;  
  7.     END$$  
  8. DELIMITER ;  
  9. DELIMITER $$  
  10. CREATE  
  11.     TRIGGER update_hongbao AFTER UPDATE  
  12.     ON hongbao  
  13.     FOR EACH ROW BEGIN  
  14.           if new.result = 0 then    
  15.             update c_hongbao set balance = balance + old.amount , send = send - old.amount ;  
  16.           end if;   
  17.     END$$  
  18. DELIMITER ;  
生成红包随机金额方法

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.        /** 
  2.  * 生成随机红包金额 
  3.  * @param openid 
  4.  * @param billNo 
  5.  * @return 
  6.  */  
  7. public synchronized  Hongbao getAmount(String openid,String billNo){  
  8.     //该用户获取的随机红包金额  
  9.     int amount = (int) Math.round(Math.random()*(HongBaoUtil.MAX_VALUE-HongBaoUtil.MIN_VALUE)+HongBaoUtil.MIN_VALUE);  
  10.     StringBuffer hql = new StringBuffer("from CHongbao");  
  11.     //商户的红包总余额  
  12.     CHongbao po = service.queryOneByHQL(hql.toString());  
  13.     //如果此次随机金额比商户红包余额还要大,则返回商户红包余额  
  14.     if(amount > po.getBalance()){  
  15.         amount =  po.getBalance();  
  16.     }  
  17.     Hongbao hongbao = new Hongbao();  
  18.     hongbao.setAddTime(new Date());  
  19.     hongbao.setAmount(amount);  
  20.     hongbao.setOpenid(openid);  
  21.     hongbao.setResult(HongBaoUtil.LOCK);  
  22.     hongbao.setBillNo(billNo);  
  23.     //先锁定用户领取的金额,防止领取金额超过预算金额  
  24.     service.save(hongbao);  
  25.     return hongbao;  
  26. }  


HongBaoUtil类

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * @author userwyh 
  3.  * @date 2015年3月5日  
  4.  * @time 上午11:53:40  
  5.  * @version 1.0 
  6.  */  
  7. public class HongBaoUtil {  
  8.     public static final String MCH_ID = "XX";      //商户号  
  9.     public static final String WXAPPID = "XX";     //公众账号appid  
  10.     public static final String NICK_NAME = "XX";   //提供方名称  
  11.     public static final String SEND_NAME = "XX";   //商户名称  
  12.     public static final int MIN_VALUE = 100;       //红包最小金额 单位:分  
  13.     public static final int MAX_VALUE = 200;       //红包最大金额 单位:分  
  14.     public static final int TOTAL_NUM = 1;         //红包发放人数  
  15.     public static final String WISHING = "XX";     //红包祝福语  
  16.     public static final String CLIENT_IP = "XX";   //调用接口的机器IP  
  17.     public static final String ACT_NAME = "XX";    //活动名称  
  18.     public static final String REMARK = "XX";      //备注  
  19.     public static final String KEY = "XX";         //秘钥  
  20.       
  21.     public static final int FAIL = 0;              //领取失败  
  22.     public static final int SUCCESS = 1;           //领取成功  
  23.     public static final int LOCK = 2;              //已在余额表中锁定该用户的余额,防止领取的红包金额大于预算  
  24.       
  25.     /** 
  26.      * 对请求参数名ASCII码从小到大排序后签名 
  27.      * @param params 
  28.      */  
  29.     public static void sign(SortedMap<String, String> params){  
  30.         Set<Entry<String,String>> entrys=params.entrySet();    
  31.         Iterator<Entry<String,String>> it=entrys.iterator();    
  32.         StringBuffer result = new StringBuffer();  
  33.         while(it.hasNext())    
  34.         {    
  35.            Entry<String,String> entry=it.next();    
  36.            result.append(entry.getKey())  
  37.                  .append("=")  
  38.                  .append(entry.getValue())  
  39.                  .append("&");  
  40.         }    
  41.         result.append("key=").append(KEY);  
  42.         params.put("sign", MD5Util.MD5(result.toString()));  
  43.     }  
  44.     /** 
  45.      * 生成提交给微信服务器的xml格式参数 
  46.      * @param params 
  47.      * @return 
  48.      */  
  49.     public static String getRequestXml(SortedMap<String,String> params){  
  50.             StringBuffer sb = new StringBuffer();  
  51.                 sb.append("<xml>");  
  52.                 Set es = params.entrySet();  
  53.                 Iterator it = es.iterator();  
  54.                 while(it.hasNext()) {  
  55.                      Map.Entry entry = (Map.Entry)it.next();  
  56.                      String k = (String)entry.getKey();  
  57.                      String v = (String)entry.getValue();  
  58.                      if ("nick_name".equalsIgnoreCase(k)||"send_name".equalsIgnoreCase(k)||"wishing".equalsIgnoreCase(k)||"act_name".equalsIgnoreCase(k)||"remark".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {  
  59.                          sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");  
  60.                       }else {  
  61.                          sb.append("<"+k+">"+v+"</"+k+">");  
  62.                       }  
  63.                  }  
  64.                 sb.append("</xml>");  
  65.                 return sb.toString();  
  66.         }  
  67.     /** 
  68.      * 创建map 
  69.      * @param billNo 
  70.      * @param openid 
  71.      * @param userId 
  72.      * @param amount 
  73.      * @return 
  74.      */  
  75.     public static SortedMap<String, String> createMap(String billNo,String openid,String userId,int amount){  
  76.         SortedMap<String, String> params = new TreeMap<String, String>();  
  77.         params.put("wxappid",WXAPPID);  
  78.         params.put("nonce_str",createNonceStr());  
  79.         params.put("mch_billno",billNo);  
  80.         params.put("mch_id", MCH_ID);  
  81.         params.put("nick_name", NICK_NAME);  
  82.         params.put("send_name", SEND_NAME);  
  83.         params.put("re_openid", openid);  
  84.         params.put("total_amount", amount+"");  
  85.         params.put("min_value", amount+"");  
  86.         params.put("max_value", amount+"");  
  87.         params.put("total_num", TOTAL_NUM+"");  
  88.         params.put("wishing", WISHING);  
  89.         params.put("client_ip", CLIENT_IP);  
  90.         params.put("act_name", ACT_NAME);  
  91.         params.put("remark", REMARK);  
  92.         return params;  
  93.     }  
  94.     /** 
  95.      * 生成随机字符串 
  96.      * @return 
  97.      */  
  98.     public static String createNonceStr() {  
  99.                return UUID.randomUUID().toString().toUpperCase().replace("-""");  
  100.     }  
  101.     /** 
  102.      * 生成商户订单号 
  103.      * @param mch_id  商户号 
  104.      * @param userId  该用户的userID 
  105.      * @return 
  106.      */  
  107.     public static String createBillNo(String userId){  
  108.         //组成: mch_id+yyyymmdd+10位一天内不能重复的数字  
  109.         //10位一天内不能重复的数字实现方法如下:  
  110.         //因为每个用户绑定了userId,他们的userId不同,加上随机生成的(10-length(userId))可保证这10位数字不一样  
  111.         Date dt=new Date();  
  112.         SimpleDateFormat df = new SimpleDateFormat("yyyymmdd");  
  113.         String nowTime= df.format(dt);  
  114.         int length = 10 - userId.length();  
  115.         return MCH_ID + nowTime + userId + getRandomNum(length);  
  116.     }  
  117.     /** 
  118.      * 生成特定位数的随机数字 
  119.      * @param length 
  120.      * @return 
  121.      */  
  122.     private static String getRandomNum(int length) {  
  123.         String val = "";  
  124.         Random random = new Random();  
  125.         for (int i = 0; i < length; i++) {  
  126.             val += String.valueOf(random.nextInt(10));  
  127.         }  
  128.         return val;  
  129.     }  
  130.         /** 
  131.      * post提交到微信服务器 
  132.      * @param requestXML 
  133.      * @param instream   
  134.      * @return 
  135.      * @throws NoSuchAlgorithmException 
  136.      * @throws CertificateException 
  137.      * @throws IOException 
  138.      * @throws KeyManagementException 
  139.      * @throws UnrecoverableKeyException 
  140.      * @throws KeyStoreException 
  141.      */  
  142.     public static String post(String requestXML,InputStream instream) throws NoSuchAlgorithmException, CertificateException, IOException, KeyManagementException, UnrecoverableKeyException, KeyStoreException{  
  143.         KeyStore keyStore  = KeyStore.getInstance("PKCS12");  
  144.         try {  
  145.             keyStore.load(instream, MCH_ID.toCharArray());  
  146.         }  finally {  
  147.             instream.close();  
  148.         }  
  149.         SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, MCH_ID.toCharArray()).build();  
  150.         SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(  
  151.                 sslcontext,  
  152.                 new String[] { "TLSv1" },  
  153.                 null,  
  154.                 SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);  
  155.         CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();  
  156.         String result = "";  
  157.         try {  
  158.             HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack");  
  159.             StringEntity  reqEntity  = new StringEntity(requestXML,"utf-8"); //如果此处编码不对,可能导致客户端签名跟微信的签名不一致  
  160.             reqEntity.setContentType("application/x-www-form-urlencoded");   
  161.             httpPost.setEntity(reqEntity);  
  162.             CloseableHttpResponse response = httpclient.execute(httpPost);  
  163.             try {  
  164.                 HttpEntity entity = response.getEntity();  
  165.                 if (entity != null) {  
  166.                     BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(),"UTF-8"));  
  167.                     String text;  
  168.                     while ((text = bufferedReader.readLine()) != null) {  
  169.                         result +=text;  
  170.                     }  
  171.                 }  
  172.                 EntityUtils.consume(entity);  
  173.             } finally {  
  174.                 response.close();  
  175.             }  
  176.         } finally {  
  177.             httpclient.close();  
  178.         }  
  179.         return result;  
  180.     }  

接受请求的Action

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.        @Action(value = "hongbao", results = {  
  2.        @Result(name = "success", location = "/html/hongbao/result.jsp") }, interceptorRefs = { @InterceptorRef("myDefaultInterceptorStack") })  
  3. public String hongbao() {  
  4.     String url = WeixinUtil.getUrl();  
  5.     if (StringUtils.isNotEmpty(code)) {  
  6.             url = url.replace("CODE", code);  
  7.         Map<String, Object> map = JsonUtil.getMapByUrl(url);   
  8.             String openid =map.get("openid").toString();  
  9.         // 若openid读取有误,则跳转到不合法的页面  
  10.         if (StringUtils.isNotEmpty(openid)) {  
  11.             StringBuffer hql = new StringBuffer();  
  12.             hql.append(" from Bind b where 1=1 ");  
  13.             hql.append(" and b.openid = '");  
  14.             hql.append(openid).append("'");  
  15.             List<Bind> list = service.queryListByHQL(hql.toString());  
  16.             if (list != null && list.size() > 0) {  
  17.                 Bind user = list.get(0);  
  18.                 //用户类型  
  19.                 String userType = user.getRegisterusertypeid().toString();  
  20.                 //只允许MASTER_TYPE类型标识的用户参与红包领取  
  21.                 if(userType.equals(AppKeysUtil.MASTER_TYPE)){  
  22.                     hql.setLength(0);  
  23.                     hql.append("from Hongbao where openid = '").append(openid).append("' order by hongBaoId desc");  
  24.                     SystemLogger.debug(hql.toString());  
  25.                         //默认没有领取过红包  
  26.                     boolean isReceive = false;  
  27.                     List<Hongbao> hongBaoList = dao.queryListByHQL(hql.toString());  
  28.                    if(hongBaoList!=null&&hongBaoList.size()>0){  
  29.                       //最近一条记录领取成功或者处于锁定状态  
  30.                       if(hongBaoList.get(0).getResult()!=0){  
  31.                         isReceive = true;  
  32.                        }  
  33.                    }  
  34.                        if(!isReceive){  
  35.                         String billNo = HongBaoUtil.createBillNo(user.getUserid()+"");  
  36.                         Hongbao hongbao = getAmount(openid,billNo);  
  37.                         SortedMap<String, String> map = HongBaoUtil.createMap(billNo, openid, user.getUserid()+"",hongbao.getAmount());  
  38.                         HongBaoUtil.sign(map);  
  39.                         String requestXML = HongBaoUtil.getRequestXml(map);  
  40.                         try {  
  41.                             HttpServletRequest request = ServletActionContext.getRequest();  
  42.                             //加载微信提供给商户的证书  
  43.                             InputStream instream = request.getSession().getServletContext().getResourceAsStream("XXXXXXXXXXXXXXXXXXX");   
  44.                             //与微信交互并接收返回参数  
  45.                             String responseXML = HongBaoUtil.post(requestXML,instream);  
  46.                             //将微信返回的xml格式参数转成Map  
  47.                             Map<String,String> resultMap = XmlUtil.parseXml(responseXML);  
  48.                             String return_code = resultMap.get("return_code").toString();  
  49.                             if("SUCCESS".equals(return_code)){  
  50.                                 hongbao.setResult(HongBaoUtil.SUCCESS);  
  51.                                 message = "红包已经发送";  
  52.                             }else{  
  53.                                 hongbao.setResult(HongBaoUtil.FAIL);  
  54.                                 message = "领取红包失败,请重试";  
  55.                             }  
  56.                             hongbao.setRemark(responseXML);  
  57.                             service.update(hongbao);      
  58.                         } catch (KeyManagementException e) {  
  59.                             e.printStackTrace();  
  60.                             hongbao.setResult(HongBaoUtil.FAIL);  
  61.                             message = "领取红包失败,请重试";  
  62.                             service.update(hongbao);  
  63.                         }   
  64.                     }else{  
  65.                         message = "已经领取过红包";  
  66.                     }  
  67.                 }else{  
  68.                     message = "该用户类型不能参与红包领取";  
  69.                 }  
  70.             } else {  
  71.                 message = "没有关注";  
  72.             }  
  73.         }//StringUtils.isNotEmpty(openid)  
  74.     }     
  75.     return SUCCESS;  
  76. }  

测试结果:

       随机在100-200分之间生成的一个随机数,写了几百行代码换来的红包

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值