更新记录

1.0.0(2026-10-30)

  • 新增 iOS 15+ StoreKit 2 商品查询与消耗型、非消耗型、非自动续期订阅、自动续期订阅购买能力。
  • 新增已验证交易 JWS、业务账户 UUID 关联、当前权益、最近交易、未完成交易查询和更新监听,供业务核验与补单。
  • 新增交付声明校验与手动完成交易、用户主动恢复购买、订阅与续期信息验证及首购优惠资格查询。
  • 新增系统订阅管理、退款申请与 iOS 16+ 优惠码兑换入口,并明确系统页回执和业务结果的区别。
  • 新增原生交互互斥、请求期限、单次结果与完成回调、有界批量部分结果、迟到交易处理及监听释放能力。
  • 新增 Native/UTS 构建身份核对、本地有界脱敏诊断、完整报告、固定快照分段复制及复制失败手动回退。
  • 新增 uni-app(Vue2 / Vue3)与 uni-app x 的商品、交易、订阅、配置四类 Tabs 示例,覆盖全部 19 个公开 API。
  • 新增 Apple 官方服务端库验签参考、证书根指纹核对与应用、商品、账户、环境策略校验,不代替业务幂等交付或实时权益核对。

平台兼容性

uni-app(5.33)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
√ √ × × - × × - ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × × × ×

uni-app x(5.33)

Chrome Safari Android iOS 鸿蒙 微信小程序
× × × - × ×

lizhao-apple-iap 苹果内购 苹果应用内购买与交易管理 UTS插件

介绍

lizhao-apple-iap 为 uni-app 与 uni-app x 提供 iOS StoreKit 2 应用内购买、交易恢复和订阅管理源码,适合应用内数字内容与权益接入。本次整理首次发布配置;已有本地合同、宿主资源与生成物验证,匹配基座、Apple 原生编译和沙箱业务验收仍是独立门槛。

iOS 15+ 为目标运行范围,优惠码系统页需 iOS 16+。插件保留两个宿主的同一套 API;其他平台提供能力说明及明确拒绝,不能在 Android、Harmony、Web 或小程序完成 Apple 支付。

功能特色

功能 解决的问题 对应 API / 配置
能力与初始化 识别真实平台、支付权限、基座适配及初始化状态 getIapCapabilitiesSync、initializeIap
商品与四类购买 取得真实商品/价格,处理消耗型、永久权益及两类订阅 getIapProducts、purchaseIapProduct;productId、quantity、appAccountToken
补单与交付确认 查询未完成交易,业务持久化交付后再完成,避免漏单 getIapUnfinishedTransactions、finishIapTransaction;deliveryConfirmed
权益与恢复 查看当前权益、最近交易,响应用户主动恢复 getIapCurrentEntitlements、getIapLatestTransaction、restoreIapPurchases
订阅与首购优惠 分开呈现订阅状态、续期验证与首购优惠资格 getIapSubscriptionStatus、isIapIntroOfferEligible
Apple 系统页面 进入管理订阅、退款申请与兑换码页面 showIapManageSubscriptions、requestIapRefund、presentIapOfferCodeSheet
更新监听与释放 接收迟到交易、续订和撤销,按真实句柄解除监听 onIapTransaction、offIapTransaction、disposeIap
完整诊断 保留失败上下文、版本来源及环境,支持完整/分段/手动复制 getIapDiagnosticsSync、setIapDiagnosticsEnabled;示例底部公共日志
服务端验签参考 使用 Apple 官方库验证签名和账户/商品策略 独立 uni_modules/lizhao-apple-iap/server-example/ 源码;不自动发货、不替代实时状态核对

14 个异步入口返回字符串请求编号,按 success 或 fail → complete 派发一次终态。金额和交易 ID 保持字符串;原生验证通过仍需业务服务端验签、核对归属并持久化交付。

适合哪些场景

场景 推荐能力 使用边界
游戏道具、次数等消耗品 商品查询、购买、未完成交易补单 不通过当前权益查询恢复已消耗商品;需服务端账本
永久解锁、专业版 购买、当前权益、主动恢复 按应用账户与 Apple 交易归属核验,不直接认领他人订单
自动续期会员 订阅状态、续期签名、更新监听与订阅管理 willAutoRenew 不等于当前已付费;退款/宽限状态需服务器持续维护
非自动续期服务 购买、最近交易及服务端记录 到期时间由业务计算,不能仅凭历史购买判断有效
异常补单与售后 未完成交易、更新监听、退款申请 超时不能证明未扣款;退款申请回执不等于退款到账
联调和问题定位 分类示例、能力检测、完整报告 非 iOS 只验证拒绝合同;源码/资源编译不等于实际支付通过

下载与导入

宿主 示例代码位置
uni-app(Vue2 / Vue3) uni_modules/lizhao-apple-iap/example/uniapp/index.vue
uni-app x uni_modules/lizhao-apple-iap/example/uniappx/index.uvue

从基础到进阶:按业务模块使用

基础功能配置示例

准备应用与商品

步骤 操作 应得到的结果
1. 导入插件 将完整 uni_modules/lizhao-apple-iap/ 放入自己的工程,业务只从插件根导入 保留平台实现、接口、拦截器与报告资源
2. 配置 Apple 应用 在 App Store Connect 选择与安装包 Bundle ID 一致的应用,确认内购能力及付费协议状态 签名身份、应用与商品归属于同一开发者账户
3. 创建商品 按业务选择内购或订阅,填写名称、Product ID、本地化和价格;订阅还需配置组与周期 得到真实 Product ID;不是商品的数字 Apple ID
4. 准备测试 创建沙箱测试账户,在开发签名包或 TestFlight 中测试 账户/密码只在 Apple 系统中输入,不填入插件或日志
5. 准备基座 首次导入需联编或生成包含本插件的 iOS 自定义基座,目标 iOS 15+ getIapCapabilitiesSync 返回真实能力与不可用原因
6. 进入示例 注册下方对应页面;先初始化,再填真实商品 ID 查询 显示商品数、价格或真实失败;不自动购买

