微信跳转小程序实现免输卡号绑定银行卡
微信免输绑卡在 项目启动前,银行会与微信官方对接,会有一个技术支持的群,正在做免输绑卡功能或者已经做过免输绑卡功能的朋友,肯定知道群里技术助手回答问题的效率有多高,一般一天1~2句话,很影响进度;而且技术助手大多数会让你参照文档,对于已经做过的,可能感觉不难,对于正在做的,难在不懂整个流程;我这里简单总结下,也希望对后面做免输绑卡功能的朋友有帮助。
需要实现的效果图:
首先,我先说下我在做免输绑卡时遇到的比较迷茫的几个问题?
1、从微信到小程序,微信和小程序如何做交互,小程序如何获取微信参数?
微信打开小程序前所有功能,都是微信自带的,不需要开发;微信跳转小程序后,可以在小程序onload中接收参数,拿到参数(银行三要素)调后端接口进行解密,获取银行卡号。
2、小程序免输绑卡插件,使用时有几种场景,银行卡列表使用插件和银行卡列表不使用插件,是自己画页面,还是使用插件带的样式?
银行官方人员会建议你全使用插件的页面,这样容易审核通过,所以作为前端人员,一个页面不用画,我是是画了一大堆页面,结果后面全部改成了使用插件带的页面。
3、验短信页面是不是免输绑卡插件带的,短信接口怎么处理?
验短信页面是插件带的,只需要拉起验短api,短信页面自带的有,接口前端也不用处理。
免输卡号产品简介、接入流程、开发指引:
https://pay.weixin.qq.com/wiki/doc/apiv3/Offline/open/chapter1_1_1.shtml
免输卡号绑卡小程序插件简介:
https://mp.weixin.qq.com/wxopen/plugindevdoc?appid=wx1bf7bb903143fa2c&token=1244483084〈=zh_CN
流程
1、在onload中接收微信传来的参数,调接口获取openid,校验银行侧的openid和微信传来的openid是否一致;
2、拿到微信传来的参数,引入sha1加密方式,按照免输绑卡文档的拼接方式进行加密,再将加密后的结果与微信传来的paysign做对比,如果一致,则通过;
3、使用微信侧传来的三要素,调后端接口查询银行卡信息,使用插件,展示查询的银行卡信息;
4、在插件的用户点击同意的方法事件中,先调后端的绑卡的接口;绑卡接口成功不代表绑卡成功,需要在绑卡接口成功的回调中再拉起验短api,按照验短api需要送的字段(验短api需要送字段拼接的加密paysign;验短api传的时间戳随机数,只能是9位,要截取)
注:在使用插件前,需要在pages.json页面中配置使用插件,可以在小程序公众平台查看插件的appid
后面附带了我的代码,希望对后面的前端朋友有帮助
代码:
<template>
</template>
<script>
import {
accountNoFormat
} from '@/utils/util.js';
import {
showModal
} from '@/core/app.js';
// 引入sha1算法
import sha1 from 'sha1';
export default {
data() {
return {
param: {}, //接收微信传来的参数
noncestr: '', //生成随机不到32位字符串
paysign: '', //生成签名
openid: '', //用于银行校验
sessionKey: '',
timestamp: '', // 随机数
data: {
pluginData: { // 银行向插件传的数据
appId: '', // 银行小程序的appid
bankCardLists: []
}
},
// 绑卡接口需要送的字段
phone: '', // 手机号
sgntrPrsnIdentTp: '', // 证件类型
sgntrAcctNm: '', // 签约人账户名称
sgntrPrsnIdentNo: '', // 证件号
}
},
onLoad(option) {
// 调用一个loading
wx.showLoading({
title: '加载中'
})
// 生成随机32位字符
this.randomStr();
if (option != undefined && option != null && option != '') {
this.param = option
this.data.pluginData.appId = option.appid
// wx签名校验
this.compareWXPaysign();
};
},
methods: {
// 获取Code
getCode() {
var that = this
uni.login({
// timeout: 10000,
provider: 'weixin',
success: function(data) {
//获取临时登录凭证code
that.getUserEnter(data.code)
},
fail: function(err) {
console.log("loginerr", err);
}
})
},
// 获取用户openid
getUserEnter(jsCode) {
this.$api.getUserOpenid(jsCode).then(res => {
if (res.code == 'MOP000000') {
this.openid = res.data.data.openid
this.sessionKey = res.data.data.session_key
this.compareOpenid(this.openid)
}
})
.catch(error => {});
},
// 校验微信传来的openid
compareOpenid(id) {
const openid = this.param.openid
// 校验不通过,进入无卡绑定页面
if (openid == id) {
this.getCardInfo()
} else {
if(this.checkingVersion()) {
this.goPlugin();
}
}
},
// 查询银行卡信息
getCardInfo() {
const encbankelem = this.param.enc_bankelem;
let data = {
threeElements: encbankelem,
};
this.$api.queryCard(data).then(res => {
if(res.code == 'MOP000000'){
this.phone = res.data.phone; // 手机号
this.sgntrPrsnIdentTp = res.data.cre_type; //证件类型
this.sgntrAcctNm = res.data.true_name; //签约人账户名称
this.sgntrPrsnIdentNo = res.data.cre_id; // 证件号
// 给储蓄卡数组赋值
let debitCardList = [];
debitCardList = res.data.AcctInfoList;
if (debitCardList.length != '0') {
debitCardList.forEach((item) => {
item.cardNumber = accountNoFormat(item.acctNo);
item.accTCardType = 'XX银行 储蓄卡';
item.sgntrPrsnAcctTp = '00';
item.cardType = 'DEBIT';
})
}
// 给信用卡数组赋值
let creditCardList = [];
creditCardList = res.data.AccountInfoList;
if (creditCardList.length != '0') {
creditCardList.forEach((item) => {
item.cardNumber = accountNoFormat(item.cardNbr);
item.acctNo = item.cardNbr;
item.accTCardType = 'XX银行 信用卡';
item.sgntrPrsnAcctTp = '01';
item.cardType = 'CREDIT';
})
}
this.data.pluginData.bankCardLists = [
...debitCardList,
...creditCardList
]
if(this.checkingVersion()) {
this.goPlugin();
wx.hideLoading();
}
} else{
if(this.checkingVersion()) {
this.goPlugin();
wx.hideLoading();
}
}
})
},
// 进入页面微信侧数据生成签名并做校验
compareWXPaysign() {
const appid = this.param.appid
const appkey = 银行小程序申请的appkey
const bind_tail = this.param.bind_tail
const enc_bankelem = this.param.enc_bankelem
const noncestr = this.param.noncestr
const openid = this.param.openid
const sessionid = this.param.sessionid
const timestamp = this.param.timestamp
const paysign =
`appid=${appid}&appkey=${appkey}&bind_tail=${bind_tail}&enc_bankelem=${enc_bankelem}&noncestr=${noncestr}&openid=${openid}&sessionid=${sessionid}×tamp=${timestamp}`
const paysign1 = this.param.paysign
// 将拼接的字符进行sha1加密生成签名
// 校验生成的签名与微信传来的签名是否一致
if (sha1(paysign) == paysign1) {
this.getCode()
} else {
if(this.checkingVersion()) {
this.goPlugin();
wx.hideLoading();
}
}
},
// 生成随机字符串
randomStr() {
/*默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1*/
var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
var maxPo = $chars.length;
var randomNum = '';
for (var i = 0; i < 32; i++) {
randomNum += $chars.charAt(Math.floor(Math.random() * maxPo));
}
this.noncestr = randomNum;
},
// 调用蒙层插件
goPlugin() {
const that = this
wx.navigateTo({
url: 'plugin://myPlugin/plugin-index',
events: {
// 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据
agreeBtnClick: function(data) { // 用户点击了协议蒙层的“同意”按钮
const appid = that.param.appid
const appkey = 银行小程序申请的appkey
const noncestr = that.noncestr
const package1 = `bank_type=NCBCB_${data.selectedCard.item.cardType}`
const sessionid = that.param.sessionid
const timestamp = Date.now().toString().substring(0, 10)
const paysign =
`appid=${appid}&appkey=${appkey}&noncestr=${noncestr}&package=${package1}&sessionid=${sessionid}×tamp=${timestamp}`
// sha1加密,并存入paysign
const paysignSuccess = sha1(paysign)
var encodsndBackURL = encodeURIComponent(`bind_scene=NO_CARD_BIND&sessionid=${that.param.sessionid}`)
// 调用一个loading
wx.showLoading({
title: '加载中'
})
const param = {
sndBackURL: encodsndBackURL, // 回跳地址
tranTpCd: "0207", // 交易类型码 固定填写“0207”(协议签约受理)
acptInstNo: "1", // 受理机构号 写死
acptInstIndID: "Z2004944000010", // 受理机构标识 写死
rsrvMblNo: that.phone, //手机号
sgntrPrsnIdentNo: that.sgntrPrsnIdentNo, // 证件号
sgntrPrsnIdentTp: that.sgntrPrsnIdentTp, // 证件类型
sgntrAcctNm: that.sgntrAcctNm, // 签约人账户名称
sgntrBnkAcctNo: data.selectedCard.item.acctNo, // 卡号
sgntrPrsnAcctTp: data.selectedCard.item.sgntrPrsnAcctTp, // 动态判断
acctInstIndNo: "C1050331000044", // 账户机构标识号
pymtCnlTp: "EPCC" //
}
that.$api.oneclkBindcardToacct(param).then(res => {
if(res.code == 'MOP000000'){
// 调起微信验短页面的数据
wx.phoneBindCardVerifySms({
timestamp: timestamp,
noncestr: that.noncestr,
package: package1,
paysign: paysignSuccess,
sessionid: that.param.sessionid,
signtype: 'sha1',
appid: that.param.appid,
success(res) {
wx.hideLoading();
},
fail(res) {
wx.hideLoading();
}
})
}
})
},
disagreeBtnClick: function() { // 用户点击了协议蒙层的“不同意”按钮
console.log('用户点击了协议蒙层的“不同意”按钮');
},
openUserProtocol: function(data) {
console.log('用户点击了某个协议,协议信息为:', data.protocolInfo);
},
noCardsStatusBtnClick: function() { // 当前没有任何银行卡,展示“无卡,空列表”页面,用户点击“知道了”按钮,退出小程序,回到微信支付选择银行的“银行列表页”
console.log('没有银行卡场景,用户点击了知道了按钮');
},
},
success: (res) => {
// 通过eventChannel向被打开页面传送数据
res.eventChannel.emit('bankListDataSend', {
bankData: that.data.pluginData
});
},
fail: (res) => {
console.log('跳转插件失败', res);
}
})
},
checkingVersion() {
if (this.getOpenerEventChannel) {
// 当前版本支持
return true;
}
wx.showModal({
title: '更新提示',
content: '当前版本过低,请更新微信到最新版本。',
showCancel: false,
confirmText: '知道了'
});
return false;
},
}
}
</script>
<style>
</style>