Ecom Interface Overview

用于处理购买和所有权验证的接口

Epic Games Store
阅读时间19分钟

开发者可以借助 Epic在线服务 (EOS)的 电子商务接口,支持通过 Epic Games商店 (EGS)进行的游戏内购。利用此接口,你可以管理主游戏、下载内容(DLC)、虚拟产品及游戏内货币等内容。具体操作包括提供商品、完成购买交易、验证所有权,以及兑换已购买项目。

请查阅使用Web API进行所有权验证,了解如何使用Web API处理Ecom接口。

通过平台接口函数 EOS_Platform_GetEcomInterface 获取 EOS_HEcom 句柄,即可访问电子商务接口。所有电子商务接口函数均需将此句柄作为首个参数。必须确保 EOS_HPlatform 句柄可以更新(tick),以便在请求完成时触发回调。

要使用电子商务接口,你的产品必须激活 Epic账户服务 (EAS),并且必须获取用户同意以便访问 基本描述(Basic Profile) 数据。你可以在开发者门户上激活EAS,或通过Epic文档了解详情。如果没有EAS和用户的同意,你仍然可以初始化EOS的SDK和电子商务接口,但所有电子商务接口函数调用后端服务时都会失败。

设立游戏内商店

EOS下的游戏内商店执行两个主要功能:将商店的 目录商品 (有时称为“商品”)对用户展示,以便购买;以及让用户能够执行购买。EOS SDK拥有从商店目录中获取商品列表的功能,并按照游戏开发者所希望的方式展示商品。商品数据包含用户将获得的一个或多个项目,以及主要图片和细节,为用户展示商品的信息。

用户选择执行购买后,结账进程将把数据推送至 EGS覆层,其将结束购买流程。EOS将通知游戏的交易结果,提供交易柄,以便游戏执行交易成功完成后的相关操作。

如果设置了EOS_PF_DISABLE_OVERLAY 标志,则Ecom接口中相关的购买功能会被禁用。

目录商品数据

目录商品(类型 EOS_Ecom_CatalogOffer)包含本地化价格信息和描述文本(这些内容基于浏览商店的用户地区)。本地价格会包含货币代码(例如代表美元的USD),并以此货币的最小单位显示。举例而言:价格为2.99美元的商品对应的值则是299。

展示商店的目录

调用 EOS_Ecom_QueryOffers 函数即可获取开发者门户中定义的目录商品列表。此函数接受一个 EOS_Ecom_QueryOffersOptions 数据结构并将在完成时调用你的委托(类型为 EOS_Ecom_QueryOffersCallbackInfo)。将 EOS_Ecom_QueryOffersOptions 结构进行如下初始化:

属性
ApiVersionEOS_ECOM_QUERYOFFERS_API_LATEST
LocalUserId请求查询的本地用户的 EOS_EpicAccountId
OverrideCatalogNamespace可选产品命名空间(如非初始化中指定)

如果委托接收到 EOS_Ecom_QueryOffersCallbackInfo 中的 ResultCode 代表成功,则你请求的商品数据将在临时缓存中可用。使用 EOS_Ecom_GetOfferCount 来确定缓存中保存的商品数量,并使用 EOS_Ecom_CopyOfferByIndex 从其中获得单个商品(类型为 EOS_Ecom_CatalogOffer)的副本。商品中每个项目将包含此项目相关的图像和发布信息的数据,你可以单独提取这些数据。你不再需要商品的副本时,可使用 EOS_Ecom_CatalogOffer_Release 将其释放。

显示个体项目数据

在从缓存获取商品之后,你可以调用 EOS_Ecom_GetOfferItemCount 来确定商品包含的项目数量,然后使用 EOS_Ecom_CopyOfferItemByIndex 来获取单独 EOS_Ecom_CatalogItem 的副本。此结构包含额外本地化文本,以及购买时提供的权利ID。要释放分配给数据副本的内存,请调用 EOS_Ecom_CatalogItem_Release