Apple 的商品配置和沙箱流程见 配置内购 与 沙箱测试。商品配置传播并非即时;缺失时先核对 Bundle ID、Product ID、环境与协议,不凭空生成商品 ID。Product ID 应符合 Apple 商品信息规则(最长 100 个字符);插件本地通用校验上限为 255,并不扩大 Apple 后台限制。

将对应项合并到自己工程的 pages.json 的 pages 数组,保留原有页面。下面分别为两个宿主的真实路径,不需要同时注册:

{
  "path": "uni_modules/lizhao-apple-iap/example/uniappx/index",
  "style": { "navigationBarTitleText": "苹果内购" }
}
{
  "path": "uni_modules/lizhao-apple-iap/example/uniapp/index",
  "style": { "navigationBarTitleText": "苹果内购" }
}

以下为 uni-app x 的 UTS 函数,传入自己创建的真实商品 ID 后调用 loadIapProduct。先检测能力,初始化成功后再查询;空商品不会被替换为演示商品。

import { getIapCapabilitiesSync, initializeIap, getIapProducts } from '@/uni_modules/lizhao-apple-iap'

function loadIapProduct(productId: string): void {
  const capability = getIapCapabilitiesSync()
  if (!capability.available) {
    console.log('当前不可用:' + capability.reason)
    return
  }
  initializeIap({
    timeoutMs: 30000,
    success: () => {
      getIapProducts({
        productIds: [productId],
        timeoutMs: 30000,
        success: (result) => { console.log('商品=' + result.products.length + ';缺失=' + result.missingProductIds.length) },
        fail: (error) => { console.log(error.errSubject + ' ' + error.errCode + ' ' + error.errMsg) },
        complete: (result) => { console.log('查询结束:' + result.outcome) }
      })
    },
    fail: (error) => { console.log('初始化失败:' + error.errCode + ' ' + error.errMsg) },
    complete: (result) => { console.log('初始化调用结束:' + result.outcome) }
  })
}

没有真实商品时,可以检测能力、检查参数错误及查看报告;不能据此验收购买。上例只输出摘要,不打印交易、JWS 或账户 UUID。uni-app 的可直接使用 JavaScript 流程见下方“完整示例”。

高级示例

下列 UTS 函数在初始化成功后由相应业务操作调用。购买、恢复、退款、管理和兑换必须来自用户明确操作;所有商品/交易/账户参数使用自己的真实值。

商品查询与购买选项

查询时传入实际在 App Store Connect 配置的商品 ID。查询通过 StoreKit 获取真实商品;缺失ID列在 missingProductIds 中,网络或环境异常进入 fail。购买入口不能因为网络超时自动重复调用。

import { getIapProducts, purchaseIapProduct } from '@/uni_modules/lizhao-apple-iap'

function queryProducts(productId: string): void {
  getIapProducts({
    productIds: [productId],
    timeoutMs: null, // null 使用查询默认值 30000 毫秒
    success: (result) => { console.log('找到商品:' + result.products.length) },
    fail: (error) => { console.log('查询失败:' + error.errCode) },
    complete: (result) => { console.log('查询完成:' + result.outcome) }
  })
}

function requestPurchase(productId: string, accountToken: string | null): void {
  purchaseIapProduct({
    productId: productId,
    appAccountToken: accountToken,
    quantity: 1,
    timeoutMs: 120000, // 购买默认值;范围 1000–600000 毫秒
    success: (result) => {
      // 本地verified后仍需将JWS交给自己的服务端核对、验签和发货;不要打印。
      console.log('已收到已验证交易,仍需完成业务交付')
    },
    fail: (error) => {
      if (error.outcome == 'pending') console.log('待审批,等待后续交易更新')
      else if (error.outcome == 'unknown') console.log('结果未知,先核对交易,禁止自动重购')
      else if (error.outcome == 'cancelled') console.log('用户取消')
      else console.log('购买失败:' + error.errCode)
    },
    complete: (result) => { console.log('本次调用结束:' + result.outcome) }
  })
}

交付确认、恢复与监听生命周期

业务服务器验签并持久化发货后才允许确认交易;deliveryConfirmed 是调用者的明确声明,不能替代服务端证明。以下函数应只由业务确认流程调用,不能放在购买成功回调里无条件执行。

import { initializeIap, finishIapTransaction, restoreIapPurchases, onIapTransaction, offIapTransaction } from '@/uni_modules/lizhao-apple-iap'

let listenerId: string | null = null

function confirmDeliveredTransaction(transactionId: string, deliveryConfirmed: boolean): void {
  finishIapTransaction({
    transactionId: transactionId,
    deliveryConfirmed: deliveryConfirmed,
    timeoutMs: 30000,
    success: (result) => { console.log('交易确认完成') },
    fail: (error) => { console.log('确认失败:' + error.errCode) },
    complete: (result) => { console.log('确认结束:' + result.outcome) }
  })
}

function restoreFromUserAction(): void {
  restoreIapPurchases({
    success: (result) => { console.log('恢复查询完成,部分结果不完整:' + result.partial) },
    fail: (error) => { console.log('恢复失败:' + error.errCode) }
  })
}

initializeIap({
  success: () => {
    const listener = onIapTransaction((event) => { console.log('交易事件类型:' + event.kind) })
    if (listener.registered) listenerId = listener.subscriptionId
    if (listener.error != null) console.log('监听未注册:' + listener.error.errCode)
  },
  fail: (error) => { console.log('初始化失败:' + error.errCode) }
})
function removePageListener(): void {
  const id = listenerId
  if (id != null) {
    const removed = offIapTransaction(id)
    listenerId = null
    if (!removed.removed) console.log('未移除监听')
  }
}

恢复应来自用户主动操作。交易更新可能涉及撤销或续订,不能仅按交易 ID 忽略后续事件。disposeIap 释放观察与回调,不撤销Apple交易;真实请求仍未返回时返回9362019。完整页面的卸载处理与报告入口可分别在上述两种宿主示例中查看。

权益、订阅和系统页

初始化成功后查询权益;消耗型补单使用未完成交易查询。订阅状态中的 transaction 和 renewalInfo 都经过验证,无法验证的条目放入 issues;partial=true 时不能将其当成全部权益。当前权益不替代服务端账本,也不能用 willAutoRenew 单独判断当前已付费。

