implement-in-app-purchases

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Unity In-App Purchasing

Unity 应用内购买

Namespace:
UnityEngine.Purchasing
| Security:
UnityEngine.Purchasing.Security
Package:
com.unity.purchasing
Unity IAP has its own initialization path via
UnityIAPServices.StoreController()
store.Connect()
. It does not require
UnityServices.InitializeAsync()
, but they can coexist if your project uses other UGS services. If Analytics is present and
InitializeAsync()
is called, IAP will automatically send transaction events.
命名空间:
UnityEngine.Purchasing
| 安全模块:
UnityEngine.Purchasing.Security
包:
com.unity.purchasing
Unity IAP 拥有独立的初始化路径,通过
UnityIAPServices.StoreController()
store.Connect()
完成。它不需要调用
UnityServices.InitializeAsync()
,但如果你的项目使用其他UGS服务,两者可以共存。如果项目中包含Analytics且调用了
InitializeAsync()
,IAP会自动发送交易事件。

Before You Start

开始之前

Always read references/pre-check.md first. It scans the project for third-party IAP packages, native Google Billing, and existing Unity IAP versions, then routes to the correct path. Do not read any other reference file or make any changes until routing is resolved.
请务必先阅读 references/pre-check.md。该文档会扫描项目中的第三方IAP包、原生Google计费系统以及现有Unity IAP版本,然后引导至正确的操作路径。在确定路径之前,请勿阅读其他参考文档或进行任何修改。

Detailed References

详细参考文档

  • Project scan and path routing (read first): See references/pre-check.md
  • API signatures & code examples: See references/api-notes.md
  • Platform extensions (Apple, Google): See references/platform-notes.md
  • Editing
    IAPProductCatalog.json
    (schema, decimal serialization, refresh):
    See references/codeless-catalog.md
  • v4 → v5 migration: See references/migration-v4-to-v5.md
  • Convert native Google BillingClient to Unity IAP 5: See references/path-convert-native-google-billing.md
  • Convert native iOS StoreKit plugin to Unity IAP 5: See references/path-convert-native-storekit.md
  • Convert Essential Kit billing to Unity IAP 5: See references/convert-essentialkit.md
  • UniPay (FLOBUK) — assessment and guidance: See references/convert-unipay.md
  • RevenueCat — conversion assessment and guidance: See references/convert-revenuecat.md
  • Adapty — conversion assessment and guidance: See references/convert-adapty.md
  • Add Unity IAP 5 to a project with no existing IAP: See references/path-add-iap-to-new-project.md
  • Implement IAP D2C Capabilities (third-party payment provider — Stripe/Coda, requires v5.4+): See references/path-implement-iap-d2c.md
Read reference files on demand — only when you need specific API signatures, platform extension details, or migration mappings.
  • 项目扫描与路径引导(优先阅读): 查看 references/pre-check.md
  • API签名与代码示例: 查看 references/api-notes.md
  • 平台扩展(Apple、Google): 查看 references/platform-notes.md
  • 编辑
    IAPProductCatalog.json
    (schema、十进制序列化、刷新):
    查看 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签名、平台扩展细节或迁移映射时再查看。

Initialization Flow

初始化流程

  1. Obtain
    StoreController
    via
    UnityIAPServices.StoreController()
    (or individual services via
    DefaultStore()
    ,
    DefaultProduct()
    ,
    DefaultPurchase()
    )
  2. Subscribe to all required events (see Required Event Subscriptions below) before calling
    Connect()
  3. await store.Connect()
    to connect to the platform store
  4. On
    OnStoreConnected
    , call
    store.FetchProducts(List<ProductDefinition>)
    to load the catalog
  5. On
    OnProductsFetched
    , products are ready for display and purchase
Use
Awake()
for initialization — ensures IAP is ready before other
Start()
methods.
Product types:
ProductType.Consumable
,
ProductType.NonConsumable
,
ProductType.Subscription
.
  1. 通过
    UnityIAPServices.StoreController()
    获取
    StoreController
    (或通过
    DefaultStore()
    DefaultProduct()
    DefaultPurchase()
    获取独立服务)
  2. 在调用
    Connect()
    之前,订阅所有必需的事件(见下方“必需事件订阅”)
  3. 调用
    await store.Connect()
    连接至平台商店
  4. OnStoreConnected
    触发时,调用
    store.FetchProducts(List<ProductDefinition>)
    加载产品目录
  5. OnProductsFetched
    触发时,产品即可用于展示和购买
