Skip to main content

Third-party Platform Purchase

1. Overview

The third-party platform purchase integration allows developers to synchronize items purchased by players from platforms (e.g. Steam, EpicGames) to PGOS. To make this possible, it is important to accurately map items sold on the third-party platform to corresponding items in PGOS. After purchasing on the third-party platform, the developer must actively call the PGOS synchronization interface to ensure the item is synchronized.

After viewing this tutorial, developers will know the following info:

  • The general sequence to purchase using PGOS and third-party platforms.
  • How to synchronize items purchased on third-party platforms to PGOS.

2. General Sequence

Developers need to go to the PGOS portal to set up the following configurations before doing actual development:

Developers need to understand the concept of items on different third-party platforms. On PGOS, item_id is the ID of an item, and item_inst_id is the ID of an item instance. However, there may be different names on third-party platforms. For example, on Steam, ItemDef is the ID of an item, and ItemInstanceID or ItemID is the ID of the item instance.

In the following, platform_item_id is the item ID on the third-party platform, and platform_item_inst_id is the item instance ID on the third-party platform.

The ID of the itemThe ID of the item instance
PGOSitem_iditem_inst_id
Definition of 3rd-party platform on PGOSplatform_item_idplatform_item_inst_id
SteamItemDefItemInstanceID/ItemID
Xboxstore_id/product_id

The general sequence is as shown below:

sequenceDiagram participant Game as Game Client participant SDK as PGOS SDK participant Backend as PGOS Backend participant Steam as 3rd-party platform(steam) note over Game,Steam: Sync Inventory After LoginPGOS(steam) Game->>SDK: SyncSteamPlayerInventory(steam) SDK->>Backend: SyncSteamPlayerInventory(steam) Backend->>Steam: sync Backend->>Backend: item mapping and grant item instances to player inventory Backend->>Steam: consume Backend-->>SDK: return sync result(item instances and currencies) SDK-->>Game: return sync result(item instances and currencies) note over Game,Steam: Show Store(Prices from Steam) Game->>SDK: GetStoreWithPlatform SDK->>Backend: GetStoreWithPlatform(steam) Backend->>Backend: get store and items mapping Backend-->>SDK: return store and items mapping SDK-->>Game: return store and items mapping Game->>Steam: GetItemsWithPrices(platform_item_ids) Steam-->>Game: return steam items' prices Game->>Game: show store UI(PGOS in-game items with prices on steam) note over Game,Steam: Purchase on Steam Game->>SDK: CheckBeforePlatformPurchase(item_id) SDK->>Backend: CheckBeforePlatformPurchase(item_id) Backend->>Backend: check unique conflict and item mapping Backend-->>SDK: return success or failed(err code/msg) SDK-->>Game: return success or failed(err code/msg) Game->>Steam: StartPurchase(platform_item_id) Steam-->>Game: return SteamInventoryResultReady_t(platform_item_inst_ids) note over Game,Steam: Sync Steam Purchases to PGOS Game->>SDK: SyncSteamPurchase(platform_item_inst_id) SDK->>Backend: SyncSteamPurchase(platform_item_inst_id) Backend->>Steam: sync(platform_item_inst_id) Backend->>Backend: item mapping and grant item instances to player inventory Backend->>Steam: consume(platform_item_inst_id) Backend-->>SDK: return sync result(item instances and currencies) SDK-->>Game: return sync result(item instances and currencies)

3. Configure Items Mapping on Web Portal

3.1 Install Add-on

Here is an example of the Steam platform.

  • Game ID: The target game ID on Steam.
  • Web API Key: The server key on Steam. Click here to get more information.
  • Use Sandbox For Economy: This switch is used by developers to debug the "synchronize purchase of items" feature in the development and test region. Enabling the switch means that only items purchased in the sandbox will be synchronized. Disable means only synchronizing non-sandbox purchased items.

img

3.2 Configure Items Mapping

Item Mapping can be added, edited, and deleted in the economy interface.

img

4.1 Get Store with Items Mapping

Get the specified store defined on the portal. This interface can obtain the value of platform_item_mapping of the specified platform configured on the PGOS Portal.

Interface prototype:

/**
* Get the specified store defined on the portal. This interface can get the value of 'platform_item_mapping'.
*
* @param Params Request params.
*/
void GetStoreWithPlatform(
const FClientGetStoreWithPlatformParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientStore* Data)> Callback) const;