import { getIapCurrentEntitlements, getIapLatestTransaction, getIapSubscriptionStatus, isIapIntroOfferEligible, showIapManageSubscriptions, requestIapRefund, presentIapOfferCodeSheet, IapFail } from '@/uni_modules/lizhao-apple-iap'

function logFailure(error: IapFail): void { console.log(error.errSubject + ' ' + error.errCode + ' ' + error.errMsg) }
function inspectSubscription(productId: string): void {
  getIapCurrentEntitlements({
    success: (r) => { console.log('权益=' + r.transactions.length + ',问题=' + r.issues.length + ',partial=' + r.partial) }, fail: logFailure
  })
  getIapLatestTransaction({productId: productId, success: (r) => { console.log('有最近交易:' + r.found) }, fail: logFailure})
  getIapSubscriptionStatus({productId: productId, success: (r) => { console.log('订阅状态条数:' + r.statuses.length + ',partial=' + r.partial) }, fail: logFailure})
  isIapIntroOfferEligible({productId: productId, success: (r) => { console.log('已配置优惠:' + r.offerConfigured + ',资格:' + r.eligible) }, fail: logFailure})
}
function manageFromUserAction(): void {
  showIapManageSubscriptions({success: (r) => { console.log('系统管理页返回:' + r.state) }, fail: logFailure})
}
function refundFromUserAction(transactionId: string): void {
  requestIapRefund({transactionId: transactionId, success: (r) => { console.log('退款申请状态:' + r.state) }, fail: logFailure})
}
function redeemFromUserAction(): void {
  presentIapOfferCodeSheet({success: (r) => { console.log('兑换页返回:' + r.state) }, fail: logFailure})
}

退款返回 submitted 仅为申请受理,实际退款需核对交易撤销和服务器通知。订阅管理、兑换页返回 dismissed 不代表已经退订或兑换;真实交易由 updates/unfinished 交付。系统交互串行执行,超时后仍等待系统真正返回再解锁,不能因收到超时立即重购。

独立服务器验签参考

随插件交付 uni_modules/lizhao-apple-iap/server-example/package.json、uni_modules/lizhao-apple-iap/server-example/package-lock.json、uni_modules/lizhao-apple-iap/server-example/verifier.mjs、uni_modules/lizhao-apple-iap/server-example/verify-cli.mjs、uni_modules/lizhao-apple-iap/server-example/prepare-roots.mjs 和 uni_modules/lizhao-apple-iap/server-example/verifier.test.mjs。将整个服务器目录复制到独立服务器位置,再执行依赖安装,避免将 node_modules 或服务器配置打进客户端。

下面是 PowerShell 本地接入路径。C:/iap-server、C:/iap-server-private 及票据文件均由接入者自行准备;示例应用/商品/UUID须改成服务器真实登记和认证会话绑定值。JWS 经标准输入传入,不放命令行参数、日志或仓库。

Copy-Item -LiteralPath 'uni_modules/lizhao-apple-iap/server-example' -Destination 'C:/iap-server' -Recurse
Set-Location 'C:/iap-server'
npm ci --ignore-scripts
node prepare-roots.mjs --out 'C:/iap-server-private/apple-roots'
$env:APPLE_BUNDLE_ID = 'com.example.app'
$env:APPLE_ENVIRONMENT = 'Sandbox'
$env:APPLE_ROOT_CERT_PATHS = '["C:/iap-server-private/apple-roots/AppleRootCA-G3.cer","C:/iap-server-private/apple-roots/AppleRootCA-G2.cer"]'
$env:APPLE_PRODUCT_IDS = '["com.example.month"]'
$env:APPLE_PRODUCT_ID = 'com.example.month'
$env:APPLE_ACCOUNT_TOKEN = 'a7ca128f-6758-4b13-8ef1-05a27871675e'
# Production 使用服务器实际 App Store 数字编号;下面仅演示配置位置。
# $env:APPLE_APP_ID = '1234567890'
$env:APPLE_ENABLE_ONLINE_CHECKS = 'true'
Get-Content -LiteralPath 'C:/iap-server-private/transaction.jws' -Raw | node verify-cli.mjs

要求 Node.js 20+;固定依赖 Apple 官方 App Store Server Library。根证书仅从 Apple PKI 下载并核对固定指纹,已有不同目标不覆盖。缺应用、商品目录、环境、根或账户上下文均拒绝;Production 还必须设置实际 App Store 应用数字编号 APPLE_APP_ID。默认开启在线证书状态检查,离线开关只影响证书检查方式,不查询实时退款状态。服务器验签不需要把 .p8 私钥放到客户端。

业务服务端可从实际文件 uni_modules/lizhao-apple-iap/server-example/verifier.mjs 导入 readVerifierConfig 和 createTransactionVerifier。verifyForAccount 的第二参数必须来自服务器认证会话,不能直接相信客户端声称的账户 UUID。返回 verified 只表示签名和原文策略通过,currentStatusChecked=false、deliveryPerformed=false:还须通过 App Store Server API 和 Notifications V2 核对当前退款/权益,持久化幂等发货后才允许客户端 finish。历史有效JWS不能证明后来没有退款。

非续期订阅返回 requiresBusinessExpiry=true,必须由业务计算有效期;自动续期已过期按保守规则拒绝,宽限/账单重试需要另外验证当前续期状态。旧账户、家庭共享、缺 token 交易不会自动认领给当前账户。Xcode本地StoreKit Test票据不是Apple服务器证书链,服务器示例故意不信任;真实服务器集成使用Sandbox/TestFlight或Production签名交易。示例不提供公网接口、订单库或虚构发货成功。

批量、数量、超时与诊断配置

配置 示例取值与用途 边界
productIds 同时传入两个真实商品 ID 1–100 个不同 ID,不自动修剪/去重
appAccountToken 账户 UUID 或 null UUID 来自业务账户绑定,不是密码,也不是订单幂等键
quantity 消耗品示例为 2;其他类型为 1 1–10 的整数;真实商品类型由原生再次校验
timeoutMs 查询 30000、购买 120000 毫秒,或 null 使用默认 1000–600000 毫秒;超时不等于取消 Apple 交易
deliveryConfirmed 服务端持久化交付后才传 true 不可在购买 success 内无条件完成交易
enabled false 关闭详细 info,true 恢复 不清除已有日志,不触发上传