使用
Awake()
进行初始化——确保IAP在其他
Start()
方法执行前准备就绪。
产品类型:
ProductType.Consumable
ProductType.NonConsumable
ProductType.Subscription

Fetching Products

获取产品

Define products as
List<ProductDefinition>
and pass to
store.FetchProducts()
. Use
StoreSpecificIds
when product IDs differ across Apple/Google stores. For complex catalogs, use
CatalogProvider
to manage product sets and store-specific IDs.
MethodBehavior
GetProducts()
Returns the cached product list (synchronous, stale if
FetchProducts
not called)
FetchProducts()
Queries the store for fresh pricing/availability and updates the cache
GetProductById(id)
Returns a single cached product by ID
These are NOT interchangeable. Always call
FetchProducts()
first before relying on
GetProducts()
.
将产品定义为
List<ProductDefinition>
并传入
store.FetchProducts()
。当产品ID在Apple/Google商店中不同时,使用
StoreSpecificIds
。对于复杂目录,使用
CatalogProvider
管理产品集和商店特定ID。
方法行为
GetProducts()
返回缓存的产品列表(同步操作,若未调用
FetchProducts
则数据可能过时)
FetchProducts()
商店查询最新定价/可用性并更新缓存
GetProductById(id)
通过ID返回单个缓存产品
这些方法不可互换。在依赖
GetProducts()
之前,务必先调用
FetchProducts()

Two-Step Purchase Flow

两步购买流程

IAP v5 uses a mandatory two-step flow: Pending → Confirm.
  1. store.PurchaseProduct(product)
    — initiates the platform purchase dialog
  2. OnPurchasePending
    fires — you receive a
    PendingOrder
  3. Validate the receipt, grant content to the player
  4. store.ConfirmPurchase(pendingOrder)
    — finalizes the transaction
  5. OnPurchaseConfirmed
    fires — receives
    Order
    base type; pattern-match
    ConfirmedOrder
    (success) vs
    FailedOrder
    (confirmation failed)
You MUST call
ConfirmPurchase(pendingOrder)
after granting content.
Unconfirmed purchases are re-delivered on next app launch to prevent lost purchases.
De-duplication:
OnPurchasePending
may fire multiple times for the same purchase (e.g., app restart before confirmation). Always check if content was already granted.
Consumables: Confirmed consumable purchases are NOT returned by
FetchPurchases
. Track consumable grants yourself (e.g., in Cloud Save or Economy).
Deferred purchases:
OnPurchaseDeferred
fires for Ask-to-Buy (iOS) and Google Play deferred purchases. Do NOT grant content — wait for
OnPurchasePending
when approved.
IAP v5使用强制的两步流程:待处理 → 确认
  1. store.PurchaseProduct(product)
    —— 启动平台购买对话框
  2. OnPurchasePending
    触发 —— 你会收到一个
    PendingOrder
  3. 验证收据,向玩家发放内容
  4. store.ConfirmPurchase(pendingOrder)
    —— 完成交易
  5. OnPurchaseConfirmed
    触发 —— 接收
    Order
    基类;通过模式匹配区分
    ConfirmedOrder
    (成功)与
    FailedOrder
    (确认失败)
在发放内容后必须调用
ConfirmPurchase(pendingOrder)
。未确认的购买会在下次应用启动时重新推送,以避免购买丢失。
去重处理:
OnPurchasePending
可能针对同一购买多次触发(例如,确认前重启应用)。务必检查内容是否已发放。
消耗型产品: 已确认的消耗型购买不会通过
FetchPurchases
返回。请自行跟踪消耗型产品的发放记录(例如,在Cloud Save或Economy中)。
延迟购买:
OnPurchaseDeferred
会在iOS的Ask-to-Buy和Google Play延迟购买时触发。此时请勿发放内容——等待批准后
OnPurchasePending
触发时再处理。

Restore Transactions

恢复交易

store.RestoreTransactions(callback)
re-delivers non-consumable and subscription purchases. Each restored purchase triggers
OnPurchasePending
.
Required on iOS for Apple App Store compliance — add a "Restore Purchases" button.
Apple non-renewable subscriptions cannot be restored via
RestoreTransactions
. Track these server-side.
store.RestoreTransactions(callback)
会重新推送非消耗型和订阅型购买。每笔恢复的购买都会触发
OnPurchasePending
根据Apple App Store合规要求,iOS平台必须添加“恢复购买”按钮。
Apple非续订订阅无法通过
RestoreTransactions
恢复
。请在服务器端跟踪这些订阅。

