微信小程序领取卡券 java

最近作了个领取微信卡券的小程序,看了不少文档资料以及花了不少时间才算搞定的,不过也算是好事多磨,这边记录分享一下,也算给一点提高。javascript

1、开发前准备前端

1:申请 微信公众号 和 微信小程序,这是两个不一样的东西,都须要单独申请、不一样的账号;java

2:微信公众号须要开通微信卡券的功能;json

3:在微信公众号里面去绑定微信小程序;小程序

4:申请微信开放平台,并将微信公众号 和 微信小程序绑定到该开放平台。(注:绑定到开发平台下的做用只是为了获取unionid,由于同一用户在 微信公众号 和 小程序下得到的openid是不同的,若是微信公众号 和 小程序都须要领取卡券,则最好经过unionid来跟踪用户;若是你只是开发微信小程序的领取卡券,则彻底能够忽略第4点,博主本人也没有去绑定到微信开放平台,感受步骤好多,特别麻烦!)后端


2、开始开发微信小程序

1:获取微信卡券api

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025272缓存

这边能够直接经过微信公众号提供的接口获取或者建立微信的卡券,此处不过多介绍,只是提一下这边要获取的access_token,网址以下https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183,代码直接以下:服务器

private static String grantType = "client_credential";
	public static String appId = "";			//微信公众号appid
    	public static String secret = "";			//微信公众号密钥
	public static AccessToken token = null;			//微信公众号的accessToken对象,因为请求次数有限制,这里使用全局静态变量保存起来
	public static AccessToken getToken() throws WeixinException, JsonParseException, JsonMappingException, IOException{
		if(token == null || token.getExpires_in() < System.currentTimeMillis()){
			//拼接参数
			String param = "?grant_type=" + grantType + "&appid=" + appId + "&secret=" + secret;
			//建立请求对象
    			HttpsClient http = new HttpsClient();
    			//调用获取access_token接口
    			Response res = http.get("https://api.weixin.qq.com/cgi-bin/token" + param);
    			System.out.println(res.asString());
    			ObjectMapper mapper = new ObjectMapper();
	    		token = mapper.readValue(res.asString(),AccessToken.class);
		}
    		return token;
	}

其中须要jackson和weixin4j的jar包,比较广泛,请自行下载;而AccessToken对象也比较简单,就errcode、errmsg、access_token、expires_in这四个参数,比较简单,在文章结尾贴代码


2:升级微信卡券

其实这个步骤也能够省略,升级微信卡券的目的是能够直接从微信卡券跳转到对应的小程序,博主就偷懒了,直接跳过了这个步骤;