以下 UTS 代码演示可选参数的实际传入方式。仅定义函数,不在加载示例时发起购买;buyTwoConsumables 只适用于已查询确认的消耗型商品。

import { getIapProducts, purchaseIapProduct, getIapDiagnosticsSync, setIapDiagnosticsEnabled } from '@/uni_modules/lizhao-apple-iap'

function queryTwoProducts(firstId: string, secondId: string): void {
  getIapProducts({
    productIds: [firstId, secondId], timeoutMs: 30000,
    success: (result) => { console.log('商品数:' + result.products.length) },
    fail: (error) => { console.log('查询失败:' + error.errCode) },
    complete: (result) => { console.log('查询结束:' + result.outcome) }
  })
}
function buyTwoConsumables(productId: string): void {
  purchaseIapProduct({
    productId: productId, appAccountToken: null, quantity: 2, timeoutMs: 120000,
    success: () => { console.log('收到已验证交易,等待业务服务端核对与交付') },
    fail: (error) => { console.log('购买状态:' + error.outcome + ';错误=' + error.errCode) },
    complete: (result) => { console.log('请求结束:' + result.outcome) }
  })
}
function inspectIapDiagnostics(): void {
  setIapDiagnosticsEnabled(false)
  const snapshot = getIapDiagnosticsSync()
  console.log('版本来源=package生成字段;条数=' + snapshot.entries.length + ';丢弃=' + snapshot.droppedEntries)
  setIapDiagnosticsEnabled(true)
}

完整示例

Tabs 功能导航与测试方法

两种宿主都使用同一功能分组。默认显示“商品”,切换 Tabs 只改变展示,不清除商品/交易参数、不重新调用 API、不取消原生操作,也不重新生成日志快照。忙碌、最近结果和底部诊断跨分类可见。

分类 / 公共区域 能力 / API 测试方法
商品 getIapProducts、purchaseIapProduct 填真实商品 ID,查询价格后显式购买;观察成功、取消、待审批或未知状态
交易 getIapUnfinishedTransactions、getIapCurrentEntitlements、getIapLatestTransaction、restoreIapPurchases 初始化后分别查询;最近交易使用共享商品 ID;恢复由用户点击
交易 finishIapTransaction、requestIapRefund 输入真实交易 ID;完成前确认已经验签并持久化交付;退款只验申请回执
订阅 getIapSubscriptionStatus、isIapIntroOfferEligible 填自动续期订阅商品 ID,核对 statuses、issues、partial 与优惠资格
订阅 showIapManageSubscriptions、presentIapOfferCodeSheet 显式进入系统页面;关闭页面不当成购买/退订/兑换成功
配置 / 公共初始化入口 initializeIap、disposeIap、onIapTransaction、offIapTransaction 初始化后注册/移除监听;无在途请求时释放;不以切换 Tabs 触发生命周期
公共状态 / 诊断 getIapCapabilitiesSync、getIapDiagnosticsSync、setIapDiagnosticsEnabled 检测实际平台,生成报告读取快照,切换详细日志开关
底部报告 完整复制、同一快照分段、手动复制回退 核对报告编号、条数、长度与首尾标记;复制失败不推进分段

uni-app x:完整初始化、查询、监听和释放流程

下面代码可放入 UTS 页面脚本。点击页面按钮调用 startIapExample,传入真实 Product ID;查询结束后解除忙碌。示例用 event.kind 展示事件摘要,业务处理交易时不得打印签名。页面拥有独立监听,卸载只移除自己的句柄;只有应用确实不再使用内购且无在途请求时才显式调用 releaseIapService。

import { onUnload } from '@dcloudio/uni-app'
import { getIapCapabilitiesSync, initializeIap, getIapProducts, onIapTransaction, offIapTransaction, disposeIap } from '@/uni_modules/lizhao-apple-iap'

let requestBusy = false
let pageActive = true
let listenerId: string | null = null

function removeExampleListener(): void {
  const id = listenerId
  if (id == null) return
  const result = offIapTransaction(id)
  if (result.removed) listenerId = null
  else console.log('监听移除失败;请查看诊断')
}
function startIapExample(productId: string): void {
  if (requestBusy || !pageActive) return
  const capability = getIapCapabilitiesSync()
  if (!capability.available) { console.log(capability.reason); return }
  requestBusy = true
  initializeIap({
    timeoutMs: 30000,
    success: () => {
      if (!pageActive) { requestBusy = false; return }
      if (listenerId == null) {
        const result = onIapTransaction((event) => { console.log('交易事件:' + event.kind) })
        if (result.registered) listenerId = result.subscriptionId
        else console.log('未注册监听;请查看诊断')
      }
      getIapProducts({
        productIds: [productId], timeoutMs: 30000,
        success: (result) => { console.log('商品=' + result.products.length + ';缺失=' + result.missingProductIds.length) },
        fail: (error) => { console.log('查询失败:' + error.errCode + ' ' + error.errMsg) },
        complete: (result) => { requestBusy = false; console.log('查询完成:' + result.outcome) }
      })
    },
    fail: (error) => { console.log('初始化失败:' + error.errCode + ' ' + error.errMsg) },
    complete: (result) => { if (result.outcome != 'succeeded') requestBusy = false }
  })
}
function releaseIapService(): void {
  if (requestBusy) return
  removeExampleListener()
  requestBusy = true
  disposeIap({
    timeoutMs: 30000,
    success: (result) => { console.log('释放结果:' + result.disposed) },
    fail: (error) => { console.log('释放失败:' + error.errCode + ';不代表购买已取消') },
    complete: () => { requestBusy = false }
  })
}
onUnload(() => { pageActive = false; removeExampleListener() })

uni-app:Vue2 / Vue3 共用的 JavaScript 页面流程