Receipt Validation

收据验证

PlatformApproach
Google Play
CrossPlatformValidator
with
GooglePlayTangle.Data()
— local validation supported
Apple (StoreKit 2)Local validation is a no-op. Use
order.Info.Apple?.jwsRepresentation
for server-side validation
Generate tangle data via Services > In-App Purchasing > Receipt Validation Obfuscator in the Unity Editor.
平台实现方式
Google Play使用
CrossPlatformValidator
搭配
GooglePlayTangle.Data()
—— 支持本地验证
Apple(StoreKit 2)本地验证无效。使用
order.Info.Apple?.jwsRepresentation
进行服务器端验证
在Unity编辑器中,通过Services > In-App Purchasing > Receipt Validation Obfuscator生成tangle数据。

Entitlement Checking

权限检查

Use when you don't have the
Order
and want to know the status of a specific product (replaces v4's
product.hasReceipt
). If you already have the
Order
, check its type instead:
PendingOrder
maps to
EntitledUntilConsumed
(consumables) or
EntitledButNotFinished
(non-consumables/subscriptions),
ConfirmedOrder
maps to
FullyEntitled
.
Call
store.CheckEntitlement(product)
and handle
store.OnCheckEntitlement
. Check
entitlement.Status == EntitlementStatus.FullyEntitled
.
EntitlementStatus
values:
FullyEntitled
,
EntitledUntilConsumed
,
EntitledButNotFinished
,
NotEntitled
,
Unknown
.
当你没有
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

Fetch Existing Purchases

获取现有购买记录

store.FetchPurchases()
retrieves all current purchases from the store. Useful at app startup.
MethodBehavior
GetPurchases()
Returns the cached purchase list
FetchPurchases()
Queries the store for current purchases and overwrites the cached list
FetchPurchases()
replaces the entire cached list on each call. Only non-consumables and subscriptions are re-fetched — confirmed consumables are not returned (see Two-Step Purchase Flow above).
store.FetchPurchases()
从商店获取所有当前购买记录。适用于应用启动时。
方法行为
GetPurchases()
返回缓存的购买记录列表
FetchPurchases()
商店查询当前购买记录并覆盖缓存列表
FetchPurchases()
每次调用都会替换整个缓存列表。仅会重新获取非消耗型和订阅型购买——已确认的消耗型购买不会返回(见上方“两步购买流程”)。

Subscription Info

订阅信息

Subscription info is on
IPurchasedProductInfo
, accessed via
order.Info.PurchasedProductInfo
NOT on
CartItem
(
CartItem
only has
Product
and
Quantity
).
IsSubscribed()
returns
Result
enum (
True
/
False
/
Unsupported
), NOT
bool
. Use
== Result.True
for null-safe comparison.
订阅信息位于
IPurchasedProductInfo
,可通过
order.Info.PurchasedProductInfo
访问——不在
CartItem
CartItem
仅包含
Product
Quantity
)。
IsSubscribed()
返回
Result
枚举(
True
/
False
/
Unsupported
),而非
bool
。使用
== Result.True
进行空安全比较。

Required Event Subscriptions

必需事件订阅

Always subscribe to BOTH success and failure events. Not subscribing to failure events generates runtime warnings.
CallSuccess EventFailure Event (REQUIRED)
FetchProducts()
OnProductsFetched
OnProductsFetchFailed
FetchPurchases()
OnPurchasesFetched
OnPurchasesFetchFailed
Connect()
OnStoreConnected
OnStoreDisconnected
PurchaseProduct()
OnPurchasePending
OnPurchaseFailed
CheckEntitlement()
OnCheckEntitlement
Always subscribe to
OnPurchaseDeferred
— fires for Ask-to-Buy (iOS) and Google Play deferred purchases. Not subscribing silently drops deferred purchases.
Subscribe to events BEFORE calling
Connect()
— pending purchases from a previous session may fire immediately.
务必同时订阅成功和失败事件。未订阅失败事件会生成运行时警告。
调用成功事件失败事件(必需)
FetchProducts()
OnProductsFetched
OnProductsFetchFailed
FetchPurchases()
OnPurchasesFetched
OnPurchasesFetchFailed
Connect()
OnStoreConnected
OnStoreDisconnected
PurchaseProduct()
OnPurchasePending
OnPurchaseFailed
CheckEntitlement()
OnCheckEntitlement
务必订阅
OnPurchaseDeferred
——在iOS的Ask-to-Buy和Google Play延迟购买时触发。未订阅此事件会导致延迟购买被静默丢弃。
在调用
Connect()
之前订阅事件——上一会话的待处理购买可能会立即触发。

