Skip to main content

Integrate with flexible pricing bundle

Last updated on April 26, 2024

Overview

The flexible pricing bundle in AccelByte Gaming Services (AGS) enables you to provide reasonable pricing options for players in your game who already own some items in the bundle. This also helps you offer more consistent discounting experience as pricing will be in-sync when contents of a bundle go on sale.

As demonstrated in the below image, the final bundle price is calculated by the following factors: player already owns an item in the bundle, item has a discount, and bundle has a discount.

Player_Portal

Integrate with game client

This section covers how to integrate the flexible pricing bundle with your game client.

Login

Add your AGS Admin Portal login credentials to allow your game client to call the API endpoints for the flexible pricing bundle.

Before you start, ensure that you have the correct configuration and have integrated your game with OSS. For more information, see AGS OSS for Unreal Engine.

IOnlineSubsystem* OnlineSubsystem = IOnlineSubsystem::Get(ACCELBYTE_SUBSYSTEM);
FOnlineIdentityAccelBytePtr IdentityInterface = StaticCastSharedPtr<FOnlineIdentityAccelByte>(OnlineSubsystem->GetIdentityInterface());
if (!IdentityInterface.IsValid())
{
return;
}

const int32 LocalUserNum = 0;
auto AccelByteLoginCompletehandle = IdentityInterface->AddAccelByteOnLoginCompleteDelegate_Handle(LocalUserNum
, FAccelByteOnLoginCompleteDelegate::CreateLambda(
[this]
(int32 LoggedInLocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId,
const FOnlineErrorAccelByte& Error)
{
UE_LOG(LogTemp, Log, TEXT("Get AccelByteOnLoginComplete: LocalUserNum=%d"), LoggedInLocalUserNum);
})
);

const EAccelByteLoginType Type = EAccelByteLoginType::AccelByte;
const FString ID = TEXT("username");
const FString Password = TEXT("password");
IdentityInterface->Login(LocalUserNum, FOnlineAccelByteAccountCredentials{ Type , ID, Password });

Get flexible pricing bundle

To get flexible pricing bundles, make sure you have already created them in Admin Portal. You can check the boolean field "Flexible" from the response to determine whether it is a flexible pricing bundle. To create a flexible pricing bundle, see Create flexible pricing bundle.

Before you start, ensure that you have the correct configuration and have integrated your game with OSS. For more information, see AGS OSS for Unreal Engine.

const FOnlineStoreV2AccelBytePtr OnlineStoreV2AccelByte = StaticCastSharedPtr<FOnlineStoreV2AccelByte>(StoreInterface);
const FOnlinePurchaseAccelBytePtr OnlinePurchaseAccelByte = StaticCastSharedPtr<FOnlinePurchaseAccelByte>(PurchaseInterface);

// GetItemsByCriteria to get flexible bundle item
FAccelByteModelsItemPagingSlicedResult ItemPagingSlicedResult{};
OnlineStoreV2AccelByte->AddOnGetItemsByCriteriaCompleteDelegate_Handle(FOnGetItemsByCriteriaCompleteDelegate::CreateLambda(
[this, &ItemPagingSlicedResult]
(bool bWasSuccessful, const FAccelByteModelsItemPagingSlicedResult& Value, const FOnlineError& Error) {
if (bWasSuccessful)
{
ItemPagingSlicedResult = Value;
}
}));

const FString Region = TEXT("US");
FAccelByteModelsItemCriteria ItemCriteria;
ItemCriteria.Language = TEXT("en");
ItemCriteria.Region = Region;
ItemCriteria.ItemType = EAccelByteItemType::BUNDLE;
ItemCriteria.CategoryPath = ECommerceTestFlexibleBundleItemCategoryPath;
auto UserId = IdentityInterface->GetUniquePlayerId(LocalUserNum).Get();
OnlineStoreV2AccelByte->GetItemsByCriteria(*UserId, ItemCriteria);


FString ItemId = ItemPagingSlicedResult.Data[0].ItemId;