以下对象可作为 .vue 页面的脚本导出;在页面按钮中调用 startExample。productId 默认留空,必须通过输入框填写自己的真实值。包含初始化、查询、失败/完成和监听释放;购买与交付的完整可操作页面见下方真实路径。

import { getIapCapabilitiesSync, initializeIap, getIapProducts, onIapTransaction, offIapTransaction, disposeIap } from '@/uni_modules/lizhao-apple-iap'

export default {
  data() { return { productId: '', busy: false, active: true, listenerId: null, status: '尚未初始化' } },
  methods: {
    startExample() {
      if (this.busy || !this.active) return
      const capability = getIapCapabilitiesSync()
      if (!capability.available) { this.status = capability.reason; return }
      this.busy = true
      initializeIap({
        timeoutMs: 30000,
        success: () => {
          if (!this.active) { this.busy = false; return }
          if (this.listenerId == null) {
            const listener = onIapTransaction(event => { if (this.active) this.status = '交易事件:' + event.kind })
            if (listener.registered) this.listenerId = listener.subscriptionId
            else console.log('监听注册失败;请查看诊断')
          }
          getIapProducts({
            productIds: [this.productId], timeoutMs: 30000,
            success: result => { if (this.active) this.status = '商品=' + result.products.length + ';缺失=' + result.missingProductIds.length },
            fail: error => { if (this.active) this.status = error.errCode + ' ' + error.errMsg },
            complete: result => { this.busy = false; console.log('查询结束:' + result.outcome) }
          })
        },
        fail: error => { if (this.active) this.status = error.errCode + ' ' + error.errMsg },
        complete: result => { if (result.outcome !== 'succeeded') this.busy = false }
      })
    },
    removeExampleListener() {
      if (this.listenerId == null) return
      const result = offIapTransaction(this.listenerId)
      if (result.removed) this.listenerId = null
      else console.log('监听移除失败;请查看诊断')
    },
    releaseService() {
      if (this.busy) return
      this.removeExampleListener()
      this.busy = true
      disposeIap({
        timeoutMs: 30000,
        success: result => { if (this.active) this.status = '释放=' + result.disposed },
        fail: error => { if (this.active) this.status = error.errCode + ' ' + error.errMsg },
        complete: () => { this.busy = false }
      })
    }
  },
  onUnload() { this.active = false; this.removeExampleListener() }
}

完整的 Tabs 页面包含全部 19 个公开 API、购买/取消/补单/订阅操作、真实错误与完整报告:uni-app x 使用 uni_modules/lizhao-apple-iap/example/uniappx/index.uvue,uni-app 使用 uni_modules/lizhao-apple-iap/example/uniapp/index.vue。报告主源为 uni_modules/lizhao-apple-iap/example/report.uts,传统页面使用 uni_modules/lizhao-apple-iap/static/report.js。维护时由 scripts/build-apple-iap-examples.cjs 生成 Vue2/Vue3 页面,不能单改生成物。

API 用途速查

公开方法

所有方法从 @/uni_modules/lizhao-apple-iap 导入。iOS入口调用真实Swift混编实现;下表列出公开接口及条件,非iOS统一拒绝Apple支付。

API 用途与返回值 条件与行为
getIapCapabilitiesSync() 同步取得 IapCapabilities 真实版本/权限/初始化状态,支持与验证结论分开
initializeIap(options) 初始化及开启交易观察,成功 IapInitializeResult 启动updates及未完成交易回放
getIapProducts(options) 查询商品,成功 IapProductsResult 真实商品及missingProductIds
purchaseIapProduct(options) 发起购买,成功 IapPurchaseResult 仅verified成功,不自动finish
getIapUnfinishedTransactions(options) 未完成交易,成功 IapTransactionsResult transactions、issues、partial
finishIapTransaction(options) 交付后完成交易,成功 IapFinishResult deliveryConfirmed必须true,找不到拒绝
getIapCurrentEntitlements(options) 当前有效权益,成功 IapTransactionsResult 不包含消耗型;业务核对有效期
getIapLatestTransaction(options) 指定商品最近交易,成功 IapLatestResult 无记录时found=false
restoreIapPurchases(options) 用户主动同步与恢复,成功 IapTransactionsResult 用户主动AppStore.sync后查权益
getIapSubscriptionStatus(options) 订阅状态,成功 IapSubscriptionResult 交易与续期均验证,问题单列
isIapIntroOfferEligible(options) 首次优惠资格,成功 IapIntroOfferResult 资格与是否配置优惠分开
showIapManageSubscriptions(options) 系统订阅管理,成功 IapSystemSheetResult 唯一前台窗口,返回dismissed
requestIapRefund(options) 系统退款申请,成功 IapSystemSheetResult 受理返回submitted;取消进入fail
presentIapOfferCodeSheet(options) 系统优惠码页,成功 IapSystemSheetResult iOS16+,返回dismissed不代表已兑换
disposeIap(options) 释放资源,成功 IapDisposeResult 在途请求返回9362019;成功释放观察/回调
onIapTransaction(callback) 注册单个持续回调,返回 IapListenerResult 先初始化;最多128订阅,必须释放
offIapTransaction(subscriptionId) 移除指定回调,返回 IapUnlistenResult 本地停止派发,Native清理失败如实返回
getIapDiagnosticsSync() 同步独立诊断快照 IapDiagnostics 原生源码标识/忙碌/容量及脱敏快照
setIapDiagnosticsEnabled(enabled) 控制详细信息日志,返回void 同时控制UTS与Native详细日志

14 个异步业务入口同步返回插件生成的字符串 requestId,不是 Promise,也不是 Apple 订单号。未填回调不改变终态;已提供的回调各至多一次,顺序为 success或fail → complete。购买仅在StoreKit验证通过时success;pending、取消和结果未知进入对应fail。complete 只包含 errSubject、errCode、errMsg、operation、requestId、outcome,不包含凭据。

选项与默认值

