A subscription is like a recurring membership. Instead of buying something once (like buying a hat in a game), the player pays regularly (monthly, yearly, etc.) to keep access to premium features. It automatically renews unless they cancel.
How the Purchase Flow Works
Step 1: Connecting to the Store
Step 2: Fetching Product Information
Your game asks the store: "What subscriptions do I have available, and what are their prices?" The store sends back the details - the localized prices (so it shows $9.99 in the US, £7.99 in the UK, etc.), descriptions, and titles.
Step 3: Player Initiates Purchase
Step 4: The Confirmation Process
You will notice that all these steps are same as what you would see for purchasing consumables which you can find a guide for here. The only difference is that when the intial purchase has gone through you want to check if the user has access to to the subscriptions content. You can do this in two ways.
The easiest way is to check their entitlements. You would use this when:
- Checking on app startup
- Simple gate keeping (unlock/lock features)
- You just need a yes/no answer
- You want the simplest code possible
public class SimpleSubscriptionCheck : MonoBehaviour
{
StoreController m_StoreController;
string subscriptionProductId = "com.mygame.premium_monthly";
bool isUserSubscribed = false;
void Start()
{
// Assume store is already connected and set up
CheckIfPlayerIsSubscribed();
}
// Call this whenever you want to check subscription status
void CheckIfPlayerIsSubscribed()
{
// Get the subscription product
Product subscriptionProduct = m_StoreController.GetProductById(subscriptionProductId);
if (subscriptionProduct == null)
{
Debug.LogError("Subscription product not found!");
return;
}
// Ask the store to check entitlement
m_StoreController.CheckEntitlement(subscriptionProduct);
// The answer comes back in the event below (not immediately)
}
// Subscribe to this event when you initialize your store
void OnCheckEntitlement(Entitlement entitlement)
{
// Make sure this is for our subscription product
if (entitlement.Product.definition.id == subscriptionProductId)
{
// Check the status
if (entitlement.Status == EntitlementStatus.FullyEntitled)
{
// Player IS subscribed
isUserSubscribed = true;
Debug.Log("Player is subscribed!");
UnlockPremiumFeatures();
}
else
{
// Player is NOT subscribed
isUserSubscribed = false;
Debug.Log("Player is not subscribed");
LockPremiumFeatures();
}
}
}
void UnlockPremiumFeatures()
{
// Enable premium content here
Debug.Log("Unlocking premium features...");
}
void LockPremiumFeatures()
{
// Disable premium content here
Debug.Log("Locking premium features...");
}
}
If you want more information from your subscriptions you can use the Subscription Info Object. You use this when:
- Displaying "Your subscription expires in X days"
- Showing "You're on a free trial"
- Checking if someone cancelled but still has time left
- Analytics or logging detailed subscription state
- Building a "Manage Subscription" screen
You can only get SubscriptionInfo from an Order (after a purchase or when fetching existing purchases). You can't just ask for it anytime like you can with entitlements.
using UnityEngine;
using UnityEngine.Purchasing;
using System.Linq;
public class DetailedSubscriptionCheck : MonoBehaviour
{
StoreController m_StoreController;
string subscriptionProductId = "com.mygame.premium_monthly";
// Method 1: Check subscription details after a purchase
void OnPurchaseConfirmed(Order order)
{
if (order is ConfirmedOrder confirmedOrder)
{
// Get the subscription info from the order
SubscriptionInfo subInfo = GetSubscriptionInfo(confirmedOrder);
if (subInfo != null)
{
DisplayDetailedSubscriptionInfo(subInfo);
}
}
}
// Method 2: Get subscription info from existing purchases
void CheckExistingSubscriptionDetails()
{
// First, fetch existing purchases
m_StoreController.OnPurchasesFetched += OnPurchasesFetched;
m_StoreController.FetchPurchases();
}
void OnPurchasesFetched(Orders orders)
{
// Look through confirmed orders for our subscription
foreach (var confirmedOrder in orders.ConfirmedOrders)
{
SubscriptionInfo subInfo = GetSubscriptionInfo(confirmedOrder);
if (subInfo != null && subInfo.GetProductId() == subscriptionProductId)
{
DisplayDetailedSubscriptionInfo(subInfo);
break;
}
}
}
// Helper method to extract SubscriptionInfo from an order
SubscriptionInfo GetSubscriptionInfo(ConfirmedOrder order)
{
// SubscriptionInfo is buried inside PurchasedProductInfo
var purchasedProductInfo = order.Info.PurchasedProductInfo?.FirstOrDefault();
if (purchasedProductInfo != null)
{
return purchasedProductInfo.subscriptionInfo;
}
return null;
}
// This shows all the detailed information you can get
void DisplayDetailedSubscriptionInfo(SubscriptionInfo subInfo)
{
Debug.Log("========== SUBSCRIPTION DETAILS ==========");
// Basic subscription state (these return Result enum: True/False/Unsupported)
Debug.Log($"Product ID: {subInfo.GetProductId()}");
Debug.Log($"Is Currently Subscribed: {subInfo.IsSubscribed()}");
Debug.Log($"Is Expired: {subInfo.IsExpired()}");
Debug.Log($"Is Cancelled: {subInfo.IsCancelled()}");
Debug.Log($"Is Auto-Renewing: {subInfo.IsAutoRenewing()}");
// Dates
Debug.Log($"Purchase Date: {subInfo.GetPurchaseDate()}");
Debug.Log($"Expiration Date: {subInfo.GetExpireDate()}");
if (subInfo.GetCancelDate().Year > 1970) // Check if there's a real cancel date
{
Debug.Log($"Cancellation Date: {subInfo.GetCancelDate()}");
}
// Time remaining
TimeSpan remaining = subInfo.GetRemainingTime();
Debug.Log($"Time Remaining: {remaining.Days} days, {remaining.Hours} hours");
// Free trial information
if (subInfo.IsFreeTrial() == Result.True)
{
Debug.Log("✓ This is a FREE TRIAL subscription");
TimeSpan trialPeriod = subInfo.GetFreeTrialPeriod();
Debug.Log($"Trial Period: {trialPeriod.Days} days");
}
// Introductory pricing information
if (subInfo.IsIntroductoryPricePeriod() == Result.True)
{
Debug.Log("✓ This subscription has INTRODUCTORY PRICING");
Debug.Log($"Intro Price: {subInfo.GetIntroductoryPrice()}");
TimeSpan introPeriod = subInfo.GetIntroductoryPricePeriod();
Debug.Log($"Intro Period: {introPeriod.Days} days");
long introCycles = subInfo.GetIntroductoryPricePeriodCycles();
Debug.Log($"Intro Cycles: {introCycles}");
}
// Subscription period (Google Play only, returns TimeSpan.Zero on Apple)
TimeSpan subPeriod = subInfo.GetSubscriptionPeriod();
if (subPeriod > TimeSpan.Zero)
{
Debug.Log($"Subscription Period: {subPeriod.Days} days");
}
Debug.Log("==========================================");
}
// Example: Use detailed info to show UI
void UpdateSubscriptionUI(SubscriptionInfo subInfo)
{
// Check if subscribed (comparing to Result enum, NOT bool!)
if (subInfo.IsSubscribed() == Result.True)
{
// Show time remaining
TimeSpan remaining = subInfo.GetRemainingTime();
string message = $"Subscription active: {remaining.Days} days remaining";
// Check if they cancelled but still have time
if (subInfo.IsCancelled() == Result.True)
{
message += " (will not renew)";
}
// Check if they're on a trial
if (subInfo.IsFreeTrial() == Result.True)
{
message = $"Free trial: {remaining.Days} days remaining";
}
Debug.Log(message);
// Update your UI with this message
}
else
{
Debug.Log("Subscription expired or not active");
}
}
}
Here is a handy table to help you figure out which one you want to use (if not both).
| Feature | Entitlement System | Subscription Info |
| How to access | Anytime via CheckEntitlement() | Only from an Order object |
| Information | Current access status only | All details (dates, trial, pricing) |
| Simplicity | Very simple - one question | Complex - many properties |
| Use case | "Should I unlock features?" | "Show subscription details in UI" |
| Result type | EntitlementStatus enum | Result enum (True/False/Unsupported) |
| When to use | Most of the time | When you need specifics |