How to implement IAPs in Unity
This guide shows you how to implement IAPs using Hiro’s UnityPurchasingSystem, from economy configuration and Nakama credentials through to purchase handling and testing.
Why use Hiro for IAP in Unity #
UnityPurchasingSystem builds on Unity’s purchasing package and takes care of the parts of a purchase implementation your game code would otherwise have to manage:
| Product registration | Reads your SKU-based store items from the Hiro economy config and registers them with Unity Purchasing automatically. |
| Platform store handling | Routes purchases to the correct store (Apple App Store or Google Play) based on the player's platform. |
| Receipt validation | Submits the receipt to Nakama for server-side validation after every platform purchase. Handles Unity IAP v4 and v5 API differences, and Apple StoreKit 2 validation. |
| Localized pricing | Exposes real store prices in the player's locale and currency, populated directly from the platform store. |
Before you begin #
You’ll need:
- Unity Purchasing Package installed in your Unity editor, either v4 or v5.
- Hiro Unity SDK installed. See Getting started. If you are using Unity’s IAP v5 package, you will also need the Hiro custom adapator for Unity IAP v5, available upon request.
In this guide #
- Create products on platform stores
- Configure IAP validation on Nakama
- Configure store items in Hiro economy
- Initialize UnityPurchasingSystem
- Purchase a store item
- Test your integration
Create products on platform stores #
Each in-app product must be created in the platform store before it can be purchased. If you’re shipping on both platforms, use the same product ID string in Apple App Store and Google Play.
Configure IAP validation on Nakama #
Nakama validates purchase receipts against the platform stores server-side before granting rewards. Configure your Nakama server with the credentials it needs to contact each store’s validation API before shipping any IAP items to production.
sku but Nakama cannot validate receipts, the purchase fails server-side after the player has already been charged by the platform store.Configure store items in Hiro economy #
Add a sku to any store item that requires a real-money purchase. The value must exactly match the product ID in App Store Connect or Google Play Console. See Virtual Store for more store configuration details.
Initialize UnityPurchasingSystem #
UnityPurchasingSystem depends on EconomySystem and must be added to your system list after it.
Pass the store type into EconomySystem using UnityPurchasingSystem.GetStoreType. This maps RuntimePlatform.Android to GooglePlay and falls back to AppleAppstore for all other platforms. Then add UnityPurchasingSystem after it.
| |
UnityPurchasingSystem reads all SKU-based store items during InitializeAsync and registers them with Unity Purchasing. You don’t call UnityPurchasing.Initialize yourself. It also populates storeItem.Cost.LocalizedProductPrice with real prices from the platform store.Purchase a store item #
You can purchase a store item defined in the Economy system.
| |
You can also purchase an item by product ID. Use the store item ID. For example, if your config defines "store_items": { "gem_pack_500": { ... } }, pass "gem_pack_500".
| |
BuyProductAsync triggers the platform’s native purchase sheet. Once the player confirms, Unity Purchasing delivers the receipt to UnityPurchasingSystem internally. The system then calls EconomySystem.PurchaseStoreItemAsync with the receipt, waits for Nakama to validate and grant the reward, and only then confirms the purchase with the platform store. This ordering ensures the player can’t receive a reward from a failed or duplicate transaction.
Handle purchase failures #
BuyProductAsync and BuyProductByIdAsync throw PurchaseFailureException when the platform purchase fails. The exception’s Reason property maps to Unity’s PurchaseFailureReason enum.
Show localized prices #
Use GetLocalizedProductPrice to display the real store price in the player’s locale and currency. This data is populated from the platform store during initialization.
| |
You can also look up a price by product ID:
| |
You can also read the localized price directly from the store item without going through the purchasing system:
| |
Restore purchases (iOS) #
Apple App Store guidelines require non-consumable purchases to be restorable. Add a “Restore Purchases” button to your settings UI and call RestorePurchasesAsync when tapped.
On non-Apple platforms (Android, PC, etc.), RestorePurchasesAsync returns immediately without doing anything. Restored transactions re-trigger the internal purchase processing, which re-validates receipts with Nakama.
RestorePurchasesAsync only applies to non-consumable products. Consumable products (gem packs, coin bundles) aren’t restorable by Apple’s design.Test your integration #
In the Unity Editor #
Call SetAllowFakeReceipts on the economy system in your server code to enable fake receipt bypass for a given environment. With this enabled, receipts containing the string fake receipt or FakeReceipt bypass Nakama validation entirely, letting you test the purchase flow without real Apple or Google credentials.
| |
SetAllowFakeReceipts defaults to false. Enable it only for non-production environments — never in a live build.
allow_fake_receipts field in the economy config JSON is deprecated. It remains in the config schema for backwards compatibility but has no effect. Use SetAllowFakeReceipts in server code instead.On device #
Use the platform’s sandbox or test tooling:
- Apple: Sandbox testing with a Sandbox Apple ID, or TestFlight for pre-release builds
- Google Play: Internal test track with a licensed tester account
After a test purchase, check the Purchases section of your Nakama developer console to confirm the receipt reached the server and was validated successfully.