struct FClientGetStoreWithPlatformParams
{
/** The specified store id. */
FString store_id;
/** Pay platform name, such as: 'Steam', 'EpicGames', etc. You can use macros starting with 'PAY_PLATFORM' in `pgos_common_define.h` */
FString pay_platform;
};

struct FClientStore
{
/** The id of the store. */
FString store_id;
/** The name of the store. */
FString display_name;
/** The description of the store. */
FString description;
/** The custom tags of the store. */
TMap<FString, FString> tags;
/** The category list of the the store, each category has a store item list. */
TArray<FClientStoreCategory> categories;
/** In-game item defines that may be referenced in the 'item_content', and you can use 'ItemDict::FindItem' to search. */
FItemDict item_dict;
/** The mapping relationship between PGOS in-game items and 3rd-party platform items configured on the PGOS portal. key: PGOS in-game item id, value: 3rd-party platform item id. */
TMap<FString, FString> platform_item_mapping;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Economy = IPgosSDKCpp::Get().GetClientEconomyAPI();
if (Economy)
{
FClientGetStoreWithPlatformParams Params;
Params.store_id = TEXT("store123456");
Params.pay_platform = TEXT("Steam");
Economy->GetStoreWithPlatform(Params, [](const FPgosResult& Ret, const FClientStore* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetStoreWithPlatform Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetStoreWithPlatform Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

4.2 Check Before Purchasing Items on Third-party Platforms

Check before purchasing items on 3rd-party platforms. It is used to check situations where items cannot be purchased due to unique items, mapping configuration, etc.

Interface prototype:

/**
* Check before purchasing items on 3rd-party platforms. It is used to check situations where items cannot be purchased due to unique items, mapping configuration, etc.
*
* @param Params Request params.
*/
void CheckBeforePlatformPurchase(
const FClientCheckBeforePlatformPurchaseParams& Params,
TFunction<void(const FPgosResult& Ret)> Callback) const;

struct FClientCheckBeforePlatformPurchaseParams
{
/** The PGOS in-game item id corresponding to the item id of the 3rd-party platform to be purchased. */
FString item_id;
/** The amount of item will be purchased. */
int32 amount = 0;
/** Pay platform name, such as: 'Steam', 'EpicGames', etc. You can use macros starting with 'PAY_PLATFORM' in `pgos_common_define.h` */
FString pay_platform;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Economy = IPgosSDKCpp::Get().GetClientEconomyAPI();
if (Economy)
{
FClientCheckBeforePlatformPurchaseParams Params;
// Fill params
Economy->CheckBeforePlatformPurchase(Params, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("CheckBeforePlatformPurchase Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("CheckBeforePlatformPurchase Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

4.3 Steam

4.3.1 Synchronize the Steam's Inventory to PGOS

Synchronize the Steam's inventory to PGOS. It is recommended to call it once after logging in to PGOS.

Interface prototype:

/**
* Synchronize the Steam's inventory to PGOS.
* It is recommended to call it once after logging in to PGOS.
*
* The PGOS backend events `event_item_granted`/`event_currency_changed` will be triggered.
*
* @param Params Request params.
* @param ResultCallback The result callback after the API execution ends, and it will be called in the GAME THREAD. For lifetime safety, it is recommended to use the CreateWeakCallback provided in PgosSDKCpp.h to create a lambda bound to an UObject.
*/
void SyncSteamPlayerInventory(
const FClientSyncSteamPlayerInventoryParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientSyncSteamPlayerInventoryResult* Data)> ResultCallback) const;

struct FClientSyncSteamPlayerInventoryParams : public FBaseBackendEventParams
{

};

struct FClientSyncSteamPlayerInventoryResult
{
/** The item instances obtained after synchronized. */
FItemInstPack item_insts;
/** The currencies obtained after synchronized. */
TArray<FCurrency> currencies;
/** Unique items that already in inventory cause failed to be synchronized. */
TArray<FClientConflictUniqueItem> conflict_unique_items;
};

struct FBaseBackendEventParams
{
/**
* [Optional] A game-defined custom value pass to PGOS backend event.
* If you need to get a custom value in the event, pass it in, otherwise, ignore this field.
* The size is limited to 4096 bytes (after converting the string to utf8 format).
* Detail for PGOS backend event: https://pgos.intlgame.com/pgosdoc/manual/service_manual/extensions/event.html.
* Detail for how to reference the 'event_custom_data':https://pgos.intlgame.com/pgosdoc/manual/service_manual/extensions/virtual_server_v2.html#43-the-request-and-response-protocol.
*/
FString event_custom_data;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Economy = IPgosSDKCpp::Get().GetClientEconomyAPI();
if (Economy)
{
FClientSyncSteamPlayerInventoryParams Params;
// Fill params
Economy->SyncSteamPlayerInventory(Params, [](const FPgosResult& Ret, const FClientSyncSteamPlayerInventoryResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SyncSteamPlayerInventory Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SyncSteamPlayerInventory Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

4.3.2 Synchronize item instances purchased from Steam to PGOS.

Synchronize item instances purchased by players from Steam to PGOS.

Interface prototype:

/**
* Synchronize item instances purchased by players from Steam to PGOS.
*
* The PGOS backend events `event_item_granted`/`event_currency_changed` will be triggered.
*
* @param Params Request params.
* @param ResultCallback The result callback after the API execution ends, and it will be called in the GAME THREAD. For lifetime safety, it is recommended to use the CreateWeakCallback provided in PgosSDKCpp.h to create a lambda bound to an UObject.
*/
void SyncSteamPurchase(
const FClientSyncSteamPurchaseParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientSyncSteamPurchaseResult* Data)> ResultCallback) const;

struct FClientSyncSteamPurchaseParams : public FBaseBackendEventParams
{
/** Steam item instance id. */
uint64 steam_item_inst_id = 0;
};

struct FClientSyncSteamPurchaseResult : public FClientSyncSteamPlayerInventoryResult
{

};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Economy = IPgosSDKCpp::Get().GetClientEconomyAPI();
if (Economy)
{
FClientSyncSteamPurchaseParams Params;
// Fill params
Economy->SyncSteamPurchase(Params, [](const FPgosResult& Ret, const FClientSyncSteamPurchaseResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SyncSteamPurchase Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SyncSteamPurchase Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

4.4 Xbox

4.4.1 Install Xbox Add-on

Here is an example of the Xbox platform.

  1. Upload Relying Party Certificate (.pfx file format)
  2. Upload Business Partner Certificate (.pfx file format)

image-20240130171445232

tip

We recommend adding the Xbox user ID claim to the xsts token when creating the relay party.

4.4.2 Synchronize the Xbox's Inventory to PGOS

Synchronize the Xbox's inventory to PGOS. It is recommended to call it once after logging in to PGOS.

Interface prototype:

/**
* Synchronize the Xbox platform's inventory to PGOS.
* It is recommended to call it once after logging in to PGOS.
*
* The PGOS virtual server events `event_item_granted`/`event_currency_changed` will be triggered.
*
* @param Params Request params.
*/
void SyncXboxPlayerInventory(
const FClientSyncXboxPlayerInventoryParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientSyncXboxPurchaseResult* Data)> Callback) const;

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Economy = IPgosSDKCpp::Get().GetClientEconomyAPI();
if (Economy)
{
FClientSyncXboxPlayerInventoryParams Params;
// Fill Params
Economy->SyncXboxPlayerInventory(Params, [](const FPgosResult& Ret, const FClientSyncXboxPurchaseResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SyncXboxPlayerInventory Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SyncXboxPlayerInventory Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

4.4.3 Synchronize item instances purchased from Xbox to PGOS.

Synchronize item instances purchased by players from Xbox to PGOS.

Interface prototype:

/**
* Synchronize item instances purchased by players from Xbox to PGOS.
*
* The PGOS virtual server events `event_item_granted`/`event_currency_changed` will be triggered.
*
* @param Params Request params.
*/
void SyncXboxPurchase(
const FClientSyncXboxPurchaseParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientSyncXboxPurchaseResult* Data)> Callback) const;

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto Economy = IPgosSDKCpp::Get().GetClientEconomyAPI();
if (Economy)
{
FClientSyncXboxPurchaseParams Params;
// Fill Params
Economy->SyncXboxPurchase(Params, [](const FPgosResult& Ret, const FClientSyncXboxPurchaseResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("SyncXboxPurchase Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("SyncXboxPurchase Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

5. Key Errors Handling

Error CodeRelevant APIHandling Suggestion
kBackendSyncInventorySyncSteamPlayerInventory
SyncSteamPurchase
It means that the sync operation failed.
kBackendUniqueItemDuplicatedCheckBeforePlatformPurchaseIt means that a unique item already exists in the inventory
kBackendMultipleUniqueItemToGrantCheckBeforePlatformPurchaseIt means that multiple unique items are synced simultaneously, which is not allowed.