评论

微信商家券H5开发全过程总结

微信商家券创建->发券->领取->核销闭环

最近遇到一个需要在第三方页面领取微信商家券的需求,其中涉及的创建->发券->领取->核销的整个闭环,简单记录一下这个过程。

1.创建商家券

参考网页:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_1.shtml

  • 创建商家券只要传递相应的参数即可,其中一个重要的参数stock_id,是发券批次的唯一标识,在后续过程中也会用到。
  • 请求的参数中涉及数量的参数可能回存在依赖关系,比如max_coupons,因此,创建的时候需要满足特定的条件才能创建成功。
  • 需要注意“券code模式”,因为需求背景是在第三方页面中调取接口,每张券的code是由第三方系统自行生成的卡号code进行管理的,所以这里选择了MERCHANT_API。第一个选项WECHATPAY_MODE是无法自主指定、而是由微信卡券系统自动随机分配code码的;第三个选项MERCHANT_UPLOAD是需要创建者预先上传code,然后系统再在这些code里面随机分配的,因此,根据需求选择了MERCHANT_API,在后续的发券addCard的时传递对应的code字段即可。

2.发券

参考网页:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_4_1.shtml

3.领取

参考网页:https://docs.qq.com/doc/DVlRacmRIWWhBUFVE?_t=1611714639674

  • 这一步的关键在于addCard要传的参数:sign,这个参数是对除了sign以外其他几个参数进行签名之后得到的结果。参考网页中可以看到,加入签名的部分参数是有下标的,例如out_request_no这个字段,在加入签名的时候是以out_request_no0、out_request_no1、out_request_no2的形式加入到签名sign当中的,但是在传入addCard的cardExt中时,仍然是out_request_no字段。如下:
// 1.单张卡券
// 参与签名的参数
const params = {
  stock_id0:'';
  out_request_no0:'';
  coupon_code0:'';
  customize_send_time0:'';
  send_coupon_merchant:'';
  openid:'';
};
// 生成签名,需要使用HMAC-SHA256算法
sign = generateSign(params);
// 构造cardExt参数
const addCardParams = [{
  cardId: stock_id, // 批次号
  cardExt = JSON.stringfy({ // JSON字符串
    stock_id:'';
    out_request_no:'';
    coupon_code:'';
    customize_send_time:'';
    send_coupon_merchant:'';
    openid:'';
    sign
  })
}]

wx.addCard({
  cardList: addCardParams, // 需要添加的商家券列表
  success: function (res) {
    var cardList = res.cardList; // 添加的商家券列表信息
  }
});


// 2.多张卡券(以两张为例)
// 参与签名的参数
const params = {
  // 第一张券的(0)
  stock_id0:'';
  out_request_no0:'';
  coupon_code0:'';
  customize_send_time0:'';
  // 第二张券的(1)
  stock_id1:'';
  out_request_no1:'';
  coupon_code1:'';
  customize_send_time1:'';
  // 这两个参数则不需要下标0、1、2
  send_coupon_merchant:'';
  openid:'';
};
// 生成签名,需要使用HMAC-SHA256算法
sign = generateSign(params);

// 构造addCard参数
const addCardParams = [{ // 第一张券
  cardId: stock_id, // 第一张券批次号
  cardExt = JSON.stringfy({ // 第一张JSON字符串
    stock_id:'';
    out_request_no:'';
    coupon_code:'';
    customize_send_time:'';
    send_coupon_merchant:'';
    openid:'';
    sign
  })
}, { // 第二张券
  cardId: stock_id, // 第二张券批次号
  cardExt = JSON.stringfy({ // 第二张券JSON字符串(不需要再传openid和sign)
    stock_id:'';
    out_request_no:'';
    coupon_code:'';
    customize_send_time:'';
  })
}
]

wx.addCard({
  cardList: addCardParams, // 需要添加的商家券列表
  success: function (res) {
    var cardList = res.cardList; // 添加的商家券列表信息
  }
});

4.核销

参考网页:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_3.shtml

  • 用户领取完以后核销,传递相应的参数即可。

5.补充

  • 开发过程中经常会出现addCard签名错误的问题,可能的原因是:1.算法出错(要用HMAC-SHA256),2.加入签名的参数字段和参数值的问题,排查错误方法:https://mp.weixin.qq.com/s/WhYpWmfuhUBw2wseTXdt2A
  • 这里卡券的核销方式(coupon_use_rule.use_method)是线下核销OFF_LINE,如果需要使用MINI_PROGRAMS的核销方式,则还需要对小程序跳转的参数使用AEAD_AES_256_GCM算法进行解密,解密的demo在这:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_2.shtml。附上一份node.js的示例供参考:
const crypto = require('crypto');
// 1、用商户平台上设置的APIv3密钥【微信商户平台—>账户设置—>API安全—>设置APIv3密钥】,记为key。
const key = '*******'; 

/**
 * 使用指定的 API V3 密钥对给定的ciphertext进行解密,返回解密后的明文数据coupon_code
 *
 * @param {Object} params - 解密参数
 * @param {string} params.associate - 加密参数 - 类型
 * @param {Buffer} params.nonce - 加密参数 - 随机数
 * @param {string} params.ciphertext - 加密密文
 * @returns {string} 明文数据
 */
const decrypt = ({ associate, nonce, ciphertext }) => {
  // 2、从跳转路径中取得参数nonce、associate和密文ciphertext;
  // 3、使用urldecode对ciphertext进行解码,得到strUrlDecodeText
  const strUrlDecodeText = decodeURIComponent(ciphertext);
 // 4、使用base64对strUrlDecodeText进行解码,得到strBase64DecodeText;
  const strBase64DecodeText = Buffer.from(strUrlDecodeText, 'base64');

  // 提取authTag和数据
  const authTag = strBase64DecodeText.slice(strBase64DecodeText.length - 16);
  const data = strBase64DecodeText.slice(0, strBase64DecodeText.length - 16);

  // 5、使用key、nonce和associate,对数据密文strBase64DecodeText进行解密,得到的字符串即为coupon_code。
  const decipher = crypto.createDecipheriv('aes-256-gcm', key, nonce);
  decipher.setAuthTag(authTag);
  decipher.setAAD(Buffer.from(associate));

  let coupon_code = decipher.update(data, null, 'utf8');
  coupon_code += decipher.final();
  
  return coupon_code;
};



文章有错误的地方还望指出修正,感谢!


最后一次编辑于  2023-05-24  
点赞 4
收藏
评论

2 个评论

登录 后发表内容