与项目相关的图像也在缓存中可用;你可以调用 EOS_Ecom_GetItemImageInfoCount 来确定与给定项目相关的图像数量,然后使用 EOS_Ecom_CopyItemImageInfoByIndex 来获取图像URL的 EOS_Ecom_KeyImageInfo、尺寸和预期用途。不再需要此数据的副本时,使用 EOS_Ecom_KeyImageInfo_Release 来释放其使用的内存。

要从缓存获取项目的释放细节,先使用 EOS_Ecom_GetItemReleaseCount 来发现数据条目的数量。然后即可使用 EOS_Ecom_CopyItemReleaseByIndex 来获取数据(类型为 EOS_Ecom_CatalogRelease)单独的块。不再需要此数据时,可调用 EOS_Ecom_CatalogRelease_Release 将其释放。

从商店进行购买

用户从商店进行购买时,此用户将接受目录商品。购买完成后,用户将拥有商品中包含的项目,而每个项目都会为购买者的用户帐户添加一个或多个 权利

从商店进行购买涉及到3个行为:进行购买、验证所有权,以及兑换权利(这是可选项)。

用户决定进行购买后,可使用一个 EOS_Ecom_CheckoutOptions 结构调用 EOS_Ecom_Checkout。将结构进行以下初始化:

属性
ApiVersionEOS_ECOM_CHECKOUT_API_LATEST
LocalUserId进行购买的本地用户的 EOS_EpicAccountId
OverrideCatalogNamespace如未提供,当前的 SandboxId 将被提供为目录命名空间。
EntryCountEntries 参数中被提供给此结构的 EOS_EcomCheckoutEntry 的数量。
Entries一个 EOS_Ecom_CheckoutEntry 元素的数组,包含用户希望购买的每个商品的 EOS_Ecom_CatalogOfferId 数据,以及与此结构相同的 ApiVersion

进行此调用后,EOS将使用此信息来生成购买令牌。EOS将使用此购买令牌来打开其自身的覆层,使用户可以查看购买、选择支付选项,并确认或取消交易。覆层关闭时(无论是因成功、失败、或被用户取消),你的回调(类型为 EOS_Ecom_OnCheckoutCallback)运行时将带有一项 EOS_Ecom_CheckoutCallbackInfo 参数,此参数将讲述交易详情。在回调信息中,如交易成功则 TransactionId 为非空,如失败或被取消则为空。

如果对 EOS_Ecom_Checkout 的任何额外调用在通过回调的首次返回之前执行,则会出现一个 EOS_AlreadyPending 错误。

访问已完成的购买数据

如用户成功进行购买之后获得有效交易ID,可以将其传至 EOS_Ecom_CopyTransactionById 函数来获得一个 EOS_Ecom_HTransactionEOS_Ecom_Transaction_GetEntitlementsCount 将返回与交易相关的权利数量,EOS_Ecom_Transaction_CopyEntitlementByIndex 将获取一个单独权利。不再需要此数据时,调用 EOS_Ecom_Entitlement_Release 进行释放。

与交易ID相关的权利包含的数据与通过 EOS_Ecom_QueryEntitlement 函数获取的数据相同,但缓存策略有所不同。由于存在此差异,进行购买之后使用交易ID获取的权利将保持为交易专属的缓存,直到显式调用 EOS_Ecom_Transaction_Release

验证所有权

不建议在游戏客户端中验证所有权,因为终端用户可能会使用软件作弊来绕过该方案。 如有可能,请在一个受信任的游戏服务器或API上验证所有权。

协助防止盗版的最新建议

为了实现防盗版,我们建议开发商对其游戏和所有DLC实施所有权验证。 强烈建议在可信服务器/API上进行在线所有权验证。 一般认为这是防盗版的唯一有效方法。 客户端验证可以用来缓解低复杂度类型的攻击, 例如重新分配未修改的游戏文件。

对于多人游戏或在线游戏:

  • 在受信任的游戏服务器或者后端API使用 电子商务接口 或者 Web API进行所有权验证。

    • 这些验证通常在验证或者进入 关卡/匹配时进行。
  • 如果正确实现, 用户应该无法通过更改API响应或者修改游戏客户端的手段来 越过这些验证。

  • 监视异常高活动等级的账号并且 在多用户共用账号的情况下进行查封。

    • 建议将每个账号并行会话的数量限制为1。

