微信小程序支付
微信小程序支付分为四个流程,获取用户 token ,创建用户订单,创建商品预订单,下单支付。
支付流程
获取用户信息
查看后台给出的接口文档,发现获取用户信息需要下面5个必传参数。其中四个参数可以通过小程序内置方法获取用户信息 uni.getUserProfile({}) 后得到,参数 code 可执行小程序登录 uni.login() 后获取到。
| 参数名 | 必选 | 类型 | 参数说明 |
|---|---|---|---|
| encryptedData | 是 | string | 执行小程序 获取用户信息后 得到 |
| rawData | 是 | string | 执行小程序 获取用户信息后 得到 |
| iv | 是 | string | 执行小程序 获取用户信息后 得到 |
| signature | 是 | string | 执行小程序 获取用户信息后 得到 |
| code | 是 | string | 执行小程序登录后获取 |
async getToken() {
// 获取用户信息
const [errPro,resPro] = await uni.getUserProfile({desc:'刀刀'})
console.log(resPro)
// 获取参数code
const [errLogin, resLogin] = await uni.login()
console.log(resLogin)
// 调用后台接口获取用户的token
const {message} = await this.$u.post('/users/wxlogin', {
encryptedData: resPro.encryptedData,
rawData: resPro.rawData,
iv: resPro.iv,
signature: resPro.signature,
code: resLogin.code
})
console.log(message.token);
return message.token
},
创建用户订单
创建用户订单所需参数如下所示。
请求头参数:
| 参数名 | 必选 | 类型 | 说明 |
|---|---|---|---|
| Authorization | 是 | string | 用户登录成功获取的token值 |
请求体参数:
| 参数名 | 必选 | 类型 | 说明 |
|---|---|---|---|
| order_price | 是 | string | 订单总价格 |
| consignee_addr | 是 | string | 收货地址 |
| goods | 是 | Array | 订单数组 |
参数订单总价与收货地址在前面已经通过计算属性获取,可以直接使用,订单数组需要的数据如下。
goods字段说明
| 参数名 | 必选 | 类型 | 说明 |
|---|---|---|---|
| goods_id | 是 | number | 商品id |
| goods_number | 是 | number | 购买的数量 |
| goods_price | 是 | number | 单价 |
查看接口文档,可以发现这些数据是每一项商品都有的数据,因此,可以通过遍历要购买的商品的数组数据获取。
拿到需要的数据后可以发送请求获取数据,封装请求头,参数为之前获取到的 token 。
async getOrderNumber(token) {
const order = {
order_price: this.cartPrice,
consignee_addr: `${this.address} ${this.username} ${this.phone}`,
goods: this.goodsChecked.map(item=>{
return {
goods_id: item.goods_id,
goods_number: item.number,
goods_price: item.goods_price
}
})
}
const {message} = await this.$u.post('/my/orders/create',order,{
Authorization: token
})
console.log(message.order_number);
return message.order_number
},
创建商品预订单
商品支付订单需要的参数如下所示,请求头(和创建订单一致,封装即可)、订单编号,已经通过创建订单的步骤获取到了,因此可直接发请求。
请求头参数:
| 参数名 | 必选 | 类型 | 说明 |
|---|---|---|---|
| Authorization | 是 | string | 用户登录成功获取的token值 |
请求体参数:
| 参数名 | 必选 | 类型 | 说明 |
|---|---|---|---|
| order_number | 是 | string | 订单编号 |
async getPay(order_number, token) {
const {message} = await this.$u.post('/my/orders/req_unifiedorder',{order_number},{
Authorization: token
})
console.log(message.pay);
return message.pay
},
下单支付
查看 uni-app 官方文档,获取到需要的数据,如下表所示。这些数据已经通过创建商品预付订单时返回接受了,因此可以直接使用。
| 参数名 | 类型 | 必填 | 说明 | 平台差异说明 |
|---|---|---|---|---|
| timeStamp | String | 微信小程序必填 | 时间戳从1970年1月1日至今的秒数,即当前的时间。 | 微信小程序 |
| nonceStr | String | 微信小程序必填 | 随机字符串,长度为32个字符以下。 | 微信小程序 |
| package | String | 微信小程序必填 | 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=xx。 | 微信小程序 |
| signType | String | 微信小程序必填 | 签名算法,应与后台下单时的值一致 | 微信小程序 |
| paySign | String | 微信小程序必填 | 签名,具体签名方案参见 微信小程序支付文档(opens new window) | 微信小程序 |
async toPay() {
const token = await this.getToken()
const order_number = await this.getOrderNumber()
const pay = await this.getPay(order_number)
const [err,res] = await uni.requestPayment(pay)
if (!err) {
this.cartbuy()
uni.switchTab({
url: '/pages/cart/cart'
})
}
},
封装登录请求
根据 vue 的代码风格,我们可以把登录获取用户 token 的函数代码封装到 vuex 内,把获取到的 token 保存到 vuex 中,方便数据的调用与使用。
-
创建
user.js的模块仓库,在index.js中导入const state = { } const mutations = { } const actions = { } export default { namespaced: true, state, mutations, actions }import user from "./modules/user"; export default new Vuex.Store({ modules: { user }, getters }) -
在
actions函数中复制获取token的函数const state = { token: uni.getStorageSync('usertoken') || '' } const mutations = { getToken(state, token) { state.token = token uni.setStorageSync('usertoken', token) } } const actions = { async getToken({commit}) { const [errPro,resPro] = await uni.getUserProfile({desc:'刀刀'}) console.log(resPro) const [errLogin, resLogin] = await uni.login() console.log(resLogin) const {message} = await this.$u.post('/users/wxlogin', { encryptedData: resPro.encryptedData, rawData: resPro.rawData, iv: resPro.iv, signature: resPro.signature, code: resLogin.code }) console.log(message.token); commit('getToken', message.token) } }在
pay.vue组件中用辅助函数获取到actions函数和保存的token值,直接调用token值即可。<script> import { mapGetters, mapMutations, mapActions, mapState } from "vuex"; export default { data () { return { bool: true, address: '', phone: '', username: '' } }, computed: { ...mapGetters(['goodsChecked', 'cartNumber', 'cartPrice']), ...mapState('user', ['token']) }, methods: { ...mapMutations('cart',['cartbuy']), ...mapActions('user', ['getToken']), async toPay() { if (!this.token) { await this.getToken() } const order_number = await this.getOrderNumber() const pay = await this.getPay(order_number) const [err,res] = await uni.requestPayment(pay) if (!err) { this.cartbuy() uni.switchTab({ url: '/pages/cart/cart' }) } }, async getPay(order_number) { const {message} = await this.$u.post('/my/orders/req_unifiedorder',{order_number},{ Authorization: this.token }) console.log(message.pay); return message.pay }, async getOrderNumber() { const order = { order_price: this.cartPrice, consignee_addr: `${this.address} ${this.username} ${this.phone}`, goods: this.goodsChecked.map(item=>{ return { goods_id: item.goods_id, goods_number: item.number, goods_price: item.goods_price } }) } const {message} = await this.$u.post('/my/orders/create',order,{ Authorization: this.token }) console.log(message.order_number); return message.order_number } } } </script> -
运行发现报错,错误信息为没有
post,因为我们把获取token的函数复制去vuex中,this指向不是uni,没有$u方法,因此把this改为uni即可。
封装请求拦截器
查看文档,封装一个请求拦截器。我们发现需要请求头的路由路径都是带 /my/ ,因此可以使用正则表达式来判断。
const install = (Vue, vm) => {
// 此为自定义配置参数,具体参数见上方说明
Vue.prototype.$u.http.setConfig({
baseUrl: 'https://api-hmugo-web.itheima.net/api/public/v1',
loadingText: '努力加载中~',
loadingTime: 800,
// 设置自定义头部content-type
// header: {
// 'content-type': 'xxx'
// }
// ......
});
// 请求拦截部分,如配置,每次请求前都会执行
Vue.prototype.$u.http.interceptor.request = (config) => {
// 引用token
// 方式二,如果没有使用uView封装的vuex方法,那么需要使用$store.state获取
// config.header.token = vm.$store.state.token;
if (/\/my\//.test(config.url)) {
config.header.Authorization = vm.$store.state.user.token;
}
// 最后需要将config进行return
return config;
// 如果return一个false值,则会取消本次请求
// if(config.url == '/user/rest') return false; // 取消某次请求
}
}