Stores
1. 什么是商店
商店是一项游戏内经济服务,玩家可以通过它购买游戏内物品。您可以在一个游戏区服中定义多个商店,然后设置商店中出售的物品及其价格。
2. 关键概念
游戏可以借助PGOS 商店服务Build各种游戏内商店,并自由定义商店内产品的销售方式。了解以下概念将帮助您更好地使用这项服务。 分类(Category) 分类是在商店内创建的集成发布计划功能的容器对象。游戏可以创建一个或多个分类来管理商店中的产品销售。所有产品都必须在某个分类下创建,并且只有通过某个分类发布后玩家才能购买。 对于对游戏内商店要求较低的游戏,可以使用单个分类来管理商店中销售的所有产品。但大多数时候,使用多个分类来多样化您的商店展示是一个不错的选择。例如:
- 为长期销售且很少更新的产品创建一个分类。此分类中的产品会在较长的一段时间内发布。
- 为限时季节性产品创建一个分类。此分类中的产品会在指定的时间段内发布。
- 为每日更新的产品集群创建一个分类。此分类每天发布不同的产品。
商店物品(Store Item) 商店物品定义了可以在商店中出售的单位。游戏可以借助商店物品更方便、更灵活地Build商店,例如以不同的价格或不同的购买限额出售同一款游戏内物品。PGOS 经济系统中有两个概念与商店物品密切相关,理清它们的关系将有助于您更好地利用 PGOS 经济系统的功能:
- 游戏内物品(In-game Item):游戏内物品是 PGOS 经济服务的基石。所有玩家可以获得、购买或拥有的对象都必须有一个定义的原型,称为游戏内物品。 游戏内物品包含描述物品特征的静态数据。
- 物品实例(Item Instance):物品实例代表玩家对游戏内物品的所有权。每当玩家获得游戏内物品时,无论通过何种方式,都会在其背包中创建该物品的实例。物品实例包括可以动态更改的属性,例如到期日期和可自定义的数据字段。 下图展示了游戏内物品 、 物品实例 、 商店物品之间的关联与区别。 商店物品的详细属性将在文中后面介绍。
发布计划
类别中的发布计划定义了该类别中商店物品的销售时间安排。只有处于已发布状态的商店物品才对玩家可见并可供购买。
发布计划可以定义多个时间段,并可以自由地将商店物品安排到这些时间段中。当商店物品的任一时间段到来时,该商店物品将被发布。否则,该商店物品将被下架。
在上述示例中:
- 在第一阶段,玩家将在分类中看到商品A/B/C,且所有商品均可购买。
- 在第二阶段,商品A/C下架,玩家只能看到并购买商品B。
- 在第三阶段,商品C下架,玩家可以看到并购买商品C。
3. 配置商店
3.1 设置商店
在PGOS门户中,进入"经济" -> "商店"页面,点击添加商店来添加一个新商店。
商店字段说明:
商店ID(Store ID): 商店标识符在游戏区服内必须是唯一的。
显示名称(Display Name):商店的显示名称。
描述(Description):商店的简短描述。
标签(Tag):您可以使用此字段存储用于分类商店的信息。
选择并查看已创建的商店。
商店详情分为三个部分:
- 商店配置(Store Configuration):您可以在此修改商店的基本属性。
- 商店分类(Store Category):在此创建和管理商店内的分类。
- 商店商品列表(Store Item List):在此浏览商店内的所有商品。请注意,商品必须在分类下创建。
3.2 设置商店分类
切换到商店分类部分,点击添加分类来新增分类。The details of the store are divided into three sections:
类别字段说明:
- 类别ID(Category ID):类别标识符在游戏区服内必须唯一。
- 显示名称(Display Name):类别的显示名称。
- 描述(Description):类别的简短描述。
- 标签(Tags):可以使用此字段存储用于分类的信息。
- 排序(Order):类别的排序权重,影响类别的排序。
- 状态(Status):指定创建后类别的状态。游戏客户端无法访问已禁用状态的类别。
- 类型(Type):类别的类型,
- 简单(Simple):简单类型的类别没有发布计划功能;添加到类别中的所有商品都会直接发布。
- 高级(Advanced):高级类型的类别具有发布计划功能,允许配置一个或多个时段来自定义商品的发布计划。
3.3 在类别中设置商品
选择一个类别并切换到类别商品列表部分,然后点击新建商品来创建新的商品。
商店物品的字段说明:
- 游戏内物品(In-game Item):选择一个游戏内物品作为商店物品的原型。
- 支付平台(Pay Platform):该商品的支付服务提供商:
- PGOS:使用虚拟货币购买该商品。
- Midas Global:使用现金购买该商品,支付服务由 Midas 提供。在此模式下,商店物品的价格配置将被禁用。
- 商店物品 ID(Store Item ID):商店物品标识符在游戏区服内必须是唯一的。
- 分类(Category):当前分类;该字段可在创建商店物品后修改。每个商店物品必须且只能属于一个分类。
- 标签(Tags):您可以使用此字段存储用于分类商店物品的信息。
- 价格模式/售价(Price Mode / Selling Prices):配置物品的定价信息,详见下文。
- 玩家购买限制(Player Purchase Limit):配置每个玩家在一个周期内可以购买的最大数量。当最大数量大于 0 时,系统会记录玩家已购买的数量。当切换到下一个周期时,已记录的玩家购买数量将被重置。
3.3.1 配置商店物品价格
您可以使用多种虚拟货币为商店物品定价,但每个价格条目都必须包含默认价格和售价。以下是这两个概念的介绍:
- 默认价格:用户在物品定义中为游戏内物品配置的虚拟货币价格。这个概念代表了该物品在 PGOS 经济系统中的原始价格。
- 售价:用户在商店物品中维护的游戏内物品售价,允许高于或低于默认价格。售价可以设置为 0,让玩家免费获得物品;请谨慎操作。顾名思义,售价就是玩家购买商店物品时需要支付的价格。
商店物品定价有两种方式,请根据游戏特点选择合适的定价模式:
- 使用默认价格:直接使用物品定义中的价格信息作为商店价格。此模式不支持手动修改商店物品的定价货币和数值。
使用自定义价格:用户可以修改定价货币和价值信息。如果您需要以不同价格销售相同的游戏内物品,可以使用此模式。使用此策略可以在不受默认价格限制的情况下,为商店物品配置价格信息。
3.3.1 配置抵扣价格
在销售捆绑商品或容器商品时,必须为独特物品内容配置抵扣价格。当玩家已经拥有这些独特物品时,这些抵扣价格将从玩家的实际购买价格中扣除。需要注意的是,所有定价货币都需要配置抵扣价格,但我们允许将抵扣价格配置为0。
4. 发布管理
4.1 什么是发布管理
在完成分类和商品配置后,您的商店基本就准备就绪了。
对于简单分类,您可以随时发布分类中的全部或部分商品。对于高级分类,您可以更灵活地自定义商店商品的发布计划,这需要完成一系列配置。详细说明如下。
发布管理的本质是控制分类和商品的状态,无论是简单分类还是高级分类。分类的状态枚举如下:
- 可用(Available):表示分类中状态为"已发布"的商品对玩家可见且可购买。
- 禁用(Disable):表示分类中的所有商品对玩家不可见且不可购买。
商品的状态枚举如下:
- 已发布(Published):当所属分类可用时,该商品对玩家可见且可购买。
- 已下架(Retired):该商品对玩家不可访问,但仍受分类计划控制。也就是说,如果后续激活的时段包含此商品,它将切换为已发布状态。
- 已禁用(Disabled):该商品对玩家不可访问,且不再受分类计划控制,除非重新启用。即使后续激活的时段包含此商品,也无法切换为已发布状态。
4.2 在简单分类中发布
对于简单分类,发布管理是直接操作分类和商品项目的状态。在创建分类时,您可以选择分类的默认状态,
您也可以在创建后随时修改分类的状态,这样可以快速控制玩家对该分类中所有商店物品的访问权限,
无法对简单类别中的商品进行下架操作,但您可以通过禁用操作快速禁用一个或多个商品。一旦商品准备就绪,可以通过启用操作重新激活。
4.3 使用手动排期发布
4.3.1 使用排期进行发布管理
您可以使用高级分类中的手动排期功能为商店物品设置多个发布时段,每个发布时段可以分配多个商店物品。
您可以使用手动排期自由定制商店物品的发布计划,但请注意以下限制:
- 同一分类下创建的时段不能有重叠的时间区间。
- 一个商店物品可以添加到多个时段中。
时段的工作机制如下:
- 当某个时段的开始时间到达时,分配给该时段的所有非禁用状态的商店物品将切换为已发布状态。
- 当某个时段的结束时间到达时,分配给该过期时段的所有已发布商店物品将切换为已下架状态。
要在分类中添加时段,开始时间和结束时间都是必填字段。
您可以在一个类别中批量添加多个具有特定长度和特定间隔的时段:
- 至少需要输入1个时段,
- 时段长度必须大于0,
- 间隔是一个可选参数。
将商品分配到时段
4.3.2 发布管理
在完成计划配置后,分类中的商店物品将按计划发布。但在某些特殊情况下,您可能希望暂时下架已发布的商店物品以进行必要的修改。有几种方法可以实现这一点:
- 禁用处于可用状态的分类。此操作将阻止玩家访问该分类中所有已发布的商店物品,无论是查看还是购买。准备就绪后,您可以执行启用操作来恢复该分类。
- 禁用处于已发布状态的商店物品。 此操作将暂时禁用该分类下的特定商店物品。无论这些物品在被禁用前处于什么状态,玩家都将无法查看或购买它们。当需要时,您可以执行启用操作来恢复这些商店物品的状态。
在禁用/启用商店项目时,需要注意PGOS将根据项目被禁用前的状态以及其时段状态来决定启用后的状态。例如:
- 如果商店项目状态为"已发布",在其时段到期前被禁用后又启用,则会保持"已发布"状态。
- 如果商店项目状态为"已发布",在其时段到期后被禁用后又启用,则会保持"已下架"状态。
- 如果商店项目状态为"已下架",在其时段开始前被禁用后又启用,则会保持"已下架"状态。
- 如果商店项目状态为"已下架",在其时段开始后被禁用后又启用,则会切换为"已发布"状态。
4.4 使用轮换计划发布
手动计划适用于安排短期的产品发布计划,例如与战斗通行证季度期间推出的类别进行协调。在处理长期、循环性的发布计划时,手动计划可能不够有效,这种情况下,您可以使用轮换计划来处理这些任务。
创建类别时,请将类型参数配置为轮换。
轮换计划将自动将商店物品分配到批量创建的时段中,因此您必须先填写该类别的商店物品清单。
然后可以配置轮换计划,
- 轮换时间(Rotation Time):轮换计划的开始和结束时间。
- 轮换周期长度(Rotation Cycle Length):每个轮换周期的长度,第一个周期从轮换开始时间开始计算。
- 发布时段长度(Publish Period Length):每个轮换周期内发布时段的长度。发布时段从周期开始计算,且不能超出该周期的结束时间,这是一个显而易见的限制。
- 每时段商品数量(Items Count per Period):每个发布时段内发布的商店商品数量。
- 商品轮换模式(Items Rotation Mode):此配置项指示 PGOS 如何在每个时段内填充商店商品,目前有两种可用策略,随机 / 顺序。
4. 通过 PGOS SDK 访问商店
4.1 获取商店信息
此操作获取在门户网站中配置的所有商店信息,包括分类和商店物品列表。
通常情况下,您无需关注 pay_platform
参数。当传入 pay_platform
参数时,它会返回 platform_item_mapping
,其中包含 PGOS 中游戏内物品与在 PGOS 门户网站配置的第三方平台物品之间的映射关系。此参数仅在开发者需要集成第三方支付时使用。更多详情,请参阅第三方平台购买。
这里还需要提到 FClientStoreCategory
。GetStore
API 只会返回至少包含一个已发布状态的商店物品的分类。
接口原型:
/**
* Get the specified store defined on the portal.
*
* @param Params Request params.
*/
void GetStore(
const FClientGetStoreParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientStore* Data)> Callback) const;
struct FClientGetStoreParams
{
/** 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;
};
struct FClientStoreCategoryBrief
{
/** The id of the category. */
FString category_id;
/** The name of the category. */
FString name;
/** The description of the category. */
FString description;
/** The custom tags of the category. */
TMap<FString, FString> tags;
/** The type of the category. */
EClientStoreCategoryType type = EClientStoreCategoryType::Simple;
/** The Unix timestamp (in seconds) for the start time of the currently active period. It is always 0 under the simple type. */
int64 published_start_time = 0;
/** The Unix timestamp (in seconds) for the end time of the currently active period. It is always 0 under the simple type. */
int64 published_end_time = 0;
};
/** The information of the store category. */
struct FClientStoreCategory : public FClientStoreCategoryBrief
{
/** Store items in the category. */
TArray<FClientStoreItem> content_items;
};
struct FClientStoreItem
{
/** The id of the store item. */
FString store_item_id;
/** Store item status: Dummy / Available / Owned. */
EClientStoreItemStatus status = EClientStoreItemStatus::Dummy;
/** The currency to purchase the store item, it may be multiple. */
TArray<FClientStoreItemPrice> prices;
/** The in-game item related to the store item. */
FInGameItem item;
/**
* The tags of store item
* Fill in the tags of item on the portal.
*/
TMap<FString, FString> tags;
/** Purchase amount limit of store item. */
FClientStoreItemPurchaseLimit purchase_limit;
};
struct FClientStoreItemPurchaseLimit
{
/** The maximum amount limit that players can purchase for this store item. 0 means no limit. */
int32 max_purchase_amount = 0;
/** When max_purchase_amount is greater than 0, record the amount that the player has purchased. Please note that when the category period is switched, its value will be recalculated. */
int32 purchased_amount = 0;
};
enum class EClientStoreItemStatus : uint8
{
/** None. */
Dummy = 0,
/** The item can be purchased normally. */
Available = 1,
/** The unique item can't be purchased because the player already owns. */
Owned = 2,
/** The store item can't be purchased because the player have reached the maximum purchase limit. */
ExceedLimit = 3
};
示例代码:
#include "PgosSDKCpp.h"
void SomeUObjectClass::SomeFunction()
{
auto Economy = IPgosSDKCpp::Get().GetClientEconomyAPI();
if (Economy)
{
FClientGetStoreParams Params;
Params.store_id = TEXT("store123456");
Params.pay_platform = TEXT("");
Economy->GetStore(Params, [](const FPgosResult& Ret, const FClientStore* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetStore Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetStore Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
4.2 批量获取商店简要信息
获取指定商店的简要信息。
与FClientStore
相比,FClientStoreBrief
缺少游戏内物品的相关信息。
接口原型:
/**
* Get brief information of specified stores.
*
* @param Params Request params.
*/
void GetStoreBriefs(
const FClientGetStoreBriefsParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientGetStoreBriefsResult* Data)> Callback) const;
struct FClientGetStoreBriefsParams
{
/** List of store IDs to be queried. */
TArray<FString> store_ids;
};
struct FClientGetStoreBriefsResult
{
/** List of store brief information queried. */
TMap<FString, FClientStoreBrief> store_briefs;
};
struct FClientStoreBrief
{
/** 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 brief list of the the store. */
TArray<FClientStoreCategoryBrief> category_briefs;
};
示例代码:
#include "PgosSDKCpp.h"
void SomeUObjectClass::SomeFunction()
{
auto Economy = IPgosSDKCpp::Get().GetClientEconomyAPI();
if (Economy)
{
FClientGetStoreBriefsParams Params;
// Fill params
Economy->GetStoreBriefs(Params, [](const FPgosResult& Ret, const FClientGetStoreBriefsResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetStoreBriefs Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetStoreBriefs Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
4.3 从商店批量获取类别
从商店批量获取类别
接口原型:
/**
* Get the specified store categories defined on the portal.
*
* @param Params Request params.
*/
void GetStoreCategories(
const FClientGetStoreCategoriesParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientGetStoreCategoriesResult* Data)> Callback) const;
struct FClientGetStoreCategoriesParams
{
/** The specified store id. */
FString store_id;
/** List of category IDs to be queried. */
TArray<FString> category_ids;
};
struct FClientGetStoreCategoriesResult
{
/** The specified categories that have been queried. */
TArray<FClientStoreCategory> categories;
/** Categories that failed to query, key: player id, value: failed reason. */
TMap<FString, FString> fails;
/** In-game item defines that may be referenced in the 'item_content', and you can use 'ItemDict::FindItem' to search. */
FItemDict item_dict;
/** 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;
};
示例代码:
#include "PgosSDKCpp.h"
void SomeUObjectClass::SomeFunction()
{
auto Economy = IPgosSDKCpp::Get().GetClientEconomyAPI();
if (Economy)
{
FClientGetStoreCategoriesParams Params;
// Fill Params
Economy->GetStoreCategories(Params, [](const FPgosResult& Ret, const FClientGetStoreCategoriesResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetStoreCategories Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetStoreCategories Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
4.4 购买商店物品
此操作用于购买商店中指定数量的指定物品。购买成功后,系统会返回消费的货币金额、物品实例以及货币余额。
购买分为两个步骤:创建订单和提交订单。
1. 创建订单: 输入您想要购买的物品、数量和单价,以生成唯一的订单ID。
/**
* Create an order before purchase the specified store item in the store.
*
* After committing the order, 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 CreateStoreOrder(
const FClientCreateStoreOrderParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientCreateStoreOrderResult* Data)> ResultCallback) const;
struct FClientCreateStoreOrderParams : public FBaseBackendEventParams
{
/** The store id of this store item. */
FString store_id;
/** The store item id. */
FString store_item_id;
/** The amount of store item will be purchased. */
int32 amount = 0;
/** The currency code and actual price to purchase the item, it must be one of the prices of this store item. */
FClientItemPrice price;
};
struct FClientCreateStoreOrderResult
{
/** Order ID required for purchase. */
FString order_id;
};
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;
};
2. 提交订单: 使用已创建的订单ID提交订单。订单状态完成后,将扣除用户的货币以获得所购买的游戏内物品。同一订单最多只会被扣款一次。多次提交不会重复扣款。
提交订单接口 CommitStoreOrder
的回调将返回 FPgosResult
和 FClientCommitStoreOrderResult
。如果 FPgosResult
由于网络相关原因失败,您可以使用相同的订单ID再次调用 CommitStoreOrder
。
FClientCommitStoreOrderResult
中的 status
表示订单状态:
- StoreOrderProcessing: 订单仍在处理中。您可以等待几秒钟后通过调用
QueryStoreOrder
接口再次获取订单状态。 - StoreOrderCompleted: 订单交易成功。已完成扣款和物品发放。
- StoreOrderFailed: 订单交易失败。
/**
* After committing the order, the payment will be deducted and the items will be dispatched.
*
* The PGOS backend events `event_item_granted`/`event_currency_changed` will be triggered.
* The `event_custom_data` filled in the parameter `CreateStoreOrderParams` will be passed to these events.
*
* @param Params Request params.
* @param ResultDelegate The result delegate after the API execution ends, and it will be called in the GAME THREAD.
*/
void CommitStoreOrder(
const FClientCommitStoreOrderParams& Params,
TFunction<void(const FPgosResult& Ret, const FClientCommitStoreOrderResult* Data)> ResultCallback) const;
struct FClientCommitStoreOrderParams
{
/** The previously created order ID. */
FString order_id;
};
struct FClientCommitStoreOrderResult
{
/** The code of currency that has been spent to purchase the store item. */
FString spent_currency_code;
/** The amount of currency that has been spent to purchase the store item. */
int32 spent_currency_amount = 0;
/** The item instances obtained after purchase. */
FItemInstPack item_insts;
/** The currencies obtained after purchase. */
TArray<FCurrency> currencies;
/** Order status. */
EClientStoreOrderStatus status = EClientStoreOrderStatus::Dummy;
};
enum class EClientStoreOrderStatus : uint8
{
/** None. */
Dummy = 0,
/** Order is being processed. */
StoreOrderProcessing = 1,
/** Purchase successful. */
StoreOrderCompleted = 2,
/** Purchase failed. */
StoreOrderFailed = 3
};
调用 Client Economy 模块的 CreateStoreOrder
、CommitStoreOrder
和 QueryStoreOrder
方法:
#include "PgosSDKCpp.h"
void SomeUObjectClass::CreateStoreOrder()
{
auto Economy = IPgosSDKCpp::Get().GetClientEconomyAPI();
if (Economy)
{
FClientCreateStoreOrderParams Params;
// Fill create order params
Economy->CreateStoreOrder(Params, [](const FPgosResult& Ret, const FClientCreateStoreOrderResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("CreateStoreOrder Success"));
// you can call `CommitStoreOrder` to commit the order
}
else
{
UE_LOG(LogTemp, Log, TEXT("CreateStoreOrder Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
void SomeUObjectClass::CommitStoreOrder(const FString& OrderId)
{
auto Economy = IPgosSDKCpp::Get().GetClientEconomyAPI();
if (Economy)
{
FClientCommitStoreOrderParams Params;
Params.order_id = OrderId;
Economy->CommitStoreOrder(Params, [](const FPgosResult& Ret, const FClientCommitStoreOrderResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("CommitStoreOrder Success"));
// If the returned order status is Processing, you can call QueryStoreOrder after a short period of time to query the order status.
}
else
{
UE_LOG(LogTemp, Log, TEXT("CommitStoreOrder Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
// If there is a network error, you can commit it again.
}
});
}
}
void SomeUObjectClass::QueryStoreOrder(const FString& OrderId)
{
auto Economy = IPgosSDKCpp::Get().GetClientEconomyAPI();
if (Economy)
{
FClientQueryStoreOrderParams Params;
Params.order_id = OrderId;
Economy->QueryStoreOrder(Params, [](const FPgosResult& Ret, const FClientQueryStoreOrderResult* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("QueryStoreOrder Success"));
// If the returned order status is Processing, you can call QueryStoreOrder after a short period of time to query the order status.
}
else
{
UE_LOG(LogTemp, Log, TEXT("QueryStoreOrder Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
5. 订单
5.1 概述
每当玩家从商店购买物品时,系统都会生成一个订单。前往门户 -> 经济 -> 商店 -> 订单可以查询和追踪订单状态信息。
每个订单包含以下信息:
商店 ID(Store ID):下订单的商店 ID。
类别ID(Category ID):所购买物品的类别。当物品有多个类别时,可以从此字段跟踪订单来源。
玩家ID(Player ID):发起订单的玩家ID。
订单ID(Order ID):订单ID。
游戏内物品ID(In-game Item ID):所购买物品的ID。
物品实例ID列表(Item Instance ID List):订单完成后授予玩家的物品实例列表。
虚拟货币(Virtual Currency):用于支付订单的货币。
订单状态(Order Status):订单当前的状态,包括:
待处理(Pending) -- 订单等待处理
初始化失败(Initialize Failed) -- 不满足购买条件
已付款(Paid) -- 订单付款完成
付款失败(Pay Failed) -- 订单付款失败
授权(Granting) -- 订单发货
授权失败(Grant Failed) -- 订单发货失败
完成(Completed) -- 订单完成
退货(Returning) -- 取回物品实例
退货失败(Return Failed) -- 取回物品实例失败
已退货(Returned) -- 物品实例已取回
退款(Refunding) -- 退款
退款失败(Refund Failed) -- 退款失败
撤销完成(Revoke Completed) -- 订单已取消
创建时间(Created Time):订单的创建时间,符合条件的订单可以在 Portal 上撤销。订单撤销后,相关物品实例将从玩家处收回,虚拟货币支付将退还给玩家。满足以下条件的订单可以撤销:
订单状态为 COMPLETE。
订单授予的物品实例仍然有效。
5.2 订单处理流程
当发起物品购买订单时,主要的处理流程如下:
- 订单验证: 通过检查商店ID和物品ID是否正确以及玩家钱包余额是否充足来验证订单请求的合法性。
- 扣款(支付): 从玩家钱包中扣除此订单的付款。如果支付成功,将在Client SDK中触发OnBalanceChanged事件。
- 发放: 将购买的物品发放到玩家的背包中。如果成功,将在Client SDK中触发OnInventoryChanged事件。如果购买的物品是包含虚拟货币的礼包或容器,发放过程将向玩家钱包添加指定数量的虚拟货币。如果成功,将在Client SDK中触发OnBalanceChanged事件。
5.3 订单撤销流程
当订单被撤销时,主要处理流程如下:
- 物品回收:已购买的物品将从玩家背包中回收。如果回收成功,将在 Client SDK 中触发 OnInventoryChanged 事件。如果购买的物品是包含虚拟货币的礼包或容器,回收过程将从玩家钱包中扣除指定数量的虚拟货币。如果扣除成功,将在 Client SDK 中触发 OnBalanceChanged 事件。
- 退款:该订单的付款将退还至玩家钱包。如果退款成功,将在 Client SDK 中触发 OnBalanceChanged 事件。