客户的要求如下:

1.在小程序中支付成功后获得一张卡券

2.用户凭卡券在店内核销

根据以上需求,折腾了我好几天(客户一直忙照顾店里生意,扫个码都要等两天)。

首先客户提供了微信公众号的账号密码给我。我登录进去,也把自己添加为运营者了。

首先声明我这里不是用接口创建卡券的

然后在左侧选择添加功能插件,当然客户是已经完成商户号、小程序和公众号的认证。

小程序卡券开发

找到下面这个功能插件

小程序卡券开发

点进去首先是应该提交商户信息,因为我的已经提交了所以没有办法截图了,这也没什么难点。提交之后会在3个工作日内审核。

审核通过之后左侧栏就有卡券功能了。点击进入。

小程序卡券开发

注意上方的右侧有一个代制模式或者自制模式,我们选择自制模式。

然后选择“优惠券”。

小程序卡券开发

点击在下方的新建优惠券。然后让你选择券的类型。按我客户的需求,我选择兑换券。

小程序卡券开发

然后填入参数,注意他有个个人领取数量限制和一个库存量。填完之后提交审核,然后秒通过。

然后回到“优惠券”那里就会像我的一样多了一行,我们还可以对其进行修改。

我们先点详情,获取卡号

小程序卡券开发

拿到卡号存好。接下来我们要获取公众号开发的权限。

在左侧的最底下

小程序卡券开发

存好appid和秘钥(需要管理员扫码)

小程序卡券开发

然后注意我们还要添加IP白名单,不然是获取不到token的。我们先把自己的服务器的IP(注意是IP不是域名)加进去,然后再把本地开发的IP加进去,当然本地的IP我们现在可能还不知道(并不是192.168什么的,是公网IP)。没关系,先保存。

以上操作都是在公众平台上面的,现在我们回到代码开发。我们理一下卡券从生成到核销的整个过程:

我们先获取公众号的token(不是小程序的),然后用这个token获得ticket,然后用ticket去加密获取签名,得到签名之后发送给前端,前端调用wx.addCard()方法来进入一个领取页面,然后用户点击领取之后前端会获得一个加密过的code,让前端把code发送给我们,我们拿到code之后再发送请求到微信服务器去解密,解密后拿到真实的code(就是卡券上面那个号码),当用户拿着卡券到店里核销的时候,相当于把code告诉我们,我们根据code去查到这个订单的内容,然后发起核销请求到微信服务器。

1.数据准备,我们先准备一个用来接收返回结果的实体(getset省写)

public class XcxJson {

   String session_key;
   String openid;// 小程序的openid
   String access_token;// 小程序或公众号的token
   String ticket;//卡券
   String expires_in;//有效时间:秒
   String code;//卡券的code

   Integer errcode;//错误码
   String errmsg;//错误信息

}

2.编写获取token的方法(JSON用的是阿里巴巴的FastJson)

/**
 * 获取公众号的token
 * @param appid   公众号的appid
 * @param secret  公众号的秘钥
 *
 * @return
 */
static public String selectToken(String appid, String secret) {
   HttpRequestor requestor = new HttpRequestor();
   Map<String, String> map = new HashMap<String, String>();
   map.put("grant_type", "client_credential");
   map.put("appid", appid);
   map.put("secret", secret);
   String res = null;
   String token = "error";
   String url="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
   url+="&appid="+appid;
   url+="&secret="+secret;
   try {
      res = requestor.doGet(url);
      System.out.println(res);
      XcxJson json = JSON.parseObject(res, XcxJson.class);
      token = json.getAccess_token();
   } catch (Exception e) {
      e.printStackTrace();
   }
   return token;
}

3.编写获取ticket的方法(JSON用的是阿里巴巴的FastJson)

/**
 * 获取公众号的ticket
 * @param token  公众号的token
 *
 * @return error-获取失败
 */