Get estimated price

As the price can be varied by who is buying a flexible pricing bundle and when, the game client needs to call the GetEstimatedPrice endpoint to get the latest pricing for players.

Before you start, ensure that you have the correct configuration and have integrated your game with OSS. For more information, see AGS OSS for Unreal Engine.

// GetEstimatedPrice to get the proper price when creating order
bool bGettingEstimatedPriceDone = false;
bool bGettingEstimatedPriceSuccess = false;
TArray<FAccelByteModelsEstimatedPrices> EstimatedPrices{};
OnlineStoreV2AccelByte->AddOnGetEstimatedPriceCompleteDelegate_Handle(FOnGetEstimatedPriceCompleteDelegate::CreateLambda(
[this, &EstimatedPrices]
(bool bWasSuccessful, const TArray<FAccelByteModelsEstimatedPrices>& Value, const FOnlineErrorAccelByte& Error) {
if (bWasSuccessful)
{
EstimatedPrices = Value;
}
}));
auto UserId = IdentityInterface->GetUniquePlayerId(LocalUserNum).Get();
OnlineStoreV2AccelByte->GetEstimatedPrice(*UserId, {ItemId}, Region);

Create order

Ensure that you use th estimated price when placing an order for a flexible pricing bundle.

Before you start, ensure that you have the correct configuration and have integrated your game with OSS. For more information, see AGS OSS for Unreal Engine.

// Create Order to enabling OrderBundleItemInfos
const FString CurrencyCode = EstimatedPrices[0].EstimatedPrices[0].CurrencyCode;
const int32 DiscountedPrice = EstimatedPrices[0].EstimatedPrices[0].DiscountedPrice;
const int32 Price = EstimatedPrices[0].EstimatedPrices[0].Price;

FString OrderNo{};
constexpr int32 Quantity = 1;
FAccelByteModelsOrderCreate OrderCreate;
OrderCreate.CurrencyCode = CurrencyCode;
OrderCreate.DiscountedPrice = DiscountedPrice * Quantity;
OrderCreate.Price = Price * Quantity;
OrderCreate.Quantity = Quantity;
OrderCreate.ReturnUrl = TEXT("https://sdk.example.com");
OrderCreate.ItemId = ItemId;
OrderCreate.Region = Region;
OrderCreate.Language = TEXT("en");

bool bCreatingOrderDone = false;
bool bCreatingOrderSuccess = false;
FAccelByteModelsOrderInfo OrderInfo{};
OnlinePurchaseAccelByte->AddOnCreateNewOrderCompleteDelegate_Handle(FOnCreateNewOrderCompleteDelegate::CreateLambda(
[this, &OrderInfo]
(bool bWasSuccessful, FAccelByteModelsOrderInfo Value, const FOnlineErrorAccelByte& Error) {
if (bWasSuccessful)
{
OrderInfo = Value;
}
}));
auto UserId = IdentityInterface->GetUniquePlayerId(LocalUserNum).Get();
OnlinePurchaseAccelByte->CreateNewOrder(*UserId, OrderCreate);

Query order

Use the QueryUserOrders endpoint to retrieve information on the bundle order.

Before you start, ensure that you have the correct configuration and have integrated your game with OSS. For more information, see AGS OSS for Unreal Engine.

// QueryUserOrders to check OrderBundleItemInfos exist 
bool bGettingUserOrdersDone = false;
bool bGettingUserOrdersSuccess = false;
FAccelByteModelsPagedOrderInfo PagedOrderInfo{};
OnlinePurchaseAccelByte->AddOnQueryUserOrdersCompleteDelegate_Handle(FOnQueryUserOrdersCompleteDelegate::CreateLambda(
[this, &PagedOrderInfo]
(bool bWasSuccessful, const FAccelByteModelsPagedOrderInfo& Value, const FOnlineErrorAccelByte& Error) {
if (bWasSuccessful)
{
PagedOrderInfo = Value;
}
}));
FAccelByteModelsUserOrdersRequest UserOrdersRequest{};
UserOrdersRequest.ItemId = ItemId;
auto UserId = IdentityInterface->GetUniquePlayerId(LocalUserNum).Get();
OnlinePurchaseAccelByte->QueryUserOrders(*UserId, UserOrdersRequest);

