更新记录

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 中配置内购产品后才能正常使用

环境配置

前置条件

  1. iOS 系统版本 15.0 及以上
  2. App Store Connect 中配置内购产品
  3. 使用有效的 Apple Developer 账号
  4. 配置正确的证书和描述文件

App Store Connect 配置

1. 创建内购产品

  1. 登录 App Store Connect
  2. 选择您的应用
  3. 进入「App 内购买项目」
  4. 点击「+」创建新产品
  5. 选择产品类型(消耗型、非消耗型、自动续期订阅、非自动续期订阅)
  6. 填写产品信息(产品ID、价格、描述等)
  7. 保存并提交审核

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+ 为 ProductionSandbox;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 文件,包含完整的内购示例代码。

技术支持

如果在使用过程中遇到问题,请:

  1. 查阅上方常见问题
  2. 参考 Apple StoreKit2 官方文档
  3. 确认 App Store Connect 中的产品配置
  4. 检查证书和描述文件是否正确

祝您开发愉快! 🎉

隐私、权限声明

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

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

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