不过升级卡券也比较简单,就是调用调用微信公众号的更改微信卡券接口(URL:https://api.weixin.qq.com/card/update?access_token=TOKEN),添加几个字段,能够参考微信官方文档3.1,连接以下:https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncement&key=1490190158&version=1&lang=zh_CN&platform=2


3:领取卡券

3.1:先获取openId

小程序端代码,经过调用wx.login获取code,再调用https://api.weixin.qq.com/sns/jscode2session接口获取openid,博主看到不少例子是直接从小程序端调用这个接口,但我事实中发现是行不通的,由于这个域名没法添加到小程序的request合法域名中,微信给的说明是不要在前端调用这个接口,须要经过后台,那没办法喽

wx.login({
          success: function (res) {
            var service_url = 'https://???/???/weixin/api/login?code=' + res.code;//须要将服务器域名添加到小程序的request合法域名中,并且必须是https开头
            wx.request({
              url: l,
              data: {},
              method: 'GET',
              success: function (res) {
                console.log(res);
                if (res.data != null && res.data != undefined && res.data != '') {
                  wx.setStorageSync("openid", res.data.openid);//将获取的openid存到缓存中
                }
              }
            });
          }
        });

后端java代码

/**
     * 小程序后台登陆,向微信平台发送获取access_token请求,并返回openId
     * @param code
     * @return 用户凭证
     * @throws WeixinException
     * @throws IOException 
     * @throws JsonMappingException 
     * @throws JsonParseException 
     */
    @RequestMapping("login")
    @ResponseBody
    public Map<String, Object> login(String code, HttpServletRequest request) throws WeixinException, JsonParseException, JsonMappingException, IOException {
        if (code == null || code.equals("")) {
            throw new WeixinException("invalid null, code is null.");
        }
        
        Map<String, Object> ret = new HashMap<String, Object>();
        //拼接参数
        String param = "?grant_type=" + grant_type + "&appid=" + appid + "&secret=" + secret + "&js_code=" + code;
        
        System.out.println("https://api.weixin.qq.com/sns/jscode2session" + param);
        
        //建立请求对象
        HttpsClient http = new HttpsClient();
        //调用获取access_token接口
        Response res = http.get("https://api.weixin.qq.com/sns/jscode2session" + param);
        //根据请求结果断定,是否验证成功
        JSONObject jsonObj = res.asJSONObject();
        if (jsonObj != null) {
            Object errcode = jsonObj.get("errcode");
            if (errcode != null) {
                //返回异常信息
                throw new WeixinException(getCause(Integer.parseInt(errcode.toString())));
            }
            
            ObjectMapper mapper = new ObjectMapper();
            OAuthJsToken oauthJsToken = mapper.readValue(jsonObj.toJSONString(),OAuthJsToken.class);
            ret.put("openid", oauthJsToken.getOpenid());
        }
        return ret;
    }

其中OAuthJsToken对象的字段为:openid、expires_in、session_key(会话密钥) ,在文章结尾贴代码;

 

3.2:生成领取卡券的签名,并调用wx.addCard方法领取卡券

这边写贴java后端代码

public static ApiTicket ticket = null;//使用全局静态变量存储ApiTicket对象,固然若是使用缓存框架保存固然更好,这边只是作一个简单示例
	/**
	 * @Description: 获取领取卡券获取签名等参数
	 * @param cardId:须要领取的卡券的cardId
	 * @return
	 * @throws WeixinException
	 * @throws JsonParseException
	 * @throws JsonMappingException
	 * @throws IOException
	 */
	@RequestMapping("getCardSign")
	@ResponseBody
	public Map<String, String> getCardSign(String cardId) throws WeixinException, JsonParseException, JsonMappingException, IOException{
		Map<String, String> ret = new HashMap<String, String>();
		//先要获取api_ticket,因为请求api_ticket的接口访问有次数限制,因此最好将得到到的api_ticket保存到缓存中,这边作法比较简单,直接使用的静态变量
		if(ticket == null || ticket.getExpires_in() < System.currentTimeMillis()){
			//建立请求对象
	        	HttpsClient http = new HttpsClient();
	        
	        	ObjectMapper mapper = new ObjectMapper();
	        
	        	AccessToken token = OpenApi.getToken();//这里获取的token就是最上方代码保存的微信公众号全局静态变量token
       	 		
       	 		//经过access_token调用获取api_ticket接口
	    		Response res = http.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + token.getAccess_token() + "&type=wx_card");
	    		System.out.println(res.asString());
	    		ticket = mapper.readValue(res.asString(), ApiTicket.class);
       		}
       
       		ret = sign(ticket.getTicket(), cardId);//生成领取卡券须要的签名,并返回相关的参数
 
       		for (Map.Entry entry : ret.entrySet()) {
           		System.out.println(entry.getKey() + ", " + entry.getValue());
       		}
       		return ret;
	}
	/**
	 * @Description: 生成卡券须要的签名并返回参数
	 * @param api_ticket:
	 * @param cardId:须要领取的卡券的cardId
	 * @return
	 */
	public static Map<String, String> sign(String api_ticket, String cardId) {
        Map<String, String> ret = new HashMap<String, String>();
        String nonce_str = create_nonce_str();
        String timestamp = create_timestamp();
        String signature = "";
        
        String param[] = new String[4];
        
        param[0] = nonce_str;
        param[1] = timestamp;
        param[2] = api_ticket;
        param[3] = cardId;
        
        Arrays.sort(param);//对参数的value值进行字符串的字典序排序
        
        StringBuilder sb = new StringBuilder();
	    for(String b : param){
	    	sb.append(b);
	    }
	    System.out.println(sb);
        //对上面拼接的字符串进行sha1加密,获得signature
        try{
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(sb.toString().getBytes("UTF-8"));
            signature = byteToHex(crypt.digest());
        }catch (NoSuchAlgorithmException e){
            e.printStackTrace();
        }catch (UnsupportedEncodingException e){
            e.printStackTrace();
        }
 
	    //返回领取卡券须要的参数,其中nonceStr和timestamp必须和签名中的保持一致
        ret.put("card_id", cardId);
        ret.put("api_ticket", api_ticket);
        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);
 
        return ret;
    }

