IAP (In App Purchasing) - VirtueSky/sunflower_2 GitHub Wiki
Setup
1. Add IapManager to the Scene
Create a GameObject in your scene (typically the first scene or Loading scene) and attach the IapManager component to it.
IapManager Inspector fields:
| Field | Description |
|---|---|
Is Persistent |
DontDestroyOnLoad when changing scenes |
Init Type |
When IAP initialization happens (see table below) |
Skus Data |
Static product configuration list |
Products |
(ReadOnly) Products created at runtime from Skus Data |
Init Type options:
| Value | Initializes in |
|---|---|
InitOnAwake |
Awake() of IapManager |
InitOnEnable |
OnEnable() of IapManager |
InitOnStart |
Start() of IapManager |
InitManually |
Call IapManager.Initialization() manually |
2. Configure Skus Data
Add products directly to the Skus Data list on the IapManager component:
Android Id : com.yourapp.removeads
iOS Id : com.yourapp.removeads
Product Type : NonConsumable
Price Config : 2.99
At runtime, IapManager will automatically create the corresponding IapDataProduct entries in Products.
3. Configure Validate Purchase (IapSettings)
Open IapSettings (via Magic Panel or find the asset in the Project window):
- Enable
Is Validate Purchaseto validate receipts - Enter
Google Play Store Key, then clickObfuscator Key - Enable
Is Custom Validate Purchaseto use a custom validator
Accessing Products
// Get an IapDataProduct by id
IapDataProduct product = IapManager.GetIapProduct("com.yourapp.removeads");
// Or get the full list
List<IapDataProduct> allProducts = IapManager.Products;
Handling Purchases
Option 1 — Direct callback (for UI):
public void OnClickRemoveAds()
{
var productRemoveAds = IapManager.GetIapProduct("com.yourapp.removeads");
productRemoveAds.OnCompleted(() =>
{
// handle success
}).OnFailed(error =>
{
// handle failed
}).Purchase();
}
public void OnClick1000Gem()
{
var product1000Gem = IapManager.PurchaseProduct("com.yourapp.1000gem");
product1000Gem.OnCompleted(() =>
{
// handle success
}).OnFailed(error =>
{
// handle failed
});
}
Option 2 — Global events (for game logic and restore purchase):
public class HandlePurchaseIap : MonoBehaviour
{
private void Awake()
{
DontDestroyOnLoad(gameObject);
IapManager.OnPurchaseSucceedEvent += HandlePurchaseSuccess;
IapManager.OnPurchaseFailedEvent += HandlePurchaseFailed;
}
private void OnDestroy()
{
IapManager.OnPurchaseSucceedEvent -= HandlePurchaseSuccess;
IapManager.OnPurchaseFailedEvent -= HandlePurchaseFailed;
}
void HandlePurchaseSuccess(string id)
{
switch (id)
{
case "com.yourapp.removeads":
// remove ads
break;
case "com.yourapp.1000gem":
// grant gem
break;
}
}
void HandlePurchaseFailed(string id, string error)
{
Debug.LogWarning($"Purchase failed: {id} - {error}");
}
}
Note: Option 1 is suitable for temporary UI feedback after a purchase. Option 2 should be used for game logic (remove ads, unlock content, etc.) as it also handles restore purchase correctly.
Checking if a Product is Purchased (NonConsumable only)
private void OnEnable()
{
bool isPurchased = IapManager.IsPurchasedProduct("com.yourapp.removeads");
buttonRemoveAds.gameObject.SetActive(!isPurchased);
}
// Or
private void OnEnable()
{
var productRemoveAds = IapManager.GetIapProduct("com.yourapp.removeads");
bool isPurchased = productRemoveAds.IsPurchased();
buttonRemoveAds.gameObject.SetActive(!isPurchased);
}
Adding Products Dynamically from Remote Config
Use InitManually and call AddProduct before Initialization():
public class GameBootstrap : MonoBehaviour
{
private async void Start()
{
// Fetch remote config first
await RemoteConfigService.Instance.FetchAsync();
// Create and add products from remote data
string packId = RemoteConfig.GetString("special_pack_id");
float packPrice = RemoteConfig.GetFloat("special_pack_price");
var dynamicProduct = new IapDataProduct(packId, packId, IapProductType.Consumable, packPrice);
IapManager.AddProduct(dynamicProduct); // skipped silently if id already exists
// Initialize IAP after all products have been registered
IapManager.Initialization();
}
}
Important:
AddProductmust be called beforeIapManager.Initialization()runs. Once initialization has started,AddProductcalls will be ignored and a warning will be logged.
Restore Purchase (iOS)
Restore purchase only applies to NonConsumable products.
- Android: Automatically restored on reinstall via
OnInitialized. - iOS: A Restore Purchase button is required to publish on the App Store.
public void OnClickRestorePurchase()
{
#if UNITY_IOS
IapManager.RestorePurchase();
#endif
}
When restore succeeds, the purchase callback for each product fires automatically — handle game logic via Option 2 (global events) to ensure it works correctly on restore.