更新记录
1.0.0(2026-08-17)
- 基于 iOS StoreKit 2 实现,购买成功直接返回 JWS 签名交易凭证(signedTransaction),服务端可走 App Store Server API 验签,安全可靠
- 支持商品查询、发起购买(appAccountToken 关联服务端订单,防串单)、恢复购买、未完成交易拉取与关单
- 交易实时监听(Transaction.updates):家长审批通过、跨设备购买、退款/撤销即时回调
- 孤儿交易自动处理:finish 前 JWS 本地留档可补报服务端核对,避免已扣款无法补发
- 所有回调统一切回主线程触发;Android 端桩实现保证双端编译
平台兼容性
uni-app(3.8.2)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| - | √ | × | × | √ | - | × | √ | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | × | × | × |
yk-iap 苹果内购插件(StoreKit 2)
基于 iOS StoreKit 2 的 uni-app UTS 插件,提供商品查询、发起购买、恢复购买、未完成交易管理能力。购买成功直接返回 JWS 签名凭证(signedTransaction),可交由服务端调用 App Store Server API 验签,安全可靠。
Android 端提供同名桩实现(返回
仅支持 iOS),保证双端工程都能编译打包。
功能特性
- 商品查询(价格、名称、描述、类型)
- 发起购买(支持传入
appAccountToken关联服务端订单) - 恢复购买(
AppStore.sync) - 拉取未确认交易(
Transaction.unfinished,冷启动恢复用) - 关闭指定未完成交易(发货完成后再
finish) - 交易实时监听(
Transaction.updates):家长审批通过、跨设备购买、退款撤销等都会实时回调 - 自动处理同商品「历史孤儿交易」死锁:finish 前将 JWS 存入本地留档队列,可经
getOrphanTransactions取走上报服务端核对,避免「已扣款但凭证丢失」 - 所有回调统一切回主线程触发,JS 侧可直接操作 UI
- 返回 StoreKit 2 JWS 凭证,服务端验签(推荐走 App Store Server API / Apple 官方验证)
安装与配置
- 将
uni_modules/yk-iap目录复制到你的工程uni_modules/下(HBuilderX 导入插件市场版本会自动放置)。 manifest.json→App原生插件配置中添加:{ "yk-iap": {} }- iOS 工程需配置 In-App Purchase 能力(Xcode 工程
Signing & Capabilities或云打包勾选内购模块),并设置最低版本 iOS 15.0。 - 在 App Store Connect 中创建好商品(Product ID 需与调用时一致)。
- 重新制作自定义调试基座 / 云打包,卸载旧 App 后安装新基座。
注意:UTS 插件必须通过静态 import 引用,编译器才会把原生代码编进基座/包。仅用
uni.requireUTSPlugin运行时加载时包内往往没有该插件。参见下方调用示例中的import ... from '@/uni_modules/yk-iap'。
API 说明
所有方法均为回调风格,回调参数为 UTSJSONObject,统一字段:
| 字段 | 说明 |
|---|---|
success |
是否成功(boolean) |
error |
失败原因(string,可选) |
code |
错误码(number,可选):-1 系统错误 / -2 JWS验签失败 / -3 用户取消 / -4 待处理 / -5 未知结果 |
cancelled |
是否用户取消(boolean,可选) |
pending |
是否等待处理(家长审批等,boolean,可选) |
getProducts(productIds, callback)
查询商品列表。
| 参数 | 类型 | 说明 |
|---|---|---|
| productIds | Array<string> |
商品 Product ID 列表 |
| callback | (res: UTSJSONObject) => void |
回调 |
成功时 res.products 为数组,每项:id / displayName / description / displayPrice / price(number) / type(consumable / nonConsumable / autoRenewable / nonRenewable)。
purchase(productId, appAccountToken, callback)
发起购买。
| 参数 | 类型 | 说明 |
|---|---|---|
| productId | string |
商品 Product ID |
| appAccountToken | string |
服务端订单关联标识(必须是合法 UUID 字符串,建议每次购买新生成) |
| callback | (res: UTSJSONObject) => void |
回调 |
成功时返回:
| 字段 | 说明 |
|---|---|
signedTransaction |
JWS 签名交易凭证(eyJ 开头),用于服务端验签 |
transactionIdentifier |
交易 ID |
originalTransactionIdentifier |
原始交易 ID(订阅/恢复购买时有用) |
productId |
商品 ID |
appAccountToken |
本次关联的订单 token |
replayUnfinished |
是否回放的同商品未完成旧交易(boolean,可选) |
restore(callback)
恢复购买(AppStore.sync())。成功后应在服务端校验所有已购凭证并发放权益。
getPendingTransactions(callback)
拉取所有未确认交易(只读 Transaction.unfinished,不会弹 Apple ID 认证框)。适合 App 冷启动时恢复未发货订单。
成功时 res.transactions 为数组,每项含 transactionIdentifier / originalTransactionIdentifier / productId / jwsRepresentation / appAccountToken。
finishTransaction(transactionIdentifier, callback)
关闭指定未完成交易(服务端验签、发货成功后再调用,避免重复发货)。
getOrphanTransactions(callback)
读取本地留档的孤儿交易(购买时自动 finish 的「同商品但 token 不匹配」旧交易,finish 前已保存 JWS)。成功时 res.transactions 为数组,每项含 signedTransaction / transactionIdentifier / originalTransactionIdentifier / productId / appAccountToken / capturedAt。
建议冷启动时调用一次,逐条上报服务端核对(服务端按 transactionIdentifier 幂等),确认无需补发后调用 clearOrphanTransactions 清空队列。
clearOrphanTransactions(callback)
清空孤儿交易留档队列(服务端核对完成后再调用)。
startTransactionListener(callback)
启动交易变更监听(建议 App 启动时调用一次,iOS 15+)。之后每来一笔交易变更回调一次,回调 res 字段:
| 字段 | 说明 |
|---|---|
event |
固定为 transactionUpdate(启动失败时为 listenerError) |
jwsRepresentation |
JWS 签名交易凭证 |
transactionIdentifier / originalTransactionIdentifier |
交易 ID / 原始交易 ID |
productId |
商品 ID |
appAccountToken |
关联订单 token |
expirationDate |
订阅到期时间(ISO8601,非订阅为空) |
revocationDate |
撤销时间(非空表示退款/撤销,需收回权益) |
revocationReason |
撤销原因码(无撤销为 -1) |
isUpgraded |
是否已被更高档订阅升级取代 |
注意:
- 监听启动瞬间会把当前未完成交易回放一遍,与服务端核对时请按
transactionIdentifier幂等去重; - 本插件自己
finish的交易不会通知(已过滤); - 监听任务常驻 App 生命周期,
stopTransactionListener仅解绑回调,任务不退出。
stopTransactionListener()
解绑交易变更回调(无参数、无回调)。
调用示例
1. 查询商品
import { getProducts } from '@/uni_modules/yk-iap'
getProducts(['com.your.app.product.vip_monthly', 'com.your.app.product.vip_yearly'], (res) => {
if (res.success) {
const products = res['products'] as Array<UTSJSONObject>
products.forEach((p: UTSJSONObject) => {
console.log('商品ID:', p['id'])
console.log('名称:', p['displayName'])
console.log('价格:', p['displayPrice'])
console.log('类型:', p['type'])
})
} else {
console.error('查询失败:', res['error'])
}
})
2. 发起购买(推荐完整流程)
import { getProducts, purchase, finishTransaction } from '@/uni_modules/yk-iap'
// 每次购买生成新 UUID 关联服务端订单,防止回调串单
function generateOrderToken(): string {
// 建议调用后端下单接口返回的订单号 UUID
return '9B2F6A1E-3D5C-4F8A-9B0C-1E2F3A4B5C6D'
}
export function buyVip(productId: string) {
getProducts([productId], (queryRes) => {
if (!queryRes.success) {
uni.showToast({ title: '商品查询失败', icon: 'none' })
return
}
const orderToken = generateOrderToken()
purchase(productId, orderToken, (res) => {
if (res.success) {
// 1. 把 signedTransaction 发给服务端验签
// 2. 服务端验签通过后:调用 finishTransaction 关单 + 发放权益
uni.request({
url: 'https://your-server.com/api/iap/verify',
method: 'POST',
data: {
productId: productId,
orderToken: orderToken,
signedTransaction: res['signedTransaction'],
transactionIdentifier: res['transactionIdentifier']
},
success: (verifyRes) => {
if (verifyRes.data.success) {
// 发货成功 → 关单,防止下次重复发货
finishTransaction(res['transactionIdentifier'] as string, () => {})
uni.showToast({ title: '购买成功', icon: 'success' })
}
}
})
} else if (res.cancelled) {
uni.showToast({ title: '已取消支付', icon: 'none' })
} else if (res.pending) {
uni.showToast({ title: '支付待处理(家长审批中)', icon: 'none' })
} else {
uni.showToast({ title: res['error'] as string || '购买失败', icon: 'none' })
}
})
})
}
3. 恢复购买
import { restore } from '@/uni_modules/yk-iap'
restore((res) => {
if (res.success) {
uni.showToast({ title: '恢复完成,正在同步权益…', icon: 'none' })
// 调用 getPendingTransactions 逐个校验并发货
syncPendingOrders()
} else {
uni.showToast({ title: res['error'] as string || '恢复失败', icon: 'none' })
}
})
4. 冷启动补发(未发货订单)
import { getPendingTransactions, finishTransaction } from '@/uni_modules/yk-iap'
export function syncPendingOrders() {
getPendingTransactions((res) => {
if (!res.success) return
const txs = res['transactions'] as Array<UTSJSONObject>
txs.forEach((tx: UTSJSONObject) => {
const txId = tx['transactionIdentifier'] as string
const jws = tx['jwsRepresentation'] as string
// 服务端校验该 JWS 是否已发货,未发货则发货并关单
uni.request({
url: 'https://your-server.com/api/iap/verify',
method: 'POST',
data: { signedTransaction: jws },
success: (verifyRes) => {
if (verifyRes.data.success && verifyRes.data.notDelivered) {
// 发货完成后关闭交易
finishTransaction(txId, () => {})
}
}
})
})
})
}
5. 启动交易监听(退款/家长审批实时处理)
import { startTransactionListener, getOrphanTransactions, clearOrphanTransactions, finishTransaction } from '@/uni_modules/yk-iap'
export function initIap() {
// App 启动时调用一次
startTransactionListener((res) => {
if (res['event'] != 'transactionUpdate') return
// 退款/撤销:收回权益
const revokedAt = res['revocationDate'] as string
if (revokedAt != null && revokedAt.length > 0) {
revokeEntitlement(res['transactionIdentifier'] as string)
return
}
// 新交易(家长审批通过、跨设备购买等):验签发货
uni.request({
url: 'https://your-server.com/api/iap/verify',
method: 'POST',
data: { signedTransaction: res['jwsRepresentation'] },
success: (verifyRes) => {
if (verifyRes.data.success && verifyRes.data.notDelivered) {
finishTransaction(res['transactionIdentifier'] as string, () => {})
}
}
})
})
// 补报孤儿交易留档(购买时自动 finish 的旧交易 JWS 保存在本地)
getOrphanTransactions((res) => {
if (!res.success) return
const orphans = res['transactions'] as Array<UTSJSONObject>
if (orphans.length == 0) return
uni.request({
url: 'https://your-server.com/api/iap/orphans',
method: 'POST',
data: { transactions: orphans },
complete: () => {
// 服务端核对完成后清空留档
clearOrphanTransactions(() => {})
}
})
})
}
6. 服务端验签(Node.js 参考)
服务端收到 signedTransaction(JWS)后,调用 App Store Server API 的 /inApps/v2/transactions/{transactionId} 或解码 JWS 并校验签名(Apple 公钥),确认 transactionId、productId、appAccountToken 与本地订单一致,且未重复使用后发货。不要信任客户端回传的本地收据。
常见问题
Q: 运行时提示插件不可用 / unavailable?
A: 说明当前自定义基座或打包产物里没有编入 yk-iap 原生代码。确认:① 页面/模块中静态 import { ... } from '@/uni_modules/yk-iap';② manifest.json 声明了 "yk-iap": {};③ 插件 package.json 的 dcloudext.type 为 "uts";④ 重新制作自定义调试基座 / 云打包,并卸载手机旧 App 后重新安装。
Q: appAccountToken 传什么?
A: 必须是合法 UUID 字符串。建议每次购买前由服务端下单接口生成订单号并返回 UUID,客户端用它关联这笔交易,服务端验签时核对,防止回调串单。
Q: 为什么购买返回的是 signedTransaction 而不是收据(receipt)?
A: StoreKit 2 推荐使用 JWS 签名交易凭证(signedTransaction)做服务端验证,比 StoreKit 1 的整包收据更精确、可定位到单笔交易。它同样可通过 App Store Server API 验签,请不要降级使用旧版收据。
Q: 只支持 iOS 吗?
A: 是的。Android 端仅提供桩实现,调用返回 仅支持 iOS,保证双端编译通过。
Q: 用户退款了怎么知道?
A: App 启动后调用一次 startTransactionListener,退款/撤销会实时回调,revocationDate 非空即表示被撤销,此时应收回权益。注意服务端还需依赖 App Store Server Notifications 兜底(App 未打开时收不到客户端回调)。
Q: 孤儿交易(token 对不上的旧交易)被自动 finish 了,凭证还能找回吗?
A: 能。finish 前插件已把该交易的 JWS 存入本地留档队列,调用 getOrphanTransactions 即可取走上报服务端核对补发,核对完成后调 clearOrphanTransactions 清空。服务端请按 transactionIdentifier 幂等去重。
兼容性
- uni-app(Vue3 或 Vue2 + UTS 支持)
- iOS 15.0+
- 仅 App 端(App-iOS)
隐私与合规
本插件不采集任何用户隐私数据,不涉及广告标识符(IDFA),无第三方 SDK。支付由 App Store 处理,凭证仅用于服务端验证订单。

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 0
赞赏 0
下载 12512156
赞赏 1943
赞赏
京公网安备:11010802035340号