对于所有游戏:

虽然无法通过控制客户端来完全杜绝盗版,但仍可以通过以下方式来阻止简易的攻击/分享:

  • 在启动所有权验证API之前检查 游戏可执行文件以及所有可执行代码 (.exe/.dll/.so/等等) 的数字签名和SHA1校验和。 包括验证EOS SDK以及其它所有第三方库。 若发现异常行为便拒绝访问。

  • 要求线上激活并返回加密的验证令牌, 只有在用户通过线上验证之后才可以离线运行。

    • 该令牌可以与特定的硬件/用户属性绑定 并且在指定的时间后失效。

    • 必须要保护游戏二进制文件中嵌入的公共密钥不被修改, 这种方式才能有效。

    • 如果发现该账号高频率或者在很多不同设备上进行激活, 拒绝激活。

  • 使用商用的加密/防破解解决方案来使得 逆向破解和修改验证更加复杂。

  • 请勿将DLC作为游戏主体文件的一部分储存。通过 Epic Games商店分发,或者作为额外的下载 从带有服务器端所有权验证的渠道获得。

电子商务接口提供两种所有权验证方法,即“在线”和“离线”方法。在线方法与Epic权限服务直接集成,而离线方法提供用户可验证或传递到第三方服务的签名令牌。在线方法适用于受信任的游戏服务器或在客户端系统上进行安全性较低的检查来简单验证。离线方法提供令牌,其中包含游戏客户端、用户和权利的相关信息,以及游戏客户端或外部服务可验证的签名。离线检查可避免外部服务获权访问用户数据,因此与执行所有权验证的第三方服务集成时,建议使用离线检查。

如果希望产品和服务通过RESTful端点访问所有权,请参阅Ecom网络API。

直接所有权验证

推荐的方法是使用你自己的后端服务来验证用户购买,这样便不会受到用户的恶意行为影响。请参考 电子商务Web API文档 来获取更多细节。

要确定用户是否拥有特定的目录项目,调用 EOS_Ecom_QueryOwnership 并传入一个 EOS_Ecom_QueryOwnershipOptions 结构。这将从服务器获取所有权,并将其传至你提供的 EOS_Ecom_OnQueryOwnershipCallback 回调函数。回调函数也将接收一个你指定的空指针,其能够包含你产品的任何信息,以便理解请求的情境。将以下信息填入 EOS_Ecom_QueryOwnershipOptions 结构即可开始:

属性
ApiVersionEOS_ECOM_QUERYOWNERSHIP_API_LATEST
LocalUserId要查询其所有权的本地用户的 EOS_EpicAccountId;用于 目录项目 (项目)描述文本及价格信息的本地化
CatalogItemIds要检查所有权的目录项目ID的列表
CatalogItemIdCountCatalogItemIds 中元素的数量
CatalogNamespace可选产品命名空间(如非初始化中指定)

完成后,EOS会将你请求的数据储存在 EOS_Ecom_OnQueryOwnershipCallback 结构中,并和它一起(以及你的void指针)调用你的回调函数。该结构包含一个数组的 EOS_Ecom_ItemOwnership 成员,其中每个都描述一个你所检索的项目,并且说明用户是否拥有它。服务器无法识别的项目会返回为不拥有。

基于令牌的权限验证

推荐的方法是使用你自己的后端服务来验证用户购买,这样便不会受到用户的恶意行为影响。请参考 电子商务Web API文档 来获取更多细节

要检查所有权并将结果缓存数分钟,则使用 EOS_Ecom_QueryOwnershipToken。此函数接受一个 EOS_Ecom_QueryOwnershipTokenOptions 结构,其初始化如下:

属性
ApiVersionEOS_ECOM_QUERYOWNERSHIPTOKEN_API_LATEST
LocalUserId要查询权利的用户的 FUniqueNetId
CatalogItemIds由最多32个(EOS_ECOM_QUERYOWNERSHIPTOKEN_MAX_CATALOGITEM_IDS)目录项目组成的数组,类型为 EOS_Ecom_CatalogItemId,用于检查权利。
CatalogItemIdCountCatalogItemIds 中的目录项目数量
CatalogNamespace可选产品命名空间(如非初始化中指定)

