跳到主要内容

参考代码 - 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
}