Failure Description Property Names

失败描述属性名称

These property names are NOT interchangeable — using the wrong one causes CS1061:
TypeFieldPropertyNOT
StoreConnectionFailureDescription
.message
.Message
.reason
ProductFetchFailed
.FailureReason
,
.FailedFetchProducts
.Message
FailedOrder
.FailureReason
,
.Details
PurchasesFetchFailureDescription
.message
,
.failureReason
.Message
,
.FailureReason
这些属性名称不可互换——使用错误名称会导致CS1061错误:
类型字段属性请勿使用
StoreConnectionFailureDescription
.message
.Message
.reason
ProductFetchFailed
.FailureReason
,
.FailedFetchProducts
.Message
FailedOrder
.FailureReason
,
.Details
PurchasesFetchFailureDescription
.message
,
.failureReason
.Message
,
.FailureReason

Validation

验证

After writing code that uses this package:
  1. Verify the project compiles without errors.
  2. Confirm all API calls match the v5 signatures in api-notes.md — do NOT use v4 legacy patterns (
    IStoreListener
    ,
    UnityPurchasing.Initialize
    ,
    ConfigurationBuilder
    ).
  3. Check the "Anti-Hallucination: Common v5 Mistakes" table in api-notes.md — do NOT use
    OnStoreConnectionFailed
    (use
    OnStoreDisconnected
    ), do NOT pass callbacks to
    FetchProducts
    /
    FetchPurchases
    (use events), do NOT use
    product.receipt
    (use
    order.Info.Receipt
    ).
  4. Check that all required events are subscribed before calling
    Connect()
    (see Required Event Subscriptions table above).
  5. Verify the two-step purchase flow:
    OnPurchasePending
    → grant content →
    ConfirmPurchase(pendingOrder)
    .
  6. Confirm both success and failure events are subscribed for every async operation (
    OnProductsFetched
    /
    OnProductsFetchFailed
    ,
    OnStoreConnected
    /
    OnStoreDisconnected
    , etc.).
  7. If handling subscriptions, verify
    IsSubscribed()
    is compared with
    == Result.True
    , not cast to
    bool
    .
  8. If updated files coexist with legacy versions in the same project, use a unique namespace (e.g., add
    .Updated
    suffix) to avoid CS0101/CS0111 compilation errors.
  9. If the project subscribes to
    OnAuthAccountChanged
    (v5.4+): verify the handler re-fetches products and purchases from scratch — Unity IAP clears both caches before raising this event. Do not read
    GetProducts()
    or
    GetPurchases()
    inside the handler.
在编写使用此包的代码后:
  1. 验证项目编译无错误。
  2. 确认所有API调用与api-notes.md中的v5签名匹配——请勿使用v4遗留模式(
    IStoreListener
    UnityPurchasing.Initialize
    ConfigurationBuilder
    )。
  3. 查看api-notes.md中的“防误解:常见v5错误”表格——请勿使用
    OnStoreConnectionFailed
    (使用
    OnStoreDisconnected
    ),请勿向
    FetchProducts
    /
    FetchPurchases
    传递回调(使用事件),请勿使用
    product.receipt
    (使用
    order.Info.Receipt
    )。
  4. 确认所有必需事件在调用
    Connect()
    之前已订阅(见上方“必需事件订阅”表格)。
  5. 验证两步购买流程:
    OnPurchasePending
    → 发放内容 →
    ConfirmPurchase(pendingOrder)
  6. 确认每个异步操作都同时订阅了成功和失败事件(
    OnProductsFetched
    /
    OnProductsFetchFailed
    OnStoreConnected
    /
    OnStoreDisconnected
    等)。
  7. 如果处理订阅,请验证
    IsSubscribed()
    是与
    == Result.True
    比较,而非转换为
    bool
  8. 如果项目中更新后的文件与遗留版本共存,请使用唯一命名空间(例如添加
    .Updated
    后缀)以避免CS0101/CS0111编译错误。
  9. 如果项目订阅了
    OnAuthAccountChanged
    (v5.4+):验证处理程序会重新从头获取产品和购买记录——Unity IAP在触发此事件前会清除两者的缓存。请勿在处理程序中读取
    GetProducts()
    GetPurchases()