参数 类型 必填 说明 默认值 可选参数
options 对应的Iap…Options 异步业务API必填 初始化、查询、释放可传空对象 无 见下列适用字段
productIds string[] 商品查询必填 1–100个不同商品ID,不自动去重/修剪 无 本地1–255字符;Apple配置最长100;字母数字、点、下划线、连字符
productId string 购买/最近交易/订阅/优惠资格必填 不自动修剪或改变大小写 无 同上商品ID字符规则
appAccountToken string或null 否 业务账户UUID;不是订单幂等键 null 8-4-4-4-12十六进制UUID
quantity number或null 否 购买数量;原生按真实商品类型再校验 1 整数1–10;非消耗/订阅必须为1
transactionId string 完成交易/退款必填 非零规范十进制UInt64字符串;禁止number转换 无 1到18446744073709551615,无前导0
deliveryConfirmed boolean 完成交易必填 业务已验签且持久交付,必须显式true 无 true;其他值拒绝9362015
timeoutMs number或null 否 毫秒;购买超时不能证明Apple未扣款 购买120000;其他30000 整数1000–600000;null使用默认
success (result) => void 否 各API校验后的专属结果 无 见结果合同
fail (error: IapFail) => void 否 可识别错误与状态;异步派发 无 见错误码
complete (result: IapCompleteResult) => void 否 与success/fail同一请求和终态的摘要 无 succeeded/failed/cancelled/pending/unknown
callback IapTransactionCallback 监听必填 单一持续回调;有真实注册才返回标识 无 IapTransactionEvent
subscriptionId string 取消监听必填 只使用成功注册返回的标识 无 不存在/已释放返回9362018
enabled boolean 诊断开关必填 false关闭info,不清空已有日志 true true/false

成功结果、事件与诊断合同

完整类型在 uni_modules/lizhao-apple-iap/utssdk/interface.uts。以下为真实业务返回字段;不代表已通过Apple环境或真机支付验收:

类型 字段及语义
IapInitializeResult requestId、initialized、observerStarted
IapProductsResult requestId、products、missingProductIds;缺失商品不能伪装查询齐全
IapProduct productId、displayName、description、displayPrice、price、currencyCode、type、subscriptionGroupId、subscriptionPeriod、introductoryOffer;price为十进制字符串
IapPurchaseResult requestId、transaction;只将verified交易视为购买成功,仍需业务交付
IapTransaction transactionId、originalTransactionId均为字符串;productId/productType、appAccountToken、quantity、purchaseDateMs、originalPurchaseDateMs、expirationDateMs、revocationDateMs、revocationReason、isUpgraded、ownershipType、environment、verification、signedTransaction
IapTransactionsResult requestId、transactions、issues、partial;验证问题显式列入issues,不静默丢失
IapLatestResult requestId、found、transaction;无记录时found=false、transaction=null
IapFinishResult requestId、transactionId、alreadyFinished;不能据此首次发货
IapSubscriptionResult requestId、statuses、issues、partial;状态含transaction、renewalVerification、willAutoRenew、autoRenewProductId、expirationReason、gracePeriodExpirationDateMs、signedRenewalInfo
IapIntroOfferResult requestId、productId、eligible、offerConfigured;资格与是否配置优惠分开
IapSystemSheetResult requestId、state;系统页结束不等于购买、退款或退订成功
IapDisposeResult requestId、disposed;不撤销Apple交易
IapTransactionEvent eventId、source、kind、transaction、error;kind目标包括updated/revoked/verificationFailed;不能仅按transactionId去重
IapCapabilities platform、implementation、available、initialized、paymentPermission、minimumIOS、三个can…系统页能力、reason
IapDiagnostics pluginId、pluginVersion(来自package生成文件)、buildId、platform、implementation、generatedAt、entries、droppedEntries、retainedChars、detailed、nativeSnapshot

时间戳单位为 Unix 毫秒,可选原生字段不可取得时为 null。JWS 原文和 appAccountToken 仅交可信业务处理,禁止写入日志。当前诊断缓存上限300条、64000字符;单条超限会截断并标注,回收优先保留错误,所有丢弃均计数。诊断开关不触发上传。

错误码说明

所有插件错误的 errSubject 均为 lizhao-apple-iap。IapFail 同时包含 operation、requestId、outcome、stage、nativeType/nativeCode/nativeCause/nativeStack;当前没有原生异常时对应字段为 null。

错误码 含义 处理建议
9362001 参数无效 按stage修正;不会静默转换ID/数量
9362002 非iOS平台不支持支付 查询能力,禁止发起Apple购买
9362003 当前宿主未加载Native适配器 检查iOS入口及匹配源码的基座
9362004 尚未初始化 先initializeIap,再注册监听或操作交易
9362005 已有原生交互进行中 等待当前操作,不并发重购
9362006 系统限制购买 根据真实支付权限处理
9362007 商品不存在 核对实际商品和运行环境
9362008 用户取消,outcome=cancelled 结束本次UI操作,不当成成功
9362009 待审批,outcome=pending 等待交易更新/查询恢复,不重购
9362010 交易或续期信息验证失败 不发货、不盲目finish,保留问题信息
9362011 结果未知,outcome=unknown 先查询交易和服务端结果;禁止自动重购
9362012 查询超时 可由用户重查;不能推断购买状态
9362013 网络或商店服务不可用 展示真实错误,结合交易查询恢复
9362014 找不到可确认交易 核对ID和unfinished结果
9362015 未明确确认交付 先完成业务验签和持久化发货
9362016 原生或桥接失败 复制脱敏完整报告定位
9362017 系统版本/窗口不满足 检查能力及系统页条件
9362018 监听标识无效或已释放 不冒充移除成功;检查真实注册结果
9362019 释放未完成、原生仍处理 等待原生操作,不假设释放即取消购买

全部错误码都有真实校验或原生分支。9362016还可能表示桥接结构、构建身份或容量不匹配;这些错误不能解释为Apple必然未扣款。除取消、待审批和结果未知外,错误outcome为failed。回调抛错只进入脱敏诊断,不改写本次终态。

注意事项

宿主和平台