You can find the bundle order information in the OrderBundleItemInfos variable.

auto BundleInfo = PagedOrderInfo.Data[0].OrderBundleItemInfos;

Auto-calculate estimated price

In order to simplify getting flexible bundle items with an estimated price, set the autoCalcEstimatedPrice parameter to true when getting the item.

Get item by ID

string itemId = "YourItemId";
string storeLanguage = "en-US";
string storeRegion = "US";
bool autoCalcEstimatedPrice = true;
PopulatedItemInfo itemInfo;
int itemPrice;

Items items = AccelByteSDK.GetClientRegistry().GetApi().GetItems();

items.GetItemById(itemId, storeRegion, storeLanguage, autoCalcEstimatedPrice, result =>
{
if (result.IsError)
{
// Your logic to handle errors in GetItemById
// ...
Debug.Log($"Error GetItemById, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}

itemInfo = result.Value;
itemPrice = itemInfo.regionData[0].price;
});

Get item by SKU

string itemSku = "ItemSku";
string itemLanguage = "en-US";
string itemRegion = "US";
bool autoCalcEstimatedPrice = true;
ItemInfo itemInfo;
int itemPrice;

Items items = AccelByteSDK.GetClientRegistry().GetApi().GetItems();

items.GetItemBySku(itemSku, itemLanguage, itemRegion, autoCalcEstimatedPrice, result =>
{
if (result.IsError)
{
// Your logic to handle errors in GetItemBySku
// ...
Debug.Log($"Error GetItemBySku, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}

itemInfo = result.Value;
itemPrice = itemInfo.regionData[0].price;
});

Get local items by bulk

string[] itemIds = { "YourItemId1", "YourItemId2" };
string storeLanguage = "en-US";
string storeRegion = "US";
bool autoCalcEstimatedPrice = true;
ItemInfo[] itemsInfo;

Items items = AccelByteSDK.GetClientRegistry().GetApi().GetItems();

items.BulkGetLocaleItems(itemIds, storeLanguage, storeRegion, autoCalcEstimatedPrice, result =>
{
if (result.IsError)
{
// Your logic to handle errors in BulkGetLocaleItems
// ...
Debug.Log($"Error BulkGetLocaleItems, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}

itemsInfo = result.Value;
});

Search item

string itemKeyword = "ItemKeyword";
string itemRegion = "US";
int offset = 0;
int limit = 5;
bool autoCalcEstimatedPrice = true;
ItemInfo[] itemsInfo;

Items items = AccelByteSDK.GetClientRegistry().GetApi().GetItems();

items.SearchItem(storeLanguage, itemKeyword, offset, limit, itemRegion, autoCalcEstimatedPrice, result =>
{
if (result.IsError)
{
// Your logic to handle errors in SearchItem
// ...
Debug.Log($"Error SearchItem, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}

itemsInfo = result.Value.data;
});

List active section contents

string storeId = "YourStoreIdFromAdminPortal";
string viewId = "YourViewId";
string storeLanguage = "en-US";
string storeRegion = "US";
bool autoCalcEstimatedPrice = true;
SectionInfo[] sectionsInfo;

StoreDisplay storeDisplay = AccelByteSDK.GetClientRegistry().GetApi().GetStoreDisplayService();

storeDisplay.ListActiveSectionContents(storeId
, viewId
, storeRegion
, storeLanguage
, autoCalcEstimatedPrice
, result =>
{
if (result.IsError)
{
// Your logic to handle errors in ListActiveSectionContents
// ...
Debug.Log($"Error ListActiveSectionContents, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}

sectionsInfo = result.Value;
// Your logic to run after ListActiveSectionContents is successful
});