参考代码 - Uniapp框架支付
基于 Uniapp Webview 实现,目前支持安卓端支付宝和微信支付,以及iOS端苹果应用内购(理论上支持所有 uni.requestPayment 支持的支付方式)。
示例代码:
<template>
<view class="page">
<web-view
v-if="webviewSrc"
:src="webviewSrc"
:update-title="false"
@message="handleMessage"
/>
</view>
</template>
<script setup>
import { createEzrevenueService } from './ezrevenue.js'
import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue'
const webviewSrc = ref(null)
onLoad(() => {
webviewSrc.value = '用户的付费界面paywallUrl'
})
const ezrevenueService = createEzrevenueService()
async function handleMessage(event) {
return await ezrevenueService.onMessage(event)
}
</script>
<style></style>
/**
Webview逻辑:
判断是Uniapp Webview + iOS 环境
请求APP requestProduct
根据Products请求结果,渲染商品列表,展示苹果的定价
请求APP restoreCompletedTransactions
根据需要请求APP finishTransaction
用户点击充值时,请求APP requestPayment
APP逻辑:
判断是Uniapp APP + iOS 环境
实现 IAP API,封装成 onMessage 方法
*/
// uniapp iap
// https://uniapp.dcloud.net.cn/api/plugins/payment.html#iap
// -------------------------------------------------------------------
class Iap {
_channel = null
_channelError = null
_productIds = []
_ready = false
constructor({ products }) {
this._productIds = products
}
init() {
return new Promise((resolve, reject) => {
this.getChannels(
(channel) => {
this._ready = true
resolve(channel)
},
(err) => {
reject(err)
}
)
})
}
getProduct(productIds) {
return new Promise((resolve, reject) => {
this._channel.requestProduct(
productIds || this._productIds,
(res) => {
resolve(res)
},
(err) => {
reject(err)
}
)
})
}
requestPayment(orderInfo) {
return new Promise((resolve, reject) => {
uni.requestPayment({
provider: 'appleiap',
orderInfo: orderInfo,
success: (res) => {
resolve(res)
},
fail: (err) => {
reject(err)
},
})
})
}
restoreCompletedTransactions(username) {
return new Promise((resolve, reject) => {
this._channel.restoreCompletedTransactions(
{
manualFinishTransaction: true,
username,
},
(res) => {
resolve(res)
},
(err) => {
reject(err)
}
)
})
}
finishTransaction(transaction) {
return new Promise((resolve, reject) => {
this._channel.finishTransaction(
transaction,
(res) => {
resolve(res)
},
(err) => {
reject(err)
}
)
})
}
getChannels(success, fail) {
if (this._channel !== null) {
success(this._channel)
return
}
if (this._channelError !== null) {
fail(this._channelError)
return
}
uni.getProvider({
service: 'payment',
success: (res) => {
this._channel = res.providers.find((channel) => {
return channel.id === 'appleiap'
})
if (this._channel) {
success(this._channel)
} else {
this._channelError = {
errMsg: 'paymentContext:fail iap service not found',
}
fail(this._channelError)
}
},
})
}
get channel() {
return this._channel
}
}
// END uniapp ------------------------------------------------------------
function isArray(value) {
return Object.prototype.toString.call(value) === '[object Array]'
}
const _EVENT_TYPE_LIST = [
'iapRequestProduct',
'iapRestoreCompletedTransactions',
'iapFinishTransaction',
'requestPayment',
]
function _extractEventData(event) {
let eventData = event.detail.data
if (!isArray(eventData)) {
eventData = [eventData]
}
for (let item of eventData) {
if (item?.type && _EVENT_TYPE_LIST.includes(item.type)) {
return item
}
}
return null
}
function _getWebviewNode() {
let pageList = getCurrentPages()
let page = pageList[pageList.length - 1]
let currentWebview = page.$getAppWebview()
let wvNode = currentWebview.children()[0]
return wvNode
}
function _invokeCallback(callback, data) {
if (!callback) {
return
}
let wvNode = _getWebviewNode()
let dataJson = JSON.stringify(data)
wvNode.evalJS(`window.${callback}(${dataJson})`)
}
export function createEzrevenueService() {
const _iap = new Iap({ products: [] })
const self = {
async onMessage(event) {
let eventData = _extractEventData(event)
if (!eventData) {
return
}
let eventType = eventData.type
const handlerFunc = self[eventType]
const { request, callback } = eventData
console.log(`[EZREVENUE] onMessage ${eventType}`, request)
let res = null
try {
res = await handlerFunc(request)
} catch (err) {
console.error(`[EZREVENUE] ${eventType} error`, err)
_invokeCallback(callback, { success: false, data: err })
return
}
console.log(`[EZREVENUE] ${eventType} success`, res)
_invokeCallback(callback, { success: true, data: res })
},
async _waitIapReady() {
if (!_iap._ready) {
await _iap.init()
}
},
async iapRequestProduct({ productIds } = {}) {
await self._waitIapReady()
return await _iap.getProduct(productIds)
},
async iapRestoreCompletedTransactions({ username } = {}) {
await self._waitIapReady()
return await _iap.restoreCompletedTransactions(username)
},
async iapFinishTransaction({ transaction }) {
await self._waitIapReady()
return await _iap.finishTransaction(transaction)
},
async requestPayment(request) {
return await new Promise((resolve, reject) => {
console.log(`[EZREVENUE] requestPayment`, request)
uni.requestPayment({
...request,
success: (res) => {
resolve(res)
},
fail: (err) => {
reject(err)
},
})
})
},
}
return self
}