Integrate and Use Provider Entitlements #
To use Pragma Engine’s Provider Entitlement feature, there are several steps that need to be taken:
- Set up a third-party provider of your choice.
- Link accounts between the third-party provider and Pragma Engine.
- Map offerings in Pragma Engine’s Content Data system.
- Configure the Entitlement Provider Plugin in Pragma Engine.
- Sync entitlements.
- (optional) Access provider entitlement history.
Set up a third-party provider #
Before integrating with Pragma Engine, an account with a third-party provider of your choice is needed. We’ll be using the third-party provider to manage store catalogs. This includes setting up item descriptions, regional pricing, and creating and completing purchases. In most cases, this will also involve the use of a third-party provider’s SDK to render an in-game store.
Once a purchase has been completed, the player receives an entitlement on their account in the third-party provider’s system. Pragma Engine then handles the process of granting the entitlement to the player.
For more information on each third-party provider’s system, refer to their respective documentation:
The Provider Entitlement feature allows for integration with multiple third party providers.
Link accounts #
To allow Pragma Engine to fetch entitlements, set up account linking between the chosen third-party provider and Pragma Engine. See Identity Providers for details on how to configure each provider with Pragma Engine.
Set up configuration for fetching linked accounts #
During the SyncEntitlements
stage, the Inventory service makes a request to the Accounts service to fetch all linked provider identities for players. To allow the Game node to make this request to the Social node, a social bearer token needs to be configured.
To set up this configuration, add a SocialBackendPartnerClientConfig
with a bearerToken
that has the encrypted Social Partner token.
game:
serviceConfigs:
SocialBackendPartnerClientConfig:
bearerToken: <encrypted social partner token>
Check out the Generate Partner tokens guide if you need help generating Partner tokens.
Map offerings in Pragma Engine #
In this section, we’ll go over defining entitlement content in Pragma Engine’s Content Data system.
Collect the IDs #
To grant players the items from their entitlements, first collect the third-party provider’s generated IDs. These IDs are used by Pragma Engine to know what corresponding content to grant a player when an entitlement is consumed.
In some cases, third-party providers may have different locations for their generated IDs. As shown in the tabs below, Steam has different locations for their Steam Inventory and Steam App IDs.
- Log into the Epic Games Developer Portal.
- Navigate to your product’s Epic Games Store dashboard.
- Go to Offers and click on an offer.
- Under the right panel Offer Details, collect the
Audience Item IDs
.
- Note the offer type because that will be used when creating provider entitlement content in Pragma Engine. Pragma Engine currently supports the offer types:
CONSUMABLE
,ADDON
,EDITION
,DEMO
,DIGITALEXTRA
,SEASONPASS
, andGAME
.
To find the generated IDs for items such as cosmetics in Steam’s Inventory Service:
- Log into Steamworks.
- Navigate to the Steamworks Admin page for your game.
- Hover over the Community tab, click Inventory Service.
- Under the Item Definitions section, click
Edit Raw Definition and collect the
itemdefid
.
To find Steam App Ids:
- Log into Steamworks.
- Navigate to the Steamworks Admin page for your game.
- Go to Apps and Packages.
- Click on View All Associated Items.
- Under the All DLC section, grab the values under the # column. These are the
AppIds
.
- Log into the Twitch Developer Portal.
- Navigate to Drops Campaigns.
- Go to Reward Manager and collect the Reward IDs.
Create provider entitlement content #
Provider entitlement content is defined within content/src/ProviderEntitlements.json
. The structure of provider details is specific to each provider.
While mapping entitlements in Pragma Engine, keep the following in mind:
A third-party provider’s generated ID can only be mapped to a single data type in Pragma Engine. For instance, to map both a stackable and an instanced item, an item bundle must be used.
If you are mapping a stackable item, an amount greater than 0 must be assigned.
Creating provider entitlement content for each supported third-party provider:
- Create a content entry in
ProviderEntitlements.json
.id
must be a unique string for each entry.
- Assign a grant type to the content entry: instanced item, stackable item, or item bundle.
- Add Epic as a provider under
providerDetails
.- Create an
epicEntitlement
object. - Add the collected
audienceItemId
from Epic Games Store. - Define the Epic offer type. Pragma Engine supports the types:
CONSUMABLE
,ADDON
,EDITION
,DEMO
,DIGITALEXTRA
,SEASONPASS
, andGAME
.
- Create an
// example ProviderEntitlement.json content object for Epic
{
"id": "60-elemental-orbs",
"stackable": {
"catalogId": "elemental_orbs",
"amount": 60
},
"providerDetails": [
{
"epicEntitlement": {
"audienceItemId": "<audienceItemId>",
"offerType": "<offer type>"
}
}
]
}
- Create a content entry in
ProviderEntitlements.json
.id
must be a unique string for each entry.
- Assign a grant type to the content entry: instanced item, stackable item, or item bundle.
- Add Steam as a provider under
providerDetails
.- Create a
steamEntitlement
object. - Add the collected
itemDefId
and/orappId
.
- Create a
// example ProviderEntitlement content object for Steam
{
"id": "60-elemental-orbs",
"stackable": {
"catalogId": "elemental_orbs",
"amount": 60
},
"providerDetails": [
{
"steamEntitlement": {
"inventoryItem": {
"itemDefId": "<itemDefId>"
}
}
},
{
"steamEntitlement": {
"app": {
"appId": "<appId>"
}
}
}
]
}
- Create a content entry in
ProviderEntitlements.json
.id
must be a unique string for each entry.
- Assign a grant type to the content entry: instanced item, stackable item, or item bundle.
- Add Twitch as a provider under
providerDetails
.- Create a
twitchDrop
object. - Add the collected
rewardId
.
- Create a
// example ProviderEntitlement content object for Twitch
{
"id": "60-elemental-orbs",
"stackable": {
"catalogId": "elemental_orbs",
"amount": 60
},
"providerDetails": [
{
"twitchDrop": {
"rewardId": "<rewardId>"
}
}
]
}
Apply content data #
In order to register the content you just defined with Pragma Engine, you must apply your content data changes. You may apply content data either using the command line with make
or via an IntelliJ run configuration.
If the JSON is not valid, Pragma Engine returns an error message and the content will not be applied.
Configure the Entitlement Provider Plugin #
The following are supported preimplemented plugins for third-party providers:
Provider | Pragma Engine Plugin |
---|---|
Epic Games Store | pragma.inventory.EpicEntitlementProviderPlugin |
Steam | pragma.inventory.SteamEntitlementProviderPlugin |
Twitch | pragma.inventory.TwitchEntitlementProviderPlugin |
For third-party providers not listed, studios can implement a custom Entitlement Provider Plugin. See the Create a Custom Entitlement Provider Plugin section for more information.
To enable integration with your chosen third-party providers, configure the appropriate plugin in your Pragma Engine config:
- In Pragma Engine, open
local-dev.yml
. - Add Epic as an Entitlement Provider under
InventoryService.entitlementProviderPlugins
.# local-dev.yml game: pluginConfigs: InventoryService.entitlementProviderPlugins: plugins: Epic: class: "pragma.inventory.EpicEntitlementProviderPlugin" config: sandboxId: "<sandbox>" deploymentId: "<deploymentId>" clientId: "<clientId>" clientSecret: "<encryptedClientSecret>"
- Update
sandbox
,deploymentId
,clientId
, andclientSecret
inlocal-dev.yml
. To find these values, log into the Epic Games Developer Portal:- Select the desired product.
- Click Product Settings. If the SDK Downloads & Credentials tab is not active, click it.
- In Pragma Engine, open
local-dev.yml
. - Add Steam as an Entitlement Provider under
InventoryService.entitlementProviderPlugins
.# local-dev.yml game: pluginConfigs: InventoryService.entitlementProviderPlugins: plugins: Steam: class: "pragma.inventory.SteamEntitlementProviderPlugin" config: appConfig: appId: "<appId>" steamWebAPIKey: "<steamWebAPIKey>"
- Update
appId
andsteamWebAPIKey
inlocal-dev.yml
. To find these values, log into Steamworks:- Navigate to Users & Permissions and click Manage Groups.
- Select the group for the application you want.
- Get the Web API key.
- In Pragma Engine, open
local-dev.yml
. - Add Twitch as an Entitlement Provider under
InventoryService.entitlementProviderPlugins
.# local-dev.yml game: pluginConfigs: InventoryService.entitlementProviderPlugins: plugins: Twitch: class: "pragma.inventory.TwitchEntitlementProviderPlugin" config: clientId: "<clientId>" clientSecret: "<encryptedClientSecret>" gameId: "<gameId>"
- Update
clientId
,clientSecret
, andgameId
inlocal-dev.yml
. To find these values, log into the Twitch Developer Portal:clientId
:- Navigate to Drops Campaigns.
- Go to Applications and click Manage.
- Under the section ClientID get the client ID.
clientSecret
- Use the Client Secret key associated to your application.
- Navigate to Drops Campaigns.
- Go to Applications and click Manage.
- Click on New Secret.
gameId
- Navigate to Drops Campaigns.
- Click on Edit Details.
- Hover over Game ID to get the game ID.
Pragma Engine allows for simultaneous integration with multiple third-party providers.
Create a custom Entitlement Provider Plugin #
Pragma Engine offers the flexibility to create custom Entitlement Provider Plugins to integrate with any third-party provider.
Below is a plugin interface to implement a custom Entitlement Provider Plugin. Refer to the inline comments for detailed instruction:
data class Entitlement(val id: String, val thirdPartyItemId: String)
/**
* Plugin interface used to manage entitlement fulfillment with a third party provider.
*/
interface EntitlementProviderPlugin: PragmaServicePlugin {
/**
* A unique ID for the provider this plugin is implemented for.
* Used when mapping entitlements returned from [fetchPlayerEntitlements] to
* content configured in `ThirdPartyEntitlementMappings.json`
*
* @returns a string representing an ID of the provider, ex: "EPIC", "TWITCH", etc.
*/
fun entitlementProviderId(): String
/**
* Used by the Inventory Service to obtain a full view of the player's entitlements in
* the third party provider. These results will be used to determine if any
* Pragma content should be granted to the player's inventory.
*
* Note: Unconsumed entitlements that are returned from this method will be passed back
* to [markEntitlementsConsumed] once they have been marked for fulfillment internally
* in Pragma.
*
* @returns all active basic entitlements and all unconsumed entitlements.
*/
suspend fun fetchPlayerEntitlements(
playerId: PlayerId,
idProviderAccounts: List<IdProviderAccount>
): List<Entitlement>
/**
* This method will be invoked with a list of entitlements previously returned by
* [fetchPlayerEntitlements] that had been successfully marked for fulfillment
* internally in Pragma.
*/
suspend fun markEntitlementsConsumed(
playerId: PlayerId,
idProviderAccounts: List<IdProviderAccount>, entitlements: List<Entitlement>
)
/**
* if the [ProviderEntitlement] has [ProviderDetails] for this plugin's provider,
* return the thirdPartyItemIds from those details
*/
fun getThirdPartyItemIdsForProvider(providerEntitlement: ProviderEntitlement):
List<String> = listOf()
}
Sync entitlements #
Use Pragma Engine’s InventoryRpc.SyncEntitlementsV1
endpoint to give players the corresponding content listed in their entitlements. This call can be made any time we want to check when a player’s account has been given an entitlement, such as on login or purchase completion.
The following is the information returned on entitlements:
FulfilledEntitlements
are entitlements that have been granted to a player and are now marked as fulfilled by Pragma Engine.UnfulfilledEntitlements
are entitlements that failed to be granted to a player. This can be due to a number of issues; there are available server logs for more details.
// On InventoryService
void SyncEntitlements(FOnCompleteSyncEntitlementsDelegate Delegate);
DECLARE_DELEGATE_OneParam(
FOnCompleteSyncEntitlementsDelegate,
TPragmaResult<FPragmaSyncEntitlementsResults>
);
struct FPragmaSyncEntitlementsResults
{
TArray<FPragma_Inventory_ProviderEntitlementV1> FulfilledEntitlements;
TArray<FPragma_Inventory_ProviderEntitlementV1> UnfulfilledEntitlements;
};
// On InventoryService
public Future<SyncEntitlementsResults> SyncEntitlements()
public void SyncEntitlements(SyncEntitlementsDelegate completeDelegate)
public delegate void SyncEntitlementsDelegate(Result<SyncEntitlementsResults> result);
public class SyncEntitlementsResults
{
public IReadOnlyList<ProviderEntitlementV1> FulfilledEntitlements { get; }
public IReadOnlyList<ProviderEntitlementV1> UnfulfilledEntitlements { get; }
}
message SyncEntitlementsV1Request {
option (pragma_session_type) = PLAYER;
option (pragma_message_type) = REQUEST;
}
/*
* Returns lists of fulfilled and unfulfilled entitlements by their provider and provider
* item ID. Details of the items granted can be looked up in the
* ProviderEntitlementMappings content file. Subsequent calls will attempt to fulfill
* any unfulfilled entitlements.
*/
message SyncEntitlementsV1Response {
option (pragma_session_type) = PLAYER;
option (pragma_message_type) = RESPONSE;
// List of Provider and Item ID for all entitlements that were fulfilled by this request.
repeated ProviderEntitlementV1 fulfilled = 1;
// List of Provider and Item ID for all entitlements that were available
// but unable to be fulfilled
repeated ProviderEntitlementV1 unfulfilled = 2;
}
/*
* Represents an entitlement from a specific provider.
*/
message ProviderEntitlementV1 {
// The identifier of the provider granting the entitlement.
string provider_id = 1;
// The provider's identifier of the entitlement,
// mapped to Pragma Inventory content in ProviderEntitlementMappings.
string provider_item_id = 2;
}
Access provider entitlement history #
Pragma Engine tracks the processing of player entitlements for player support. There are Operator and Partner endpoints that can be used to access this data.
The GetProviderEntitlementHistory
endpoint returns a ProviderEntitlementRecord
for each entitlement Pragma Engine has recieved from a configured Provider Entitlement Plugin.
This record includes the following information:
parameter | description |
---|---|
player_id | player ID from request |
fulfillment_id | used in the database layer to make sure entitlements are only granted once |
provider_id | provider ID assigned in the Provider Entitlement Plugin which identifies the provider for this entitlement |
provider_entitlement_id | content entry ID which identifies the item grants for this entitlements |
provider_item_id | provider ID used by the third-party provider to identify the purchased entity |
provider_entitlement_record_status | processing status–either pending or completed |
fulfilled_timestamp_millis | time in milliseconds it took to fulfill the item grant |
The GetProviderEntitlementHistory
endpoint returns a ProviderEntitlementRecord
for each entitlement Pragma Engine has recieved from a configured Provider Entitlement Plugin. This record includes information like the status (pending
or completed
) and the time in milliseconds it took to fulfill the entitlement.
// in PragmaInventoryPartnerServiceRaw
virtual void GetProviderEntitlementHistoryPartnerV1(
const FPragma_Inventory_GetProviderEntitlementHistoryPartnerV1Request& Request,
FGetProviderEntitlementHistoryPartnerV1Delegate Delegate
) const;
virtual void GetProviderEntitlementHistoryPartnerV1(
const FPragma_Inventory_GetProviderEntitlementHistoryPartnerV1Request& Request,
TUniqueFunction<
void(TPragmaResult<FPragma_Inventory_GetProviderEntitlementHistoryPartnerV1Response>,
const FPragmaMessageMetadata&)> Callback
) const;
};
// In InventoryServiceRaw.cs
public virtual void GetProviderEntitlementHistoryPartnerV1(
GetProviderEntitlementHistoryPartnerV1Request request,
Protocol.OnComplete<GetProviderEntitlementHistoryPartnerV1Response> callback)
{
Connection.Game.SendMessage(request, callback);
}
public RpcFuture<GetProviderEntitlementHistoryPartnerV1Response>
GetProviderEntitlementHistoryPartnerV1(
GetProviderEntitlementHistoryPartnerV1Request request)
{
return Connection.Game
.SendMessage<GetProviderEntitlementHistoryPartnerV1Request,
GetProviderEntitlementHistoryPartnerV1Response>(request);
}
Partner:
Request
message GetProviderEntitlementHistoryPartnerV1Request {
option (pragma_session_type) = PARTNER;
option (pragma_message_type) = REQUEST;
// The playerId to return records for
Fixed128 player_id = 1;
// The index of the page to view for pagination.
int32 page_index = 2;
// The size of the page to view for pagination, equivalent to the entry count of historical purchases.
int32 page_size = 3;
}
Response
message GetProviderEntitlementHistoryPartnerV1Response {
option (pragma_session_type) = PARTNER;
option (pragma_message_type) = RESPONSE;
// list of player's entitlement history records
repeated ProviderEntitlementRecord provider_entitlement_records = 1;
}
Operator:
Request
message GetProviderEntitlementHistoryOperatorV1Request {
option (pragma_session_type) = OPERATOR;
option (pragma_message_type) = REQUEST;
// The playerId to return records for
Fixed128 player_id = 1;
// The index of the page to view for pagination.
int32 page_index = 2;
// The size of the page to view for pagination, equivalent to the entry count of historical purchases.
int32 page_size = 3;
}
Response
message GetProviderEntitlementHistoryOperatorV1Response {
option (pragma_session_type) = OPERATOR;
option (pragma_message_type) = RESPONSE;
// list of player's entitlement history records
repeated ProviderEntitlementRecord provider_entitlement_records = 1;
}