其中ApiTicket对象的属性有:errcode、errmsg、ticket、expires_in,在文章结尾贴出该代码

再贴小程序端代码

var that = this;
    var service_url = 'https://???/???/weixin/api/getCardSign?cardId=' + cardId;//须要将服务器域名添加到小程序的request合法域名中,并且必须是https开头
    wx.request({
      url: service_url,
      data: {},
      method: 'GET',
      success: function (res) {
        console.log(res);
          wx.addCard({
            cardList: [{
              cardId: that.data.cardId,
              cardExt: '{"code":"","openid":"","timestamp":' + res.data.timestamp + ',"nonce_str":"' + res.data.nonceStr + '","signature":"' + res.data.signature + '"}'
            }],//这里须要注意的是cardExt参数的value值是 String类型,不要使用对象发送;另外openid若是在建立优惠券的时候没有指定,则这边为空,千万不要填写当前用户的openid
            success: function (result) {
              console.log(res);
 
              wx.showToast({
                title: '领取成功',
                icon: 'success',
                duration: 2000
              });
            },
            fail: function (res) {
              console.log('领取失败');
              console.log(res);
            }
          })
        
      }
    });

ok,若是领取成功,能够直接到微信卡包里面查看。下面贴出AccessToken、ApiTicket、OAuthJsToken的java模型代码

public class BaseResponse {
	private int errcode;
	private String errmsg;
	
	public int getErrcode() {
		return errcode;
	}
	public void setErrcode(int errcode) {
		this.errcode = errcode;
	}
	public String getErrmsg() {
		return errmsg;
	}
	public void setErrmsg(String errmsg) {
		this.errmsg = errmsg;
	}
}
public class AccessToken extends BaseResponse{
	private String access_token;
	private long expires_in;
	
	public String getAccess_token() {
		return access_token;
	}
	public void setAccess_token(String access_token) {
		this.access_token = access_token;
	}
	public long getExpires_in() {
		return expires_in;
	}
	public void setExpires_in(long expires_in) {
		this.expires_in = System.currentTimeMillis() + (expires_in - 100) * 1000;//原expires_in是有效时长,好比:7200,现改成过时的时间戳
	}
}
public class ApiTicket extends BaseResponse{
	private String ticket;
	private long expires_in;
	
	public String getTicket() {
		return ticket;
	}
	public void setTicket(String ticket) {
		this.ticket = ticket;
	}
	public long getExpires_in() {
		return expires_in;
	}
	public void setExpires_in(long expires_in) {
		this.expires_in = System.currentTimeMillis() + (expires_in - 100) * 1000;//原expires_in是有效时长,好比:7200,现改成过时的时间戳
	}
}
public class OAuthJsToken {
	private String openid;				//用户惟一标识
	private int expires_in = 7200;		//凭证有效时间,单位:秒
	private String session_key;			//会话密匙
	private long exprexpiredTime;			//过时时间
	
	public String getOpenid() {
		return openid;
	}
	public void setOpenid(String openid) {
		this.openid = openid;
	}
	public int getExpires_in() {
		return expires_in;
	}
	public void setExpires_in(int expires_in) {
		this.expires_in = expires_in;
		this.exprexpiredTime = System.currentTimeMillis() + expires_in * 1000;
	}
	public String getSession_key() {
		return session_key;
	}
	public void setSession_key(String session_key) {
		this.session_key = session_key;
	}
	
	public long getExprexpiredTime() {
		return exprexpiredTime;
	}
	public void setExprexpiredTime(long exprexpiredTime) {
		this.exprexpiredTime = exprexpiredTime;
	}
	/**
     * 判断用户凭证是否过时
     *
     * @return 过时返回 true,不然返回false
     */
    public boolean isExprexpired() {
    	return System.currentTimeMillis() >= this.exprexpiredTime;
    }
}
相关文章
相关标签/搜索