static public String selectTicket(String token) {
   String ticket ="error";
   try {
      String res = HttpRequestor.doGet("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+token+"&type=wx_card");
      System.out.println(res);
      XcxJson json = JSON.parseObject(res, XcxJson.class);
      if(json.getErrcode()==0){
         ticket = json.getTicket();
      }
   } catch (Exception e) {
      e.printStackTrace();
   }
   return ticket;
}

4.有了ticket之后,我们提供给前端小程序card_id、timestamp、nonce_str、signature。当然如果需要指定领取用户就让前端先发openid过来。然后我们要计算signature。下面是我用java.util.UUID随机生成nonce_str,然后时间戳要除以1000,之后用nonce_str、ticket、timestamp、card_id计算签名,当然我这里是限定了领取用户的,所以加了openid,如果不需要,请把所有openid都去掉。

public JSONObject select(String openid) {
   String token = xcxService.getGzhToken("gzh_token");
   String ticket =xcxService.getTicket("gzh_ticket",token);
   Long timestamp = System.currentTimeMillis()/1000;
   String card_id="";
   String noncestr = UUID.randomUUID().toString().replaceAll("-","");
   String sign = getSignature(noncestr,ticket,timestamp,card_id,openid);
   JSONObject object = new JSONObject();
   object.put("noncestr",noncestr);
   object.put("timestamp",timestamp);
   object.put("sign",sign);
   object.put("card_id",card_id);
   return object;
}

5.生成签名必须先将值进行排序然后拼接在一起,最后用SHA1加密。生成签名之后连其他参数应该传给前端

/**
 * 生成加密签名,用于微信卡券
 * @param noncestr   随机字符串
 * @param jsapi_ticket   小程序ticket
 * @param timestamp   时间戳
 * @param card_id   卡券ID
 * @param openid 领券用户的Openid
 * @return
 */
public  String getSignature(String noncestr,String jsapi_ticket,Long timestamp,String card_id,String openid){
   List<String> params=new ArrayList<String>();
   params.add(jsapi_ticket);
   params.add(timestamp+"");
   params.add(noncestr);
   params.add(card_id);
   params.add(openid);
   Collections.sort(params);
   String sign = "";
   for(String str:params){
      sign+=str;
   }
   // System.out.println(sign);
   String sha = org.apache.commons.codec.digest.DigestUtils.sha1Hex(sign);
   return sha;
}

6.前端拿到这几个参数之后调用wx.addCard()方法,如下(openid没有返回去,因为本身openid就是前端给我的),

注意cardExt是经过JSON.stringify() 转成字符串的。还有加密时和这里的参数必须对应上,除了ticket以外,所有加密时的参数都必须在这里对应上,不能多也不能少,card_id单独拿出来。可以在https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=cardsign验证你的签名是不是算对了。这里很容易出现签名错误的问题。只要确保token和ticket都是用公众号的、加密没有错,参数对应得上、cardExt转json字符串,就不会签名出错。

var card = {

     cardId: res.data.data.card_id,

     cardExt: JSON.stringify({

       openid:globalData.openid,

       timestamp: res.data.data.timestamp,

       nonce_str: res.data.data.noncestr,

       signature: res.data.data.sign

     })

}

wx.addCard({

   cardList: [card],

   success: res => {

        console.log(res);

   }

})

7.在上面小程序代码中,我们只打印了领取结果res。如果领取成功了,我们需要前端把res.cardList[0].code发给服务器,当然最好带上其他参数,比如我还需要带上订单ID,否则都不知道这个code属于哪单。

后端拿到code之后用下面的方法进行解码

/**
 * 解码卡券领取后获得的code
 * @param code 前端传回来的code
 * @param token 公众号的token
 * @return
 */
public static String decode(String code,String token){
   String url = "https://api.weixin.qq.com/card/code/decrypt?access_token="+token;
   JSONObject object = new JSONObject();
   object.put("encrypt_code",code);
   String res = HttpRequestor.postJson(url,object.toJSONString());
   XcxJson json = JSON.parseObject(res,XcxJson.class);
   if(json.getErrcode()==0){
      return json.getCode();
   }else{
      return "error";
   }
}

8.解码之后保存好code,并把code绑定到相关的数据库表中,方便下次查。当用户要核销卡券的时候,

扫码枪扫出来的或者用户展示的code和我们存的code 是一直的,我们把相关的东西展示到前台,然后执行核销(最好让前台员工确认后)。

/**
 * 核销卡券
 * @param code 卡券的code
 * @param card_id 卡券的ID
 * @param token 公众号的token
 *
 * @return 0-失败,1-成功
 */
public static Integer cancel(String code,String card_id,String token){
   String url = "https://api.weixin.qq.com/card/code/consume?access_token="+token;
   JSONObject object = new JSONObject();
   object.put("code",code);
   object.put("card_id",card_id);
   String res = HttpRequestor.postJson(url,object.toJSONString());
   XcxJson json = JSON.parseObject(res,XcxJson.class);
   if(json.getErrcode()==0){
      return 1;
   }else{
      return 0;
   }
}

核销完之后卡包里面的卡券就会变成已使用。这样我们的整个逻辑链就完整了。

我这里主要讲的是在公众平台生成的卡券,如果需要自定义程度高的卡券,则需要调用创建卡券的接口。然后投放和核销会多一些参数。

相关文章:

  • 2022-12-23
  • 2021-07-25
  • 2021-07-07
  • 2021-12-20
  • 2021-12-13
  • 2021-12-13
  • 2021-12-30
  • 2021-06-29
猜你喜欢
  • 2021-12-23
  • 2021-12-13
  • 2021-12-13
  • 2021-12-13
  • 2021-12-10
  • 2022-12-23
  • 2021-12-13
相关资源
相似解决方案