操作完成时,你的回调函数(类型为 EOS_Ecom_OnQueryOwnershipTokenCallback)将接收一个 EOS_Ecom_QueryOwnershipTokenCallbackInfo 结构,其中包含过期时间为5分钟的JSON Web令牌(JWT)。可使用公共密钥验证该JWT,并解包提取密钥ID。也可将其发送给第三方服务,后者可验证权利信息来自EGS。通过其他Web调用而获得或与组织共享的公共密钥将采用JSON Web密钥(JWK)的形式,可用于验证JWT中的签名。将GET发送至 https://ecommerceintegration-public-service-ecomprod02.ol.epicgames.com/ecommerceintegration/api/public/publickeys/{kid} 即可执行HTTP请求。范例请求如下:

GET
/ecommerceintegration/api/public/publickeys/pbvnNIE97vErdePGIRoG41h8hnP_2wIxG8xbwZCIj3g HTTP/1.1
Host: ecommerceintegration-public-service-ecomprod02.ol.epicgames.com
{
"kty": "RSA",
"e": "AQAB",
"kid": "pbvnNIE97vErdePGIRoG41h8hnP_2wIxG8xbwZCIj3g",
"n": "gcStqtD8XD9c9ifNuxXT9Xd_EEZLLCw34yxINRQPt0MxEWkoOFsuisRWGktSFtGrnUuQnp8GQY0k4Pyl_yDItWAcRtO7JUjrhQnxx3xXp_0P8xJMH1ny-RcxHF3bEJWhDzNW5PBpBjQTQZis-83499z-4OlNA7oUnDKEJkqNfzh4mMDFluPxvW_Hwpaw71nhzJI7-N-BdsPsLdqUANajLsFKq9fr06Lek_tm-6-RUxNPE3yS0x0UIsGyapA4Apcczz0xTzRDfwOkq_TyKGZiZc7vtgjkWnqdsCyXZC7dzKJvg0ggO3mKXhqZNNC_2pz24o1X_xCbG8rXtuvX8-ux-Q"
}

所有权验证令牌细节

