Unity 应用内购
命名空间:UnityEngine.Purchasing | 安全:UnityEngine.Purchasing.Security
包:com.unity.purchasing
Unity IAP 有自己的初始化路径:UnityIAPServices.StoreController() → store.Connect()。它不需要 UnityServices.InitializeAsync(),但如果您的项目使用了其他 UGS 服务,它们可以共存。如果存在 Analytics 并且调用了 InitializeAsync(),IAP 将自动发送交易事件。
开始之前
始终首先阅读 references/pre-check.md。 它会扫描项目中的第三方 IAP 包、原生 Google Billing 和现有的 Unity IAP 版本,然后路由到正确的路径。在路由解决之前,不要读取任何其他参考文件或进行任何更改。
详细参考
- 项目扫描和路径路由(首先阅读): 参见 references/pre-check.md
- API 签名和代码示例: 参见 references/api-notes.md
- 平台扩展(Apple, Google): 参见 references/platform-notes.md
- 编辑
IAPProductCatalog.json(架构、小数序列化、刷新): 参见 references/codeless-catalog.md - v4 到 v5 迁移: 参见 references/migration-v4-to-v5.md
- 将原生 Google BillingClient 转换为 Unity IAP 5: 参见 references/path-convert-native-google-billing.md
- 将原生 iOS StoreKit 插件转换为 Unity IAP 5: 参见 references/path-convert-native-storekit.md
- 将 Essential Kit 计费转换为 Unity IAP 5: 参见 references/convert-essentialkit.md
- UniPay (FLOBUK) — 评估和指导: 参见 references/convert-unipay.md
- RevenueCat — 转换评估和指导: 参见 references/convert-revenuecat.md
- Adapty — 转换评估和指导: 参见 references/convert-adapty.md
- 向没有现有 IAP 的项目添加 Unity IAP 5: 参见 references/path-add-iap-to-new-project.md
- 实现 IAP D2C 能力(第三方支付提供商 — Stripe/Coda,需要 v5.4+): 参见 references/path-implement-iap-d2c.md
根据需要读取参考文件——仅当您需要特定的 API 签名、平台扩展详细信息或迁移映射时。
初始化流程
- 通过
UnityIAPServices.StoreController()获取StoreController(或通过DefaultStore()、DefaultProduct()、DefaultPurchase()获取单个服务) - 在调用
Connect()之前,订阅所有必需的事件(参见下方必需的事件订阅) await store.Connect()连接到平台商店- 在
OnStoreConnected上,调用store.FetchProducts(List<ProductDefinition>)加载目录 - 在
OnProductsFetched上,产品准备好显示和购买
使用 Awake() 进行初始化——确保 IAP 在其他 Start() 方法之前准备就绪。
产品类型:ProductType.Consumable、ProductType.NonConsumable、ProductType.Subscription。
获取产品
将产品定义为 List<ProductDefinition> 并传递给 store.FetchProducts()。当产品 ID 在 Apple/Google 商店之间不同时,使用 StoreSpecificIds。对于复杂目录,使用 CatalogProvider 管理产品集和特定于商店的 ID。
| 方法 | 行为 |
|---|---|
GetProducts() | 返回缓存的产品列表(同步,如果未调用 FetchProducts 则为过时) |
FetchProducts() | 向商店查询最新的定价/可用性并更新缓存 |
GetProductById(id) | 按 ID 返回单个缓存产品 |
这些方法不可互换。在依赖 GetProducts() 之前,始终首先调用 FetchProducts()。
两步购买流程
IAP v5 使用强制性的两步流程:待确认 → 确认。
store.PurchaseProduct(product)— 启动平台购买对话框OnPurchasePending触发 — 您收到一个PendingOrder- 验证收据,向玩家授予内容
store.ConfirmPurchase(pendingOrder)— 最终确定交易OnPurchaseConfirmed触发 — 接收Order基类型;模式匹配ConfirmedOrder(成功)与FailedOrder(确认失败)
授予内容后,您必须调用 ConfirmPurchase(pendingOrder)。 未确认的购买将在下次应用启动时重新交付,以防止购买丢失。
去重: 同一购买可能多次触发 OnPurchasePending(例如,在确认前重启应用)。始终检查内容是否已被授予。
消耗品: 已确认的消耗品购买不会由 FetchPurchases 返回。自行跟踪消耗品的授予(例如,在 Cloud Save 或 Economy 中)。
延迟购买: 对于 Ask-to-Buy (iOS) 和 Google Play 延迟购买,OnPurchaseDeferred 会触发。不要授予内容——等待批准后的 OnPurchasePending。
恢复交易
store.RestoreTransactions(callback) 重新交付非消耗品和订阅购买。每个恢复的购买都会触发 OnPurchasePending。
iOS 上需要此功能以符合 Apple App Store 要求——添加一个“恢复购买”按钮。
Apple 非续期订阅无法通过 RestoreTransactions 恢复。请通过服务器端跟踪这些订阅。
收据验证
| 平台 | 方法 |
|---|---|
| Google Play | CrossPlatformValidator 与 GooglePlayTangle.Data() — 支持本地验证 |
| Apple (StoreKit 2) | 本地验证是空操作。使用 order.Info.Apple?.jwsRepresentation 进行服务器端验证 |
通过 Unity 编辑器中的 Services > In-App Purchasing > Receipt Validation Obfuscator 生成 tangle 数据。
权限检查
当您没有 Order 但想知道特定产品的状态时使用(替代 v4 的 product.hasReceipt)。如果您已经有 Order,请检查其类型:PendingOrder 映射到 EntitledUntilConsumed(消耗品)或 EntitledButNotFinished(非消耗品/订阅),ConfirmedOrder 映射到 FullyEntitled。
调用 store.CheckEntitlement(product) 并处理 store.OnCheckEntitlement。检查 entitlement.Status == EntitlementStatus.FullyEntitled。
EntitlementStatus 值:FullyEntitled、EntitledUntilConsumed、EntitledButNotFinished、NotEntitled、Unknown。
获取现有购买
store.FetchPurchases() 从商店检索所有当前购买。在应用启动时使用。
| 方法 | 行为 |
|---|---|
GetPurchases() | 返回缓存的购买列表 |
FetchPurchases() | 向商店查询当前购买并覆盖缓存的列表 |
FetchPurchases() 每次调用都会替换整个缓存的列表。只有非消耗品和订阅才会被返回。