宿主 / 平台 源码与示例范围 当前兼容结论
uni-app x / iOS UTS 根入口、Swift 混编;本工程采用 Vapor;iOS 15+ 目标平台;本地资源/生成物验证与 Apple 编译、匹配基座业务验收分开,市场声明保留待确认
uni-app / iOS / Vue2、Vue3 / App-vue 同一 API,独立 JavaScript 拦截与生成的选项式示例 目标宿主;分别做资源验证,不能以 x 的通过代替;市场声明保留待确认
uni-app x / 非 Vapor API 不因示例整理删除;本轮未建立此渲染器运行证据 未验收,不声明已兼容
uni-app / nvue 未提供 nvue 专属示例与运行验收 不支持范围,不能用 .vue 通过代替
Android 有明确平台拒绝实现 不提供 Apple 支付;能力 available=false,有效业务请求失败 9362002
Harmony 有明确平台拒绝实现 不提供 Apple 支付;本轮未做 DevEco 原生编译/设备验证
Web(Chrome / Safari / Firefox / Edge) 可展示示例并调用拒绝入口 不提供 Apple 支付;浏览器交互验证只覆盖页面与拒绝合同
微信 / 支付宝小程序 独立入口保留相同公开合同 不提供 Apple 支付;本轮未做小程序构建与运行
其他小程序、快应用 未适配 Apple StoreKit 不支持;没有专属入口时框架可选公共入口,但不保证该宿主编译或支付能力
uniCloud 无云函数/云对象实现 不支持云插件部署;独立 Node.js 验签参考不等于 uniCloud 支持

发布配置使用两个宿主各自的 platforms.client 节点。Vue 框架兼容项表示相应语法与调用入口可接入;操作系统项另行表示 Apple 内购能力,不把框架兼容或网页示例当成支付验收。

发布配置项 本次标记 依据与接入边界
uni-app 的 Vue2 / Vue3 √ 支持 两种框架均已有独立示例、真实 Web 运行、公共合同及 iOS 资源生成证据;实际支付按 iOS 项单独验收
uni-app 的 App-vue - 待确认 已有资源编译,仍需匹配 iOS 基座调用验收
uni-app 与 uni-app x 的 iOS - 待确认 已有 StoreKit 2 源码及 iOS 15.0 目标配置,原生/支付验收未补齐
Vapor dcloudext.vapor: "-" 示例按 Vapor 维护,资源编译不等于 iOS 运行通过
Android / Harmony / nvue x 不支持 不提供 Apple 内购;保留明确拒绝和能力检测
Web(配置列 Chrome / Safari) x 不支持 Firefox、Edge 等浏览器同样不能通过本插件调用 Apple 内购
uni-app 的微信 / 支付宝 / 抖音 / 百度 / 快手 / 京东 / 鸿蒙 / QQ / 飞书 / 小红书小程序 mp 下逐项为 x 使用当前分类字段;未实现的小程序不默认为可用
uni-app x 的微信 / 支付宝小程序 mp.weixin / mp.alipay 为 x 不提供 Apple 内购;其余未列宿主也不在支持范围
华为 / 联盟快应用 quickapp.huawei / quickapp.union 为 x 未接入 StoreKit,无 Apple 支付实现
阿里云 / 腾讯云 / 支付宝云 cloud 下逐项为 x 无 uniCloud 部署实现;Node.js 验签参考需独立运行
暗黑 / 多语言 / 宽屏模式 dcloudext 对应项为 x 未声明专属外观、国际化或宽屏适配能力

插件市场字段遵循 uni_modules 配置规范:- 表示待确认,x 表示不支持。目录存在、跨端导入成功或拒绝合同通过,都不能当成平台支付支持证据。未实现的快应用或渲染方式按本表明确排除,不复制其他插件的支持勾选。

构建项 要求与来源
HBuilderX / 宿主 package.json.engines 保留既有最低 5.33 声明;本轮使用实际 HX 5.34.2026102909,不把当前版本冒充应用构建版本
iOS 系统 uni_modules/lizhao-apple-iap/utssdk/app-ios/config.json 为 15.0;优惠码页额外要求 16.0
原生工具链 现有 Swift 源码使用 Swift 5.9 特性,需 Xcode 15+;Windows 资源输出不是 Apple SwiftCompile
原生框架 StoreKit、Foundation、UIKit;无需第三方支付 SDK
基座匹配 首次接入或原生/依赖/构建身份变化需要重新联编;本次首次发布元数据包含版本与构建身份更新,需使用新源重新联编 iOS 基座

uni_modules/lizhao-apple-iap/utssdk/app-ios/config.json 声明StoreKit/Foundation/UIKit。Native使用Swift 5语言模式和Swift 5.9特性,需Xcode 15+;兑换码页需iOS16+,其余系统页还需唯一的前台活动窗口,多个活动窗口不猜测目标。退款submitted仅表示申请受理;dismissed仅表示系统页返回。

uni_modules/lizhao-apple-iap/utssdk/app-ios/PrivacyInfo.xcprivacy 为本地请求计时使用的 systemUptime 声明 SystemBootTime / 35F9.1;只测量本应用内经过时间,不读取或上传开机时刻。插件不默认收集上报交易或账户,宿主业务自行发送到服务器时必须维护自己的隐私声明。无额外IAP entitlement,实际签名、商品与Apple服务配置由应用维护。

生命周期、容量与恢复

同时最多32个实际原生请求;JS页面订阅最多128个、共用一个Native订阅,直接UTS原生订阅最多64个(包含JS桥占用的订阅)。商品缓存200项,进程内已完成证明缓存200笔。重复finish只在本进程确证已完成时返回alreadyFinished=true,重启后找不到返回9362014。注册失败不保留页面回调;off先停止本地派发,Native清理失败如实返回。JS宿主使用固定dispatcher和显式代际,页面引用仅保留在可释放表中。

iOS商品模型内部使用合法的description override保留公开字段,并通过白名单DTO字符串桥交给JS,避免普通class字符串化为空。调用者始终读取相同字段;自行序列化原生对象时应显式组装所需字段,禁止把交易/JWS整体写入日志。

批量最多扫描1000条,完整结果payload约1MiB;超过条数或实测JSON字节预算时保留已返回部分并给出 partial=true 和 verification=unknown 的容量问题。未返回交易不会自动finish。未完成交易可先核验/交付/完成当前批次后再次查询;权益或订阅大批量应使用服务器接口完整核对。其他结果或单笔事件超限返回9362016/capacity,不伪造空成功。无监听时最多缓存50条/256KiB事件,丢弃计数可查;缓存不是订单账本,仍应主动查询unfinished并与服务端核对。

