文章目錄
一、支付寶小程序源碼運行
1.開發者工具下載
2.運行小程序
npm run dev:my
其實mpvue兼容的所有平臺的項目的運行命令都在package.json的script字段中:
"scripts": {
"dev:wx": "node build/dev-server.js wx",
"start:wx": "npm run dev:wx",
"build:wx": "node build/build.js wx",
"dev:swan": "node build/dev-server.js swan",
"start:swan": "npm run dev:swan",
"build:swan": "node build/build.js swan",
"dev:tt": "node build/dev-server.js tt",
"start:tt": "npm run dev:tt",
"build:tt": "node build/build.js tt",
"dev:my": "node build/dev-server.js my",
"start:my": "npm run dev:my",
"build:my": "node build/build.js my",
"dev": "node build/dev-server.js wx",
"start": "npm run dev",
"build": "node build/build.js wx",
"lint": "eslint --ext .js,.vue src"
},
即默認微信小程序,可指定平臺:微信小程序(wx)、百度小程序(swan)、頭條小程序(tt)、支付寶小程序(my)
二、支付寶小程序用戶授權
打開src\pages\index\index.vue,小程序的加載是從mounted中調用init方法開始的,而在init方法中調用了getSetting方法來判斷是否已經具備獲取用戶信息權限:
...
init() {
showLoading({ title: '正在加載' })
this.getSetting() // 判斷是否已經具備獲取用戶信息權限
}
},
mounted() {
this.init()
}
}
getSetting方法中調用了getSetting接口函數(在src\api\wechat.js):
getSetting() {
this.prepare = false
this.loading = true
const vue = this
// 判斷當前小程序是否具備userInfo權限
getSetting(
'userInfo',
(res) => {
console.log('驗證成功...', res)
vue.authorized = true
vue.prepare = true
vue.getUserInfo()
},
(res) => {
console.log('驗證失敗...', res)
vue.authorized = false
vue.prepare = true
hideLoading()
}
)
},
爲了做兼容,getSetting接口函數修改爲如下:
export function getSetting(authorize, onSuccess, onFail) {
const wx = (res) => {
if (res.authSetting[`scope.${authorize}`]) {
onSuccess(res)
} else {
onFail(res)
}
}
const my = (res) => {
if (res.authSetting[authorize]) {
onSuccess(res)
} else {
onFail(res)
}
}
mpvue.getSetting({
success: (res) => {
adapter({ wx, my }, res)
},
fail: () => {
setError('獲取權限失敗')
}
})
}
adapter 方法源碼:
function adapter(fn, params) {
return fn[mpvuePlatform] && fn[mpvuePlatform](params)
}
主要是因爲微信和支付寶小程序接口的調用方式不同,比如有些地方的參數需要以對象的形式傳入
除了這個接口其他接口調用都需要做兼容處理,由於篇幅原因這裏就不再展示全部代碼了
支付寶目前無法拿到用戶授權的回調,所以在完成授權之後需要手動刷新頁面,否則授權登錄頁面不會消失
通過官方文檔:獲取會員基礎信息 可知:
- 開發者使用 button 組件 喚起授權框,請添加屬性 open-type=“getAuthorize” 用於支持用戶授權。這條和微信是類似的,因此做了如下兼容:
<button
class="auth-btn"
@getuserinfo="getUserInfo"
open-type="getUserInfo"
plain="true"
v-if="platform === 'wx'"
>
授權登錄
</button>
<button
class="auth-btn"
plain="true"
open-type="getAuthorize"
scope='userInfo'
v-else
>
授權登錄
</button>
- 開發者調用 my.getAuthCode 和 alipay.system.oauth.token 接口獲取支付寶會員標識(user_id)——能力詳情
- 與微信不同的一點是,支付寶允許一旦判斷用戶授權不存在直接彈出彈窗,因此getSetting方法改造爲如下(用戶未授權時:微信設置授權狀態爲false,而支付寶直接拉起授權面板,而不經過按鈕點擊的一步):
getSetting() {
this.prepare = false
this.loading = true
const vue = this
// 判斷當前小程序是否具備userInfo權限
getSetting(
'userInfo',
(res) => {
console.log('驗證成功...', res)
vue.authorized = true
vue.prepare = true
vue.getUserInfo()
},
(res) => {
console.log('驗證失敗...', res)
if (mpvuePlatform === 'wx'){
vue.authorized = false
}
vue.prepare = true
vue.getUserInfo()
hideLoading()
}
)
},
src\api\wechat.js的getUserInfo如下:
export function getUserInfo(onSuccess, onFail) {
const wx = () => {
mpvue.getUserInfo({
success(res) {
const { userInfo } = res
console.log('getUserInfo', userInfo)
onSuccess(userInfo)
},
fail() {
onFail ? onFail() : setError('獲取用戶信息失敗')
}
})
}
const my = () => {
mpvue.getAuthUserInfo({
success(res) {
console.log(res)
res.avatarUrl = res.avatar || res.avatarUrl
delete res.avatar
console.log('getOpenUserInfo', res)
onSuccess(res)
},
fail() {
onFail ? onFail() : setError('獲取用戶信息失敗')
}
})
}
adapter({ wx, my })
}
- 獲取用戶授權信息需要到支付寶小程序的後臺開發管理中手動添加權限:
三、支付寶小程序獲取OpenId
1.源碼改造
src\pages\index\index.vue的getUserInfo方法調用src\api\wechat.js的getUserInfo方法,並將回調函數傳入,成功回調函數中調用了src\api\wechat.js的getUserOpenId方法(這裏在import時做了一下轉換:getUserOpenId as getOpenId)
getUserInfo() {
const vue = this
const onOpenIdComplete = (vue, openId, userInfo) => {
vue.openId = openId
// 獲取首頁數據
vue.getHomeData(openId, hideLoading)
// 上報用戶信息,註冊賬號
register(openId, userInfo)
// 判斷用戶今天是否簽到過
vue.getSignState(openId)
}
console.log('getUserInfo...')
getUserInfo(
(userInfo) => {
vue.userInfo = userInfo
setStorageSync('userInfo', userInfo)
const openId = getStorageSync('openId')
console.log('openId', openId)
if (!openId || openId.length === 0) {
getOpenId((openId) => {
onOpenIdComplete(vue, openId, userInfo)
})
} else {
onOpenIdComplete(vue, openId, userInfo)
}
},
(err) => {
console.log('getUserInfo failed', err)
}
)
},
src\api\wechat.js調用wx.getUserInfo或my.getAuthUserInfo
export function getUserInfo(onSuccess, onFail) {
const wx = () => {
mpvue.getUserInfo({
success(res) {
const { userInfo } = res
console.log('getUserInfo', userInfo)
onSuccess(userInfo)
},
fail() {
onFail ? onFail() : setError('獲取用戶信息失敗')
}
})
}
const my = () => {
mpvue.getAuthUserInfo({
success(res) {
console.log(res)
res.avatarUrl = res.avatar || res.avatarUrl
delete res.avatar
console.log('getOpenUserInfo', res)
onSuccess(res)
},
fail() {
onFail ? onFail() : setError('獲取用戶信息失敗')
}
})
}
adapter({ wx, my })
}
- 這裏支付寶的接口名稱和微信不同,是
getAuthUserInfo
而不是getUserInfo
在src\api\wechat.js的getUserOpenId方法的支付寶處理模塊中調用了src\api\index.js的接口請求方法getAlipayOpenId
export function getUserOpenId(cb) {
const wx = () => {
mpvue.login({
success: function(res) {
console.log(res)
if (res.code) {
const appid = 'wx0fad7b50f723dc46'
getOpenId(appid, res.code).then(response => {
if (handleError(response)) {
const openId = response.data.data.openid
const sessionKey = response.data.data.session_key
setStorageSync('openId', openId)
setStorageSync('session_key', sessionKey)
cb && cb(openId)
}
})
} else {
console.log('獲取用戶登錄態失敗!' + res.errMsg)
setError('獲取用戶登錄態失敗!')
}
},
fail() {
setError('獲取openId失敗!')
}
})
}
const my = () => {
mpvue.getAuthCode({
scopes: 'auth_user', // 主動授權(彈框):auth_user,靜默授權(不彈框):auth_base
success: async (res) => {
console.log('getOpenId', res)
if (res.authCode) {
const code = res.authCode
const appId = '2019060665444521'
const response = await getAlipayOpenId(appId, code)
if (handleError(response)) {
const openId = response.data.data.openid
const sessionKey = response.data.data.session_key
setStorageSync('openId', openId)
setStorageSync('session_key', sessionKey)
cb && cb(openId)
}
} else {
setError('獲取openId失敗!')
}
},
fail: () => {
setError('獲取openId失敗!')
}
})
}
adapter({ wx, my })
}
src\api\index.js的接口請求方法getAlipayOpenId,這裏的接口是自己服務器上的而不是官方服務器上的,接下來來看服務器端是如何處理的
export function getAlipayOpenId(appId, code) {
return get(`${API_PREFIX}/openId/get/alipay`, { appId, code })
}
2.服務器接口
下載好服務器端的源碼後需要準備:
- 支付寶密鑰
- 服務器端的數據庫配置
- https證書
準備完成之後將https證書引入到app.js中,並在app.js中將“獲取支付寶openId”的一步中拿私鑰的地方替換爲自己的,然後通過node app.js
運行服務器端項目
(1)生成公鑰和私鑰
開發 openId 獲取接口需要生成公鑰和私鑰,查看:技術文檔
(2)填入公鑰信息
進入小程序開發者後臺,在設置->開發設置->開發信息下填入公鑰
(3)服務端 SDK
支付寶小程序獲取 openId 需要通過服務端 SDK 開發進行支持,查看:開發文檔
(4)Node.js 版本開發
首先需要安裝依賴:
npm install alipay-sdk
下面提供核心代碼:
const AlipaySdk = require('alipay-sdk').default
app.get('/openId/get/alipay', (req, res) => {
const appId = req.query.appId
const code = req.query.code
if (!appId || !code) {
onFail(res, '獲取openId失敗')
} else {
const alipaySdk = new AlipaySdk({
appId,
privateKey: fs.readFileSync(appIdMap[appId], 'ascii')
})
alipaySdk.exec('alipay.system.oauth.token', {
grantType: 'authorization_code',
code,
refreshToken: 'token'
}).then(result => {
console.log('alipay', result)
if (result) {
const { alipayUserId, userId, accessToken } = result
onSuccess(res, '獲取openId成功', {
openid: `${userId}|${alipayUserId}`,
session_key: accessToken
})
} else {
onFail(res, '獲取openId失敗')
}
}).catch(err => {
onFail(res, '獲取openId失敗', err)
})
}
})
這裏有兩點需要注意:
- appIdMap[appId] 爲私鑰地址
- 將 userId|alipayUserId 定義爲 openId,因爲支付寶中沒有 openId 的概念
四、支付寶小程序組件定製開發
1.ImageView改造
<template>
<div class="image-view" @click="onClick">
<img
:class="round ? ' round image' : 'image'"
:style="{ height }"
:src="src"
:mode="mode"
:lazy-load="lazyLoad"
@error="onError"
@load="onLoad"
v-show="!isLoading"
>
<img
:class="round ? ' round image' : 'image'"
:style="{ height }"
src="/static/images/loading.jpeg"
:mode="mode"
@error="onPreloadError"
@load="onPreload"
v-show="isLoading || error"
v-if="mpvuePlatform === 'wx'"
>
</div>
</template>
2.Button改造
<template>
<van-button
:size="size"
:type="type"
:round="round"
:custom-class="customClass"
@click="onClick"
v-if="platform === 'wx'"
>
{{text}}
</van-button>
<button
:size="size"
:type="type"
:class="customClass"
:style="{borderRadius: round ? '50px' : '0'}"
@click="onClick"
v-else-if="platform === 'my'"
>
{{text}}
</button>
</template>
<script>
export default {
props: {
customClass: String,
text: String,
color: String,
size: String,
round: {
type: Boolean,
default: false
}
},
data() {
return {
platform: mpvuePlatform
}
},
methods: {
onClick() {
this.$emit('click')
}
}
}
</script>
<style lang="scss" scoped>
</style>
3.Icon改造
<template>
<van-icon
:class="customClass"
:style="customStyle"
:name="name"
:color="color"
:size="size"
v-if="platform === 'wx'"
></van-icon>
<icon
:class="customClass"
:style="customStyle"
:type="name"
:color="color"
:size="size"
v-else-if="platform === 'my'"
/>
</template>
<script>
export default {
props: {
customStyle: Object,
customClass: String,
name: String,
color: String,
size: String
},
data() {
return {
platform: mpvuePlatform
}
}
}
</script>
<style lang="scss" scoped>
</style>
需要改造的一大部分原因是支付寶小程序無法使用UI組件庫:vant-weapp,建議使用支付寶自帶組件庫:基礎組件、擴展組件。