所有权验证令牌是使用RS512签名的JWT(使用SHA-512的RSA PKCS#1签名,RSA密钥大小为2048),创建5分钟后过期。该令牌包含下列声明:

声明描述
jti此令牌的专属辨识符
sub用于请求令牌的帐户ID
clid用于请求令牌的客户端ID
ent此令牌验证的权利数组;如此值为空,则帐户无权获得给定sandboxId的请求权利。
iat表示令牌发放时间的Unix时间戳
exp表示令牌过期时间的Unix时间戳

完成购买

进行购买后,用户的帐户将获得权利,但用户可能还不能在游戏中看到购买生效。在一些情况下,完成购买比较简单,就是检查用户拥有特定权利,并对结果应用一些游戏逻辑。在这些情况下,通过EOS验证所有权已足够。在其他情况下(例如涉及消耗品或游戏货币的购买),则可能需要在游戏中、或兑换权利通过第三方服务来完成订单。

枚举权利

要获取用户帐户的权利,用 EOS_Ecom_QueryEntitlementsOptions 结构调用 EOS_Ecom_QueryEntitlements 函数。此结构的初始化如下:

请注意 EOS_Ecom_QueryEntitlements API不应该用于验证已购买的持续性内容,因为它不考虑与相关联的目录物品之间的关系,比如季票内包含的DLC。因此, EOS_Ecom_QueryEntitlements 通常用于可消耗的商品,而持续性内容由上文所述的所有权验证来管理。

EOS_Ecom_QueryEntitlements 初始化如下:

属性
ApiVersionEOS_ECOM_QUERYENTITLEMENTS_API_LATEST
LocalUserId要获取其权利的本地用户的 EOS_EpicAccountId
EntitlementNames要检查的权利名的数组。
EntitlementNameCountEntitlementNames 属性中包含的权利名的数量。最大可为EOS_ECOM_QUERYENTITLEMENTS_MAX_ENTITLEMENT_IDS。如为0,则将请求与用户帐户相关的所有权利。
bIncludeRedeemed如为true,已兑换的权利将包含在结果中。

操作完成时,EOS将缓存结果信息并用一个 EOS_Ecom_QueryEntitlementsCallbackInfo 参数运行你的回调函数(类型为 EOS_Ecom_OnQueryEntitlementsCallback)。如此参数的 ResultCodeEOS_Success,缓存将包含你请求的数据。你可以调用 EOS_Ecom_GetEntitlementsCount 来确定缓存中权利的数量;调用 EOS_Ecom_CopyEntitlementByIndex 来获取个体元素(类型为 EOS_Ecom_Entitlement)的副本,其中包括提供权利的目录项目ID、该权利的专属ID,以及其他相关数据。

兑换权利

在实现权利、或通过第三方服务管理完成之后,以 EOS_Ecom_RedeemEntitlementsOptions 结构调用 EOS_Ecom_RedeemEntitlements 函数。结构初始化如下:

属性
ApiVersionEOS_ECOM_QUERYOFFERS_API_LATEST
LocalUserId请求查询的本地用户的 EOS_EpicAccountId
EntitlementIdCountEntitlementIds 中元素的数量
EntitlementIds要兑换的权利(类型为 EOS_Ecom_EntitlementId

完成后,你的 EOS_Ecom_OnRedeemEntitlementsCallback 类型的回调会接收到一个 EOS_Ecom_RedeemEntitlementsCallbackInfo 结构。

兑换过权利之后,它不会再出现在 EOS_Ecom_QueryEntitlements 调用的结果中,除非 EOS_Ecom_QueryEntitlementsOptions 参数的 bIncludeRedeemed 被设置为 true。注意,权利在兑换后仍保存在缓存中。无法清空缓存。

电子商务接口术语表

本节是电子商务接口中常用的术语及定义。

术语定义
目录商品(Catalog Offers)目录商品是一个或多个目录项目及相关价格(可为0)的组合。购买商品后,将提供每个项目的权利。
目录项目(Catalog Item)目录项目可以是整个游戏、某种下载内容、或是游戏内货币/武器皮肤之类的虚拟物品。这种项目可以用来定义将权利提供给帐户的方式。项目也包含权利名称,此名称可以用来对权利进行逻辑分组。
权利(Entitlement)权利是用户拥有的凭证,在不同产品中意义有所不同。权利可以是动态的,提供对一个或多个目录项目的访问。
消耗性权利(Consumable Entitlement)消耗性权利持续性较短。每次在游戏中使用项目时,“使用次数”都将减少。这通常用于游戏内货币、经验值提升、以及会耗尽并再次通过购买进行补充的其他项目。一些情况下会通过外部服务实现权利。外部服务接收到权利相关的信息后,其便会兑换,使用次数变为0,将其从用户帐户移除。在此之后,外部服务将负责处理项目在游戏中的效果。
持久权利(Durable Entitlement)部分购买拥有持久性,例如下载内容,甚至整个游戏。如果第三方服务需要,责任转移之后其可以将权利标为“不激活”。
完成(Fulfillment)权利进入用户账户之后,还需要进一步实现。其可为隐式(由SDK API检查);或者第三方服务负责,通过后端API调用来完成。

下表包含电子商务接口中使用的不同ID类型,以及他们的来源和描述内容。

ID类型描述
目录商品ID(Catalog Offer ID)目录商品ID是商店中商品的独特辨识符。这些ID在一个产品中独一无二。结账流程需要目录商品ID。
目录项目ID(Catalog Item ID)目录项目ID在一个产品中独一无二,其识别单个目录项目。需要此ID才能检查特定用户是否拥有相应的目录项目。
权利名称(Entitlement Name)每个项目可拥有一个与其相关的权利名称。这些权利名称可以用于分组。不过,权利名称(Entitlement Name)通常就是目录商品ID。基于目录商品中和目录项目相关的权利名称,利用电子商务接口可查询该目录项目授予其的权力。
权利ID(Entitlement ID)完成的目录商品及其目录项目在用户帐户中是为此用户保存的特殊权利。每项权利都拥有其专属的辨识符。兑换权利时,此ID由电子商务接口使用。