Native超时只结束一次等待,原生交互锁保留到系统操作实际返回;迟到已验证购买会进入补单事件。UTS另有比原生期限晚约1秒的桥接兜底,不保证系统实时调度。dispose有任何实际请求未返回时拒绝9362019;成功释放观察/回调/缓存,不修改Apple未完成交易。商品实际支付价格以Apple确认页为准,业务展示前应重新查询商品。

诊断、隐私和报告

示例底部“一键复制日志”复制环境、插件快照和保留的完整操作轨迹,独立于页面可见行。分段复制基于同一固定报告编号和正文,只在实际复制成功后推进;失败显示真实错误并允许手动复制。生成新报告时会建立新快照,不能混用不同报告的分段。

版本来源分开:插件版本来自生成并检查的package字段;示例构建标记来自源码;运行环境来自实际宿主API;无法取得的编译器、构建HX、自定义基座身份明确记为unknown。不得用开发者当前电脑版本冒充应用安装包构建版本。日志会隐藏JWS、账户UUID、完整交易身份、授权头和敏感路径/地址,不自动上传日志,不为诊断申请无关权限。

验证与基座边界

同源UTS桌面回归、静态语法、宿主资源编译、Swift生成物、Apple SwiftCompile、匹配基座运行、真实支付是不同层次。Windows上的资源导出不能证明Apple原生编译或真实购买。需匹配源码的iOS联编/自定义基座,appResource或WGT不能替换旧基座原生代码。

当前未验收Apple签名/真机/沙箱购买、退款、恢复、订阅、离线重试和实际系统剪贴板。需准备真实开发者配置、商品和沙箱/TestFlight环境;账户、证书、密钥不要放入插件源码。恢复不自动触发,finish前必须验签并幂等交付,取消或超时不自动重购。

作者系列 UTS 插件

下表由作者统一维护并核实的已发布清单生成;本插件尚无已核实的市场详情页,因此未列为已上架产品。

插件 能力方向 插件市场
lizhao-alipay-kit 支付宝授权登录、App 支付与脱敏诊断 查看插件
lizhao-app-keepalive 应用保活、唤醒、自愈与报告 查看插件
lizhao-bg-audio 背景音频播放、队列、倍速与事件 查看插件
lizhao-ble BLE 扫描、连接、读写、通知与自动重连 查看插件
lizhao-call-kit 电话、短信、通讯录原生能力 查看插件
lizhao-camera-pro 原生相机、拍照录像、水印与媒体保存 查看插件
lizhao-cast-screen DLNA 投屏、AirPlay 路由入口 查看插件
lizhao-choose-file 原生文件选择、上传、进度与取消 查看插件
lizhao-device-id 设备标识、隐私策略与诊断 查看插件
lizhao-doc-corrector 文档扫描、矫正、增强与识别 查看插件
lizhao-emu-detect 模拟器环境检测、风险评分与证据 查看插件
lizhao-float-window 悬浮窗、画中画、权限与诊断 查看插件
lizhao-gallery-pro 相册媒体分页、筛选、缩略图与导出 查看插件
lizhao-hce-card NFC 虚拟卡、APDU 交互与 NDEF 标签模拟 查看插件
lizhao-health 健康数据授权、查询、统计与写入 查看插件
lizhao-http-server 本地 HTTP 服务、文件传输与实时通信 查看插件
lizhao-icon-pro SVG 图标组件、多主题与缓存 查看插件
lizhao-id-photo 证件照制作、尺寸换算、换底色与模板换装 查看插件
lizhao-img-eraser 图片去水印、局部修复与原生编辑器 查看插件
lizhao-live-activity 实时活动、系统胶囊与普通通知降级 查看插件
lizhao-nfc-pro NFC 标签读写、NDEF、IsoDep 与诊断 查看插件
lizhao-notify-pro 本地通知、点击动作、进度与定时提醒 查看插件
lizhao-novel-reader 原生小说阅读、仿真翻页与段落评论 查看插件
lizhao-office-view Office 与 PDF 文件预览、进度与缓存管理 查看插件
lizhao-pdf-pro PDF 阅读、签批、真实写回与页面处理 查看插件
lizhao-scan-pro 原生扫码、连续扫码、相册识别 查看插件
lizhao-serial-port 路径串口、USB 串口、多会话收发与诊断 查看插件
lizhao-share-plus 系统分享、远程文件下载后分享 查看插件
lizhao-smart-tts 系统 TTS、云端合成、听书方案 查看插件
lizhao-sqlite-pro 原生 SQLite、迁移、备份与诊断 查看插件
lizhao-sse-pro SSE、Line、JSONL 与 Raw 流式请求 查看插件
lizhao-tcp-pro TCP 客户端、服务端、多连接与诊断 查看插件
lizhao-usb-storage U 盘、移动硬盘文件浏览、读写与批量复制 查看插件
lizhao-video-editor 视频裁剪、压缩、取帧与 FFmpeg/FFprobe 查看插件
lizhao-video-feed 短视频上下滑、短剧选集与分页加载 查看插件
lizhao-video-player 原生视频播放器、清晰度与播放交互 查看插件
lizhao-video-thumb 视频封面、批量取帧与 Base64 返回 查看插件
lizhao-vpn-pro 企业 VPN、IKEv2、安全接入与脱敏诊断 查看插件
lizhao-wechat-kit 微信登录、分享、支付、小程序与客服 查看插件

隐私、权限声明

1. 本插件需要申请的系统权限列表:

使用系统StoreKit/Foundation/UIKit,无额外照片、相机、定位或存储权限。需真实商品配置和匹配iOS基座;systemUptime仅测量请求期限,已声明35F9.1。用户主动复制脱敏报告写入剪贴板。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

经显式API使用Apple系统StoreKit查询商品、发起交易或恢复;已验证JWS和业务账户UUID仅交调用方。诊断本地有界脱敏,不默认上传;服务端参考需业务自行部署。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

暂无用户评论。