更新记录
1.0.0(2026-02-11)
支持应用内购买
平台兼容性
uni-app(4.76)
| Vue2 | Vue2插件版本 | Vue3 | Vue3插件版本 | Chrome | Safari | app-vue | app-vue插件版本 | app-nvue | app-nvue插件版本 | Android | iOS | iOS插件版本 | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | 1.0.0 | √ | 1.0.0 | × | × | √ | 1.0.0 | √ | 1.0.0 | × | 15 | 1.0.0 | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | × | × | × |
uni-app x(4.76)
| Chrome | Safari | Android | iOS | iOS插件版本 | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|---|
| × | × | × | 15 | 1.0.0 | × | × |
tt-apple-iap
💰 iOS 内购 SDK 插件,基于 StoreKit2 框架,为 uni-app x 提供完整的 iOS 应用内购买集成解决方案
📖 目录
SDK版本信息
| 平台 | 版本 | 支持状态 |
|---|---|---|
| iOS | 15.0+ | ✅ 完全支持 |
| Android | - | ❌ 不支持 |
| HarmonyOS | - | ❌ 不支持 |
📚 推荐阅读: Apple官方文档 - StoreKit2
🚨 重要提示
⚠️ 必须使用自定义基座运行,标准基座无法调试内购功能
⚠️ 仅支持 iOS 15.0 及以上版本,与其他平台同时运行需要加条件编译
⚠️ 需要真机测试,iOS 模拟器可以测试部分功能,但完整测试需要真机
⚠️ 需要配置内购产品,在 App Store Connect 中配置内购产品后才能正常使用
环境配置
前置条件
- iOS 系统版本 15.0 及以上
- 在 App Store Connect 中配置内购产品
- 使用有效的 Apple Developer 账号
- 配置正确的证书和描述文件
App Store Connect 配置
1. 创建内购产品
- 登录 App Store Connect
- 选择您的应用
- 进入「App 内购买项目」
- 点击「+」创建新产品
- 选择产品类型(消耗型、非消耗型、自动续期订阅、非自动续期订阅)
- 填写产品信息(产品ID、价格、描述等)
- 保存并提交审核
2. 重要注意事项
💡 产品ID格式:产品ID 必须是唯一的,建议使用反向域名格式,如:
com.yourapp.product1⚠️ 审核要求:内购产品需要提交审核,审核通过后才能使用
🔄 沙盒测试:开发阶段可以使用沙盒账号进行测试
📱 测试账号:在 App Store Connect 中创建沙盒测试账号进行测试
快速开始
1. 导入插件
// #ifdef APP-IOS
import * as iap from "@/uni_modules/tt-apple-iap";
// #endif
export default {
data() {
return {
// #ifdef APP-IOS
iapImpl: null as iap.TTAppleIAP | null,
// #endif
}
},
onLoad() {
// #ifdef APP-IOS
this.iapImpl = iap.getTTAppleIAP();
// #endif
},
methods: {
// 内购方法
handlePurchase() {
// 购买实现代码见下方
}
}
}
2. 条件编译
由于内购仅支持 iOS 平台,建议使用条件编译:
// #ifdef APP-IOS
import * as iap from "@/uni_modules/tt-apple-iap";
export default {
data() {
return {
iapImpl: null as iap.TTAppleIAP | null,
}
},
onLoad() {
this.iapImpl = iap.getTTAppleIAP();
}
}
// #endif
基础使用
平台检测
checkPlatformSupport() {
// #ifndef APP-IOS
uni.showModal({
title: '平台不支持',
content: 'iOS 内购仅支持 iOS 15.0 及以上版本',
showCancel: false
});
return false;
// #endif
return true;
}
API文档
1. 获取产品信息
获取 App Store 中配置的产品信息,包括价格、描述、订阅信息等。
参数说明
TTAppleIAPGetProductsOptions
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| productIds | string[] | ✅ | 产品ID数组 |
| success | function | ❌ | 成功回调 |
| fail | function | ❌ | 失败回调 |
| complete | function | ❌ | 完成回调(无论成功失败) |
示例代码
// 获取产品信息
fetchProducts() {
// #ifdef APP-IOS
if (!this.iapImpl) {
uni.showToast({
title: '内购SDK未初始化',
icon: 'error'
});
return;
}
this.iapImpl.getProducts({
productIds: ['com.example.product1', 'com.example.product2'],
success: (res) => {
console.log('✅ 获取产品成功:', res.products);
this.products = res.products;
// 显示产品信息
res.products.forEach(product => {
console.log(`产品: ${product.displayName}`);
console.log(`价格: ${product.displayPrice}`);
console.log(`类型: ${product.type}`);
});
},
fail: (err) => {
console.error('❌ 获取产品失败:', err);
uni.showToast({
title: `获取失败: ${err.errMsg}`,
icon: 'error'
});
}
});
// #endif
}
返回值说明
TTAppleIAPGetProductsSuccess
| 参数名称 | 类型 | 描述 |
|---|---|---|
| products | TTAppleIAPProduct[] | 产品信息数组 |
TTAppleIAPProduct
| 参数名称 | 类型 | 描述 |
|---|---|---|
| id | string | 产品ID |
| displayName | string | 产品显示名称 |
| desc | string | 产品描述 |
| displayPrice | string | 产品价格(本地化格式,如 "$0.99") |
| price | number | 产品价格(数值) |
| currencyCode | string | 货币代码(如 "USD", "CNY") |
| type | string | 产品类型(consumable, nonConsumable, autoRenewableSubscription, nonRenewableSubscription) |
| isSubscription | boolean | 是否为订阅产品 |
| subscriptionPeriodUnit | string | null | 订阅周期单位(day, week, month, year) |
| subscriptionPeriodValue | number | null | 订阅周期数量 |
| subscriptionGroupId | string | null | 订阅组ID |
| introductoryPrice | TTAppleIAPIntroductoryPrice | null | 介绍价格信息 |
| promotionalOffers | TTAppleIAPProductPromotionalOffer[] | null | 促销优惠列表 |
2. 请求购买
发起购买请求,调用系统支付界面。
参数说明
TTAppleIAPPurchaseOptions
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| productId | string | ✅ | 产品ID |
| quantity | number | ❌ | 购买数量(默认1,仅消耗型产品有效) |
| appAccountToken | string | ❌ | 用户标识(UUID格式) |
| promotionalOffer | TTAppleIAPPromotionalOffer | ❌ | 促销优惠参数 |
| success | function | ❌ | 成功回调 |
| fail | function | ❌ | 失败回调 |
| complete | function | ❌ | 完成回调 |
示例代码
// 购买产品
purchaseProduct(productId: string) {
// #ifdef APP-IOS
if (!this.iapImpl) {
uni.showToast({
title: '内购SDK未初始化',
icon: 'error'
});
return;
}
this.iapImpl.requestPurchase({
productId: productId,
quantity: 1,
appAccountToken: this.getUserUUID(), // 可选:用户标识
success: (res) => {
console.log('✅ 购买成功:', res.transaction);
// 1. 服务器验证交易
this.verifyTransaction(res.transaction);
},
fail: (err) => {
console.error('❌ 购买失败:', err);
this.handlePurchaseError(err);
}
});
// #endif
}
// 服务器验证交易
verifyTransaction(transaction: iap.TTAppleIAPTransaction) {
uni.request({
url: 'https://your-server.com/api/verify-transaction',
method: 'POST',
data: {
jwsRepresentation: transaction.jwsRepresentation, // JWS格式的交易数据
productId: transaction.productId,
transactionId: transaction.id
},
success: (res) => {
if (res.data.success) {
// 2. 验证成功,发放商品
this.deliverProduct(transaction.productId);
// 3. 完成交易(关闭交易)
this.finishTransaction(transaction);
} else {
console.error('服务器验证失败');
}
},
fail: (err) => {
console.error('验证请求失败', err);
}
});
}
// 完成交易
finishTransaction(transaction: iap.TTAppleIAPTransaction) {
// #ifdef APP-IOS
this.iapImpl?.finishTransaction({
transaction: transaction,
success: () => {
console.log('✅ 交易完成');
},
fail: (err) => {
console.error('❌ 完成交易失败:', err);
}
});
// #endif
}
返回值说明
TTAppleIAPPurchaseSuccess
| 参数名称 | 类型 | 描述 |
|---|---|---|
| transaction | TTAppleIAPTransaction | 交易信息 |
TTAppleIAPTransaction
| 参数名称 | 类型 | 描述 |
|---|---|---|
| id | string | 交易唯一标识 |
| productId | string | 产品ID |
| quantity | number | 购买数量 |
| purchaseDate | number | 交易日期(时间戳,毫秒) |
| originalTransactionId | string | 原始交易ID |
| originalPurchaseDate | number | 原始交易日期(时间戳,毫秒) |
| appAccountToken | string | null | 透传参数 |
| transactionState | string | 交易状态(purchased, pending, failed, revoked, refunded) |
| environment | string | 交易环境:iOS 16+ 为 Production 或 Sandbox;iOS 15 固定为 Production |
| jwsRepresentation | string | JWS格式的交易数据(用于服务端验证) |
| isInAppOwnershipTypeFamily | boolean | 是否为家庭共享购买 |
| revocationDate | number | null | 撤销日期 |
| revocationReason | number | null | 撤销原因 |
3. 恢复购买
恢复已购买的产品,适用于非消耗型产品和订阅产品(消耗型产品无法通过恢复购买恢复)。
参数说明
TTAppleIAPRestoreOptions
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| success | function | ❌ | 成功回调 |
| fail | function | ❌ | 失败回调 |
| complete | function | ❌ | 完成回调 |
示例代码
// 恢复购买
restorePurchases() {
// #ifdef APP-IOS
if (!this.iapImpl) {
uni.showToast({
title: '内购SDK未初始化',
icon: 'error'
});
return;
}
uni.showLoading({
title: '恢复中...'
});
this.iapImpl.restorePurchases({
success: (res) => {
uni.hideLoading();
console.log('✅ 恢复成功:', res.transactions);
if (res.transactions.length === 0) {
uni.showToast({
title: '没有可恢复的购买记录',
icon: 'none'
});
} else {
uni.showToast({
title: `成功恢复 ${res.transactions.length} 条记录`,
icon: 'success'
});
// 处理恢复的交易
res.transactions.forEach(transaction => {
this.verifyTransaction(transaction);
});
}
},
fail: (err) => {
uni.hideLoading();
console.error('❌ 恢复失败:', err);
uni.showToast({
title: `恢复失败: ${err.errMsg}`,
icon: 'error'
});
}
});
// #endif
}
4. 获取交易历史
获取当前有效的交易记录。
参数说明
TTAppleIAPTransactionHistoryOptions
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| productId | string | ❌ | 产品ID(可选,如果提供则只返回该产品的交易) |
| revision | string | ❌ | 分页标识(用于获取下一页数据) |
| success | function | ❌ | 成功回调 |
| fail | function | ❌ | 失败回调 |
| complete | function | ❌ | 完成回调 |
示例代码
// 获取交易历史
getTransactionHistory() {
// #ifdef APP-IOS
if (!this.iapImpl) {
return;
}
this.iapImpl.getTransactionHistory({
productId: 'com.example.product1', // 可选:指定产品ID
success: (res) => {
console.log('✅ 获取交易历史成功:', res.transactions);
console.log('是否有更多:', res.hasMore);
console.log('分页标识:', res.revision);
this.transactions = res.transactions;
},
fail: (err) => {
console.error('❌ 获取交易历史失败:', err);
}
});
// #endif
}
5. 获取订阅状态
获取指定产品的订阅状态信息。当前实现:若存在该产品的有效权益则返回 state: 'subscribed',否则走失败回调(错误码 1004);完整状态(如 expired、revoked 等)以接口定义为准,后续版本可能增强。
参数说明
TTAppleIAPSubscriptionStatusOptions
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| productId | string | ✅ | 产品ID |
| success | function | ❌ | 成功回调 |
| fail | function | ❌ | 失败回调 |
| complete | function | ❌ | 完成回调 |
示例代码
// 获取订阅状态
getSubscriptionStatus(productId: string) {
// #ifdef APP-IOS
if (!this.iapImpl) {
return;
}
this.iapImpl.getSubscriptionStatus({
productId: productId,
success: (res) => {
console.log('✅ 订阅状态:', res.status);
console.log('状态:', res.status.state);
console.log('续费日期:', res.status.renewalDate);
console.log('过期日期:', res.status.expirationDate);
if (res.status.state === 'subscribed') {
console.log('用户已订阅');
} else if (res.status.state === 'expired') {
console.log('订阅已过期');
}
},
fail: (err) => {
console.error('❌ 获取订阅状态失败:', err);
}
});
// #endif
}
6. 获取未完成交易
获取未完成的交易列表,通常在应用启动时调用。
参数说明
TTAppleIAPUnfinishedTransactionsOptions
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| success | function | ❌ | 成功回调 |
| fail | function | ❌ | 失败回调 |
| complete | function | ❌ | 完成回调 |
示例代码
// 应用启动时检查未完成交易
onLoad() {
// #ifdef APP-IOS
this.iapImpl = iap.getTTAppleIAP();
this.checkUnfinishedTransactions();
// #endif
}
checkUnfinishedTransactions() {
// #ifdef APP-IOS
if (!this.iapImpl) {
return;
}
this.iapImpl.getUnfinishedTransactions({
success: (res) => {
if (res.transactions.length > 0) {
console.log(`发现 ${res.transactions.length} 个未完成交易`);
// 处理未完成的交易
res.transactions.forEach(transaction => {
this.verifyTransaction(transaction);
});
}
},
fail: (err) => {
console.error('❌ 获取未完成交易失败:', err);
}
});
// #endif
}
7. 完成交易
完成交易(关闭交易),在服务器验证成功后必须调用。
参数说明
TTAppleIAPFinishTransactionOptions
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| transaction | TTAppleIAPTransaction | ✅ | 交易对象 |
| success | function | ❌ | 成功回调 |
| fail | function | ❌ | 失败回调 |
| complete | function | ❌ | 完成回调 |
示例代码
// 完成交易
finishTransaction(transaction: iap.TTAppleIAPTransaction) {
// #ifdef APP-IOS
if (!this.iapImpl) {
return;
}
this.iapImpl.finishTransaction({
transaction: transaction,
success: (res) => {
console.log('✅ 交易完成:', res.state);
},
fail: (err) => {
console.error('❌ 完成交易失败:', err);
}
});
// #endif
}
产品类型详解
1. 消耗性产品(Consumable)
- 特点:可重复购买,购买后消耗使用
- 数量设置:支持设置购买数量(1-10个)
- 示例:游戏金币、道具、会员天数等
- 恢复机制:无法通过
restorePurchases恢复,只能通过getUnfinishedTransactions获取未完成的交易
2. 非消耗性产品(Non-Consumable)
- 特点:一次购买,永久拥有
- 数量限制:同一 Apple ID 只能购买一次
- 跨设备:可在登录同一 Apple ID 的任何设备上恢复
- 示例:去广告功能、永久VIP、解锁功能等
3. 自动续期订阅产品(Auto-Renewable Subscription)
- 特点:自动续费的订阅服务
- 续费机制:到期前自动续费
- 沙盒限制:沙盒环境最多自动续订12次
- 示例:月度会员、年度订阅等
- 订阅信息:支持查询订阅周期、介绍价格、促销优惠等详细信息
4. 非自动续期订阅产品(Non-Renewable Subscription)
- 特点:不自动续费的订阅服务
- 续费方式:需要用户手动续费
- 时效管理:开发者需要自己管理订阅过期时间
- 示例:月卡、季卡等
错误处理
错误码对照表
| 错误码 | 说明 | 解决方案 |
|---|---|---|
| 1000 | 正在处理中,支付结果未知 | 等待处理完成或重试 |
| 1001 | 用户中途取消 | 提示用户重新尝试 |
| 1002 | 网络连接出错 | 检查网络连接 |
| 1003 | 不允许App内购买项目 | 引导用户开启内购权限 |
| 1004 | 产品无效 | 检查产品ID配置 |
| 1005 | 促销信息错误 | 检查促销参数配置 |
| 1006 | 缺少支付参数 | 检查必填参数 |
| 1007 | 只支持iOS15以上的版本 | 提示用户升级iOS |
| 1008 | 其他未知错误 | 联系技术支持 |
错误处理最佳实践
// 统一错误处理函数
handlePurchaseError(error: any) {
console.error('❌ 购买失败:', error);
const errCode = error.errCode;
const errMsg = error.errMsg;
switch(errCode) {
case 1000:
uni.showToast({
title: '正在处理中,请稍候',
icon: 'loading',
duration: 2000
});
break;
case 1001:
// 用户取消,不显示错误提示
console.log('用户取消购买');
break;
case 1002:
uni.showToast({
title: '网络错误,请检查网络连接',
icon: 'error'
});
break;
case 1003:
uni.showModal({
title: '提示',
content: '请在设置中开启App内购买权限',
confirmText: '去设置',
success: (res) => {
if (res.confirm) {
// 跳转到系统设置(需根据项目使用 uni API 或原生能力打开设置页)
// 例如: uni.openSetting() 或 原生 openURL('app-settings:')
}
}
});
break;
case 1004:
uni.showToast({
title: '商品暂时无法购买',
icon: 'error'
});
break;
case 1005:
uni.showToast({
title: '促销信息错误',
icon: 'error'
});
break;
case 1006:
uni.showToast({
title: '缺少必要参数',
icon: 'error'
});
break;
case 1007:
uni.showModal({
title: '系统要求',
content: '需要iOS 15.0以上版本才能使用内购功能',
showCancel: false
});
break;
default:
uni.showToast({
title: errMsg || '购买失败,请重试',
icon: 'error'
});
break;
}
}
最佳实践
1. 服务器验证流程
// 购买成功后的完整处理流程
handlePurchaseSuccess(transaction: iap.TTAppleIAPTransaction) {
// 1. 发送交易数据到服务器验证
uni.request({
url: 'https://your-server.com/api/verify-transaction',
method: 'POST',
data: {
jwsRepresentation: transaction.jwsRepresentation, // JWS格式,用于服务器验证
productId: transaction.productId,
transactionId: transaction.id,
userId: this.getCurrentUserId()
},
success: (res) => {
if (res.data.success) {
// 2. 验证成功,发放商品
this.deliverProduct(transaction.productId);
// 3. 完成交易(必须在验证成功后调用)
this.finishTransaction(transaction);
uni.showToast({
title: '购买成功',
icon: 'success'
});
} else {
console.error('服务器验证失败:', res.data);
uni.showToast({
title: '验证失败,请联系客服',
icon: 'error'
});
}
},
fail: (err) => {
console.error('验证请求失败:', err);
uni.showToast({
title: '网络错误,请稍后重试',
icon: 'error'
});
}
});
}
2. 应用启动时检查未完成交易
onLoad() {
// #ifdef APP-IOS
this.iapImpl = iap.getTTAppleIAP();
// 检查未完成的交易
this.checkUnfinishedTransactions();
// #endif
}
checkUnfinishedTransactions() {
// #ifdef APP-IOS
if (!this.iapImpl) {
return;
}
this.iapImpl.getUnfinishedTransactions({
success: (res) => {
if (res.transactions.length > 0) {
console.log(`发现 ${res.transactions.length} 个未完成交易`);
// 处理未完成的交易
res.transactions.forEach(transaction => {
// 服务器验证
this.verifyTransaction(transaction);
});
}
},
fail: (err) => {
console.error('获取未完成交易失败:', err);
}
});
// #endif
}
3. 订阅产品状态检查
// 检查用户订阅状态
checkSubscriptionStatus(productId: string) {
// #ifdef APP-IOS
if (!this.iapImpl) {
return;
}
this.iapImpl.getSubscriptionStatus({
productId: productId,
success: (res) => {
const status = res.status;
switch(status.state) {
case 'subscribed':
// 用户已订阅
console.log('订阅有效,到期时间:', status.expirationDate);
this.enablePremiumFeatures();
break;
case 'expired':
// 订阅已过期
console.log('订阅已过期');
this.disablePremiumFeatures();
break;
case 'inBillingRetryPeriod':
// 续费失败,但在宽限期内
console.log('续费失败,宽限期内');
break;
case 'inGracePeriod':
// 宽限期
console.log('宽限期内');
break;
case 'revoked':
// 订阅被撤销
console.log('订阅被撤销');
this.disablePremiumFeatures();
break;
}
},
fail: (err) => {
console.error('获取订阅状态失败:', err);
}
});
// #endif
}
4. 促销优惠使用
// 使用促销优惠购买
purchaseWithPromotionalOffer(productId: string, promotionalOffer: iap.TTAppleIAPPromotionalOffer) {
// #ifdef APP-IOS
if (!this.iapImpl) {
return;
}
this.iapImpl.requestPurchase({
productId: productId,
promotionalOffer: promotionalOffer,
success: (res) => {
console.log('✅ 使用促销优惠购买成功');
this.verifyTransaction(res.transaction);
},
fail: (err) => {
console.error('❌ 购买失败:', err);
this.handlePurchaseError(err);
}
});
// #endif
}
常见问题
1. 标准基座无法调试?
解决方案: 内购功能必须使用自定义基座,标准基座不包含内购功能。
2. 产品ID无效?
解决方案:
- 确认产品ID在 App Store Connect 中已正确配置
- 检查产品是否已提交审核并通过
- 确认产品ID格式正确(建议使用反向域名格式)
- 沙盒环境需要使用沙盒测试账号
3. 购买成功但未收到商品?
解决方案:
- 检查是否调用了
finishTransaction完成交易 - 检查服务器验证逻辑是否正确
- 查看未完成交易列表,处理未完成的交易
- 确认网络连接正常
4. 恢复购买失败?
解决方案:
- 确认用户使用的是购买时的 Apple ID
- 检查产品类型(消耗型产品无法恢复)
- 确认网络连接正常
- 检查产品是否仍然有效
5. 订阅状态查询失败?
解决方案:
- 确认产品ID是订阅类型产品
- 检查网络连接
- 确认用户已购买过该订阅产品
6. iOS 版本兼容性?
解决方案:
- 插件仅支持 iOS 15.0 及以上版本
- 在 iOS 15.0 以下版本隐藏购买入口
- 使用条件编译处理平台差异
7. 服务器验证失败?
解决方案:
- 确保使用
jwsRepresentation字段进行验证 - 检查服务器端验证逻辑是否正确
- 确认 Apple 公钥是否为最新版本
- 验证应用的 Bundle ID 是否正确
8. 沙盒测试问题?
解决方案:
- 使用沙盒测试账号进行测试
- 确认测试账号已正确配置
- 注意沙盒环境的限制(如订阅最多续订12次)
- 测试完成后清除沙盒数据
完整示例
参考项目中的 pages/index/index.uvue 文件,包含完整的内购示例代码。
技术支持
如果在使用过程中遇到问题,请:
- 查阅上方常见问题
- 参考 Apple StoreKit2 官方文档
- 确认 App Store Connect 中的产品配置
- 检查证书和描述文件是否正确
祝您开发愉快! 🎉

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(1)
下载 437
赞赏 3
下载 11229577
赞赏 1860
赞赏
京公网安备:11010802035340号