最近开发一个功能,用户使用支付宝扫app商家的二维码跳指定的付款页面,然后进行付款。
整体流程是,首先设置生成二维码,使用UQRCode生成二维码,传入线上的链接,页面路径及页面上所需要展示的参数,封装二维码组件可参考另一篇文章 封装二维码组件,
第一步:获取支付宝code
为了让用户无感知进行授权,我们需要使用支付宝的静默授权,使用url拼接,请求获取到code,url的的拼接参数
参数说明:
参数名 | 是否必须 | 长度 | 描述 |
---|---|---|---|
app_id | 是 | 16 | 开发者应用的app_id; 相同支付宝账号下,不同的app_id获取的token切忌混用。 |
scope | 是 | 不定,取决于请求授权时scope个数 | 接口权限值,目前只支持auth_user(获取用户信息、网站支付宝登录)、auth_base(用户信息授权)、auth_ecard(商户会员卡)、auth_invoice_info(支付宝闪电开票)、auth_puc_charge(生活缴费)五个值;多个scope时用","分隔,如scope为"auth_user,auth_ecard"时,此时获取到的access_token,既可以用来获取用户信息,又可以给用户发送会员卡。 |
redirect_uri | 是 | 100 | 授权回调地址,是经过URLENCODE转义 的url链接(url必须以http或者https开头); 在请求之前,开发者需要先到开发者中心对应应用内,配置授权回调地址。 redirect_uri与应用配置的授权回调地址域名部分必须一致。 |
state | 否 | 100 | 商户自定义参数,用户授权后,重定向到redirect_uri时会原样回传给商户。 为防止CSRF攻击,建议开发者请求授权时传入state参数,该参数要做到既不可预测,又可以证明客户端和当前第三方网站的登录认证状态存在关联。 |
示例代码
js
// 授权成功的回调地址
let hdurl = encodeURIComponent('...域名.../#/page_wallet/payuser/paymoney');
let appId = '2021004122656497';
window.location.href =`https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?app_id=${appId}&scope=auth_user&redirect_uri=${hdurl}`;
第二步:获取code 授权成功后,会返回到我们填写的回调地址的页面,code是以参数的形式返回的,我们可以直接在url取到code
第三步:请求接口,将所需的参数传给后端,拿到我们需要的数据,再监听事件,调起支付
接下来附上完整的代码: 个人的代码可能与大家的实际场景需求不一样,请根据需求修改。
我这段代码,页面就是一个付款页面,扫码后直接进入这个页面,回调里面填的也是这个页面 用到了一个名为 lzc-keyboard 的控件
之所以用了v-show,是因为授权的过程中会有一个跳转的过程,使用初始进入页面的时候,页面是空白的,当授权成功拿到code后,再进行展示
js
<template>
<view v-show="show">
<view class="code-box">
<view>
<view style="font-weight: bold">{{ user ? user.name : '' }}</view>
<view>付款给{{ user ? user.type : '' }}</view>
</view>
<view>
<!-- <image :src="user ? user.avatar : ''"></image> -->
</view>
</view>
<view class="je" :style="{ height: 'calc(100vh - ' + allHeight + 'px)' }">
<view>金额</view>
<view class="zhi">
<view class="bs">¥</view>
<view class="jes">
{{ form.price }}
<view class="blink">|</view>
</view>
</view>
</view>
<lzc-keyboard
ref="lzckeyboard"
:defaultValue="form.price"
confirmText="付款"
:confirmStyle="confirmStyle"
@change="change"
@confirm="confirm"
></lzc-keyboard>
</view>
</template>
<script>
import lzcKeyboard from '@/components/lzc-keyboard/lzc-keyboard.vue';
import { payment_code } from '@/api/wallet';
export default {
components: {
lzcKeyboard
},
data() {
return {
show: true, // 控制是否显示内容
allHeight: 0, // 高度
user: {}, // 页面展示的信息
// 请求的参数
form: { price: '', type: 0, code: null, receiver_id: null },
// 样式
confirmStyle: {
backgroundColor: '#FF1B36'
},
// 掉起支付所需要的数据,后端返回的
paydata: {}
};
},
// 设置下高度 不重要
onReady() {
let q = uni.createSelectorQuery();
let headerHeight = 0;
headerHeight = 200;
let statusBarHeight = 0;
uni.getSystemInfo({
success: (e) => {
statusBarHeight = e.windowBottom;
}
});
setTimeout(() => {
this.allHeight = statusBarHeight + headerHeight + 45;
}, 100);
},
// 收起
onShow() {
uni.hideKeyboard();
},
onLoad(e) {
// 首次扫码进入携带的参数是有id的,需要将这些存本地一下(目的是第二次进入的授权成功进入该页面时,这些信息会消失的)
if(e.id){
this.user = {
name: e.name,
id: e.id,
type: e.tp
};
this.form.receiver_id = e.id;
uni.setStorageSync('userpay', this.user);
}else{
// 第二次进入,从本地取出信息
let data = uni.getStorageSync('userpay');
this.user = {
name: data.name,
id: data.id,
type: data.type
};
this.form.price = data.price;
this.form.receiver_id = data.id;
// 重点,第二次进入的时候,路径上已经携带了授权成功的code了
this.form.code = this.getUrlParam('auth_code');
this.$forceUpdate();
}
this.judgefacility();
},
methods:{
// 授权方法
handlAccredit(){
let ua = window.navigator.userAgent.toLowerCase();
// //判断是不是支付宝
if (ua.match(/AlipayClient/i) == 'alipayclient') {
// 如果当前code没有值的话,就去跳转去授权
if (this.form.code == null || this.form.code === '') {
let hdurl = encodeURIComponent(
'http://zhyweb.first.xinzhidi.cn/#/page_wallet/payuser/paymoney'
);
let appId = '2021004122656497';
window.location.href = `https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?app_id=${appId}&scope=auth_user&redirect_uri=${hdurl}`;
} else {
// 如果当前code有值了,那就将页面信息显示出来
this.show = true;
}
// 这里是后端要求的支付宝传2
this.form.type = 2;
}
},
// 价格改变
change(e) {
if (e < 999999999) {
this.$set(this.form,'price',e)
}
console.log('数字改变', e);
},
async confirm(e) {
uni.showLoading({});
// 请求接口
uni.$u.http.post('/api...接口地址',this.form).then(res=>{
uni.hideLoading();
this.paydata = res.data.data;
let that = this;
// 如果是支付宝支付的话
if (this.form.type == 2) {
if (window.AlipayJSBridge) {
that.alPay();
} else {
// 如果没有注入则监听注入的事件
document.addEventListener('AlipayJSBridgeReady', that.alPay, false);
}
}
}).catch((err) => {
// 如果接口调用出错的话,将值重新写入,重新授权一次,因为可能存在code已经被使用的情况
this.user.price = this.form.price;
uni.setStorageSync('userpay', this.user);
this.form.code = null;
this.handlAccredit();
});
},
alPay() {
let that = this;
// 调起支付,传入后端返回的订单号即可
AlipayJSBridge.call(
'tradePay',
{
tradeNO: that.paydata.tradeNO
},
function (result) {
// 关闭当前h5页面
window.AlipayJSBridge.call('closeWebview');
}
);
},
}
}
</script>
注意事项:我这个申请的是生活号,还有需要将回调的域名再控制台进行填写,不然跳转的时候可能会报找不到页面
代码因为是项目需求不同,所以可能和各位的对不上,可供参考部分逻辑,也可留言一起交流,谢谢!