Appearance
Appearance
Options.*.yaml
files). If you’re not familiar with them, have a look at Working with Runtime Options.You can use Subscriptions to provide players with continued access to special in-game content, like levels or special features, and even recurring premium Products like weekly rewards or item boxes. It can also mean extra bounty when the player completes a task such as leveling up or finishing a mission. In addition, using store-supported Subscriptions has some benefits over implementing an arbitrary Subscription mechanism. For example, the renewal and billing of Auto-Renewing Subscriptions are handled automatically by the In-App Purchase store without requiring repeated in-game Purchases. Additionally, the store may take a smaller cut from the revenue when a user continues the Subscription for a long time.
The Metaplay SDK supports Subscriptions with a server-side check for Subscription renewal. The initial purchase of the Subscription is made with the typical Purchase mechanism, and subsequently, the server takes care of checking for renewals. The state of the Subscription is available in PlayerModel
.
You’ll need to have at least one Subscription Product configured in Google Play and/or Apple App Store. See Play Console’s developer documentation for Google Play or Apple’s developer documentation for the Apple App Store.
InAppProducts
Config Sheet Let’s add the Subscription Product to your game’s InAppProducts
game config sheet.
You’ll also need to add the Type
column if the sheet doesn’t already have it. The cells in this column should specify either Consumable
, NonConsumable
, or Subscription
. Consumable
is the default when the column is omitted.
ProductId #key | Name | Type | Price | NumGems | DevelopmentId | GoogleId | AppleId |
---|---|---|---|---|---|---|---|
VipSubscription | VIP subscription | Subscription | 9.99 | dev.vip_subscription | com.examplecompany.examplegame.vip_subscription | com.examplecompany.examplegame.VipSubscription |
Note that game-specific reward columns such as NumGems
in this example should probably be left empty, as Subscriptions are not about providing access to a one-time reward.
The game server needs to be able to query the state of a player’s Subscriptions from Google Play’s and App Store’s servers. Therefore, some configuration is required in order to enable that.
To query a Subscription’s state from Google Play, the game server needs a service account with the correct permissions.
Please follow Google Play’s developer API guide on setting up the service account. Make sure you give the service account the following permissions:
💡 Note
It can take as long as 24 hours until the account permissions take effect. Some modifications to your In-App Products in Google Play can shorten this delay; see this Stack Overflow thread, for example.
Upon creating the service account, you should get a .json
file with the account’s credentials. Let’s call it android-publisher-api-service-account.json
. Put this file in a directory accessible by the game server, for example, Backend/Server/Secrets/
. Update the relevant Options.*.yaml
file accordingly:
# Options.prod.yaml (or some other environment)
GooglePlayStore:
# ... pre-existing values omitted from this snippet ...
EnableAndroidPublisherApi: true
AndroidPublisherApiServiceAccountPath: ./Secrets/android-publisher-api-service-account.json
🔒 Storing secrets securely
To avoid storing secrets within the code repository, you can put them in AWS Secrets Manager instead. For example, setting AndroidPublisherApiServiceAccountPath
to aws-sm://us-east-1#android_publisher_api_service_account
would denote credentials in Secrets Manager on us-east-1
with name android_publisher_api_service_account
.
To query a Subscription’s state from App Store, the game server needs the application’s “shared secret.”
If your application doesn’t already have a shared secret, please follow Apple’s developer documentation on generating it. It can be either a primary shared secret or an app-specific shared secret.
The shared secret is a hexadecimal string. Once you have the secret, update the server’s Options.*.yaml
file accordingly:
# Options.prod.yaml (or some other environment)
AppleStore:
# ... pre-existing values omitted from this snippet ...
AppleSharedSecret: 0123456789abcdef0123456789abcdef # Example value - put your secret here
Once a player has purchased a Subscription, its state is available in PlayerModel.IAPSubscriptions.Subscriptions
. You can use this to check whether the player has purchased a Subscription and whether the Subscription is still active. You should do this in in-game logic (such as in a PlayerAction
) to determine whether a player is allowed to use the game content associated with the Subscription, and in the game UI, to show the player’s subscription status.
If your game has, for example, a battle pass style system, you can offer additional rewards such as skins, items, or gems for players who bought a Subscription.
[ModelAction(ActionCodes.PlayerClaimMissionRewards)]
public class PlayerClaimMissionRewards : PlayerAction
{
public override MetaActionResult Execute(PlayerModel player, bool commit)
{
MissionState mission = player.CurrentMission;
... check that the mission has been completed ...
... check that the mission's rewards haven't been consumed yet ...
if (commit)
{
// Give the free reward regardless of the subscription.
mission.FreeReward.Consume(player, source: null);
InAppProductId subscriptionId = mission.PremiumSubscriptionProductId;
if (player.IAPSubscriptions.Subscriptions.TryGetValue(
subscriptionId,
out SubscriptionModel subscription)
&& player.CurrentTime < subscription.GetExpirationTime())
{
// Subscription is active.
// Grant the premium reward.
mission.PremiumReward.Consume(player, source: null);
}
// Mark the rewards as consumed.
mission.MarkRewardsConsumed();
}
return MetaActionResult.Success;
}
}
Similarly, you can visualize the rewards in the game UI based on whether the subscription is active.
Additionally, you can use SubscriptionModel
's method GetRenewalStatus
to check whether the Subscription is expected to renew once its expiration time is reached; in other words, whether the user has canceled or disabled auto-renewal of the Subscription.
Once a player has purchased a Subscription, you can view its state on the player page of the LiveOps Dashboard.
Expanding a subscription entry here will show more detailed information. The Individual Subscriptions show the separate Purchases of the same Subscription Product. Typically there’s just one, but there can be multiple, for example, if the player re-purchased the Subscription after it had previously expired.
Additionally, you can find the initial Purchase event among the player’s purchase history.
Restoring a Purchase means re-granting a user’s access to Products that they have already Purchased. This is relevant when a user made the Purchase on a different player account than they are currently using. Purchase restoration is needed for Subscriptions but not for consumables.
On Google platforms, restoration is initiated outside the game. On Apple platforms, however, the game should have an in-game button for initiating the restoration. See Unity’s manual on restoring transactions on how to initiate restoration using Unity IAP. After restoration has been initiated, the normal Purchase flow will take care of the rest.
If you’re using the IAPManager
class included in the Metaplay SDK, its RestoreTransactions
method initiates the restoration on Apple platforms.
The current implementation of Subscriptions in Metaplay has some limitations that are good to be aware of: