无收据付款处理(旧版 Economy)

重要

经济 v1 API 处于维护模式,将不会收到任何新功能,只有 bug 修复。 v1 API 将在可预见的未来进行维护。 请参阅 经济 v2 概述 ,详细了解 PlayFab 经济的下一个版本!

除了适用于各种平台的收据验证之外,PlayFab 还提供了一种机制,用于通过不使用基于收据或权利的系统的付款提供商实现购买。 这包括 Facebook、PayPal、Xsolla 和 Steam 等提供商。

本教程引导使用此购物车式付款系统,介绍如何:

  • 定义一组玩家要购买的商品。

  • 通过所选提供商处理玩家。

  • 验证付款是否成功处理,从而向玩家物品栏添加相应物品。

  • 此外,教程还涵盖了流程中的玩家提供商部分,完整概述如何完成购买。

下图显示在付款流程中使用的 API 调用的汇总。

PlayFab - 付款 API 流程

设置付款提供商

开始使用实际处理付款的逻辑之前,需要做的第一件事是实际设置预期付款提供商。

为此,请执行以下操作:

  • 在 PlayFab Game Manager 中选择游戏。
  • 选择 Add-ons 选项卡。

这样会进入 Add-ons Marketplace,在其中可以找到所有第三方集成,包括付款提供商

注意

某些提供商(如 Facebook 和 Steam)不仅 处理付款,本教程的“平台”部分将对它们进行介绍。

  • 选择要使用的 Providers
  • 然后对每个提供商选择 Install 选项。
  • 输入请求的信息(通常是应用程序和密钥),但是每个提供商都有自己的独特要求。

注意

通过此信息可以代表你与其服务通信,以便我们可以发起购买并检查其状态。

  • 该过程完成后,即可开始实现付款流程。

PlayFab API 调用

对于客户端,此过程的调用序列对于大多数无收据付款提供商非常类似。

  • StartPurchase - 为购买创建商品列表。
  • PayForPurchase - 建立付款提供商并锁定付款定价。
  • ConfirmPurchase - 验证购买是否成功完成,并在需要时确保将商品添加到玩家物品栏。

注意

例外情况是 Xsolla,本教程最后会单独讨论。

在下面的示例中,可以看到 {{TitleID}}{{SessionTicket}} 这类字段。 需要在自己的代码中将这些值替换为游戏和玩家身份验证票证的相应值。

发起购买

在整个购买流程中,需要处理来自游戏目录的、玩家要购买的一组已定义商品。

这是通过调用 PlayFab 客户端 API 方法 StartPurchase 来定义的商品购物车,以开始这个过程。

PlayFabClientAPI.StartPurchase(new StartPurchaseRequest() {
    CatalogVersion = "IAP1_0",
    Items = new List<ItemPurchaseRequest>() {
        new ItemPurchaseRequest() {
            ItemId = "BigBagOfGold",
            Quantity = 1,
            Annotation = "Purchased via in-game store"
        }
    }
}, result => {
    // Handle success
}, error => {
    // Handle error
});

在此示例中,我们加载的购物车包含在 ID 为 IAP1_0 的游戏目录中定义的一个物品 (BigBagOfGold)。 这是在游戏目录中定义的 ItemId

玩家只想购买其中一个物品。 注释用于指示该物品如何在玩家物品栏中关联,这可用于以后查看。

注意

可以通过包含 StoreId 参数,指定此购买使用的是来自游戏中定义的商店的定价。

StartPurchase 调用的返回包含许多信息,包括来自目录的物品的 DisplayNameDescription 值、对物品定义的任何价格以及玩家的虚拟货币余额。

不过下一步需要的主要内容是 OrderId(唯一地标识此购买),以及要使用的特定提供商(在 PaymentOptions 中)。

注意

只有为游戏启用的付款提供商才会出现在 PaymentOptions 中。

{
  "code": 200,
  "status": "OK",
  "data": {
    "OrderId": "1234567890ABCDEF",
    "Contents": [{
      "ItemId": "BigBagOfGold",
      "ItemInstanceId": "ABCDEF1234567890",
      "DisplayName": "A hefty sack of gold",
      "Description": "Why, it’s a stonking great bag of shiny!",
      "VirtualCurrencyPrices": {
        "RM": 999
      }
    }],
    "PaymentOptions": [{
        "Currency": "RM",
        "ProviderName": "Facebook",
        "Price": 999,
        "StoreCredit": 0
      },
      {
        "Currency": "RM",
        "ProviderName": "PayPal",
        "Price": 999,
        "StoreCredit": 0
      },
      {
        "Currency": "RM",
        "ProviderName": "Steam",
        "Price": 999,
        "StoreCredit": 0
      }
    ],
    "VirtualCurrencyBalances": {
      "VC": 0
    }
  }
}

建立付款提供商:Webhook 提供商

Facebook

通常,此时的流程是告诉 PlayFab 将使用的付款提供商。 这样服务就可以与该提供商通信,并设置有关交易的信息。

Facebook 是此过程的一个例外情况。 它要求首先 服务中发起购买。 在此之后,第三方服务(如 PlayFab)可以使用 Facebook 提供的交易标识符验证购买的状态。

如果商店、目录或物品 ID 是字母数字,则它们在生成 OpenGraphProduct URL 时必须进行 HTML/URL 编码,如以下代码所示。

FB.ui({
    method: 'pay',
    action: 'purchaseitem',
    product: "https://{{TitleId}}.playfabapi.com/OpenGraphProduct/{{TitleId}}/{{StoreId}}/IAP1_0/BigBagOfGold",
    request_id: "1234567890ABCDEF"
}, function (response) {
    console.log('Payment completed', response);
    print(response);
    if (response.payment_id) {
        facebookPurchaseId = response.payment_id;
        payForOrder(); // This is a function call you need to write
                       // which makes the call to the Client API PayForPurchase
                       // (see below)
    }
});

进行此调用时,请注意产品在 Facebook 中区分大小写

特别是,TitleId (需要替换为游戏在 PlayFab 中的 正确 游戏 ID) 通常在其服务中采用小写。 如果遇到任何与 Facebook 未找到正确产品有关的问题,请尝试使用 GET 请求手动获取产品,如此页面所示。

此外,还需要确保使用在流程中早些时候获得的值:

  • 包含要购买的物品的 PlayFab 定义目录的 CatalogID (IAP1_0)。

  • 游戏 PlayFab 目录中要购买的物品的 itemID (BigBagOfGold)

  • 在此过程开始时,从 StartPurchase 调用返回的 OrderID (1234567890ABCDEF)

在一个成功的响应中,Facebook 返回 payment_id,它随后可以作为对客户端 API 方法 PayForPurchase 的调用中的 ProviderTransactionId

const string paymentId = "_SOME_PAYMENT_ID" ;
PlayFabClientAPI.PayForPurchase(new PayForPurchaseRequest() {
    OrderId = "1234567890ABCDEF",
    ProviderName = "Facebook",
    Currency = "RM",
    ProviderTransactionId = paymentId
}, result => {
    // Handle success
}, error => {
    // Handle error
});

这应返回一个简单确认。

{
    "code": 200,
    "status": "OK",
    "data": {
        "OrderId": "1234567890ABCDEF",
        "Status": "Init",
        "PurchaseCurrency": "RM",
        "PurchasePrice": 999,
        "CreditApplied": 0
    }
}

这会完成循环,在公式两端将 PlayFab 连接到购买,从而使我们可以查询其状态。

可以在 Facebook 开发者门户上阅读有关 Facebook 付款系统的详细信息:https://developers.facebook.com/docs/payments/overview

Facebook 付款的另一个方面是要求游戏使用 Webhook 进行付款,以实现付款状态的实时更改通知。

这在 Facebook 模型中是必需的,因为付款在某些情况下可能需要很长时间才能完成。 幸运的是,这十分简单。 在 Facebook 的游戏设置中,输入以下内容作为回调 URL。

https://{{TitleId}}.playfabapi.com/ThirdPartyPayments/FacebookPaymentUpdate

其中 {{TitleId}} 是游戏在 PlayFab 中的游戏 ID(例如 https://aaa.playfabapi.com/ThirdPartyPayments/FacebookPaymentUpdate)。

无需在 Facebook 设置中指定令牌 - 只需设置回调 URL,在 Game ManagerFacebook Add-ons 页面上确认 PlayFab 设置正确无误,即准备就绪。

当 Facebook 使用此 Webhook 更新付款状态时,PlayFab 中的订单状态会更新。 如果成功,则会将相应物品添加到玩家物品栏中。

注意

安全性:请注意,此 API 会提示 PlayFab 再次调用 Facebook,并安全进行身份验证和验证交易。 它不是供黑客绕过 Facebook 付款的后门程序

将 Facebook 用作付款提供商时,客户端确认购买仍十分重要,如下所示。

建立付款提供商:非 Webhook 提供商

Steam

所有 付款提供商中,Steam 最易于集成。

PlayFabClientAPI.PayForPurchase(new PayForPurchaseRequest() {
    OrderId = "1234567890ABCDEF",
    ProviderName = "Steam",
    Currency = "RM"
}, result => {
    // Handle success
}, error => {
    // Handle error
});

作为 PayForPurchase 调用的结果,PlayFab 使用 Steam 提供的 Web 方法在该服务中发起购买。 这会使得通过 Steam 客户端向用户呈现购买确认对话框。

因此在收到客户端 API PayForPurchase 调用的响应的同时,系统会请用户接受付款。

{
    "code": 200,
    "status": "OK",
    "data": {
        "OrderId": "1234567890ABCDEF",
        "Status": "Init",
        "PurchaseCurrency": "RM",
        "PurchasePrice": 999,
        "CreditApplied": 0
    }
}

Steam 使用回调机制向游戏告知购买过程完成,因此需要为 MicroTxnAuthorizationResponse_t 回调注册回调处理程序。

已注册 Steam 开发者可以在以下位置获取有关此过程的进一步信息:https://partner.steamgames.com/documentation/MicroTxn#WebPurchasing

收到回调时,游戏应继续进行 ConfirmPurchase 调用,如最后一步:确认购买部分中所示。

PayPal

使用 PayPal 时,PayForPurchase 调用实际上与使用 Steam 时的情况相同。

PlayFabClientAPI.PayForPurchase(new PayForPurchaseRequest() {
    OrderId = "1234567890ABCDEF",
    ProviderName = "PayPal",
    Currency = "RM"
}, result => {
    // Handle success
}, error => {
    // Handle error
});

但是在此情况下,没有方便的客户端应用程序可将付款请求发送给玩家。

使用 PayPal 时,需要向玩家呈现 PayPal 界面,请他们验证是否同意购买。 不过在后台,此过程使用 PayPal 快速结帐,因为我们会处理所有细节。

只需向用户显示付款确认页面,这会作为 PurchaseConfirmationPageURL 返回。

{
    "code": 200,
    "status": "OK",
    "data": {
        "OrderId": "1234567890ABCDEF",
        "Status": "Init",
        "PurchaseCurrency": "RM",
        "PurchasePrice": 999,
        "PurchaseConfirmationPageURL": "https://...",
        "CreditApplied": 0
    }
}

在本例中,浏览器窗口关闭后,这应指示应转到在 PlayFab 中确认购买。

最后一步:确认购买

此过程中的最后一部分是 PlayFab 完成购买过程,从而将购买的物品(如果有)添加到玩家物品栏。

对于某些提供商,游戏在此阶段已了解购买是否成功完成。

在任何情况下,对客户端 API 方法 ConfirmPurchase 的调用只会在已付款且尚未添加物品的情况下才会将物品添加到玩家物品栏中。

PlayFabClientAPI.ConfirmPurchase(new ConfirmPurchaseRequest() {
    OrderId = "1234567890ABCDEF"
}, result => {
    // Handle success
}, error => {
    // Handle error
});

在这一点上,如果订单已经成功完成,我们将循环访问在调用客户端 API StartPurchase 时所设置的购物车中的物品。

这会将这些物品添加到玩家物品栏,并向游戏返回有关购买的物品的信息。

{
    "code": 200,
    "status": "OK",
    "data": {
        "OrderId": "1234567890ABCDEF",
        "Status": "Succeeded",
        "PurchaseDate": "2016-07-19T09:04:28Z",
        "Items": [{
            "ItemId": "BigBagOfGold",
            "ItemInstanceId": "ABCDEF1234567890",
            "CatalogVersion": "IAP1_0",
            "DisplayName": "A hefty sack of gold",
            "UnitCurrency": "RM",
            "UnitPrice": 999
        }]
    }
}

交易状态

在后台,订单会经历许多状态更改。 下面提供了完整列表。

注意

根据特定提供商过程,其中某些状态永远不会在所有三个购买过程 API 调用的结果中显示,在此处包含它们是为了完整性起见。

  • CreateCart - 虽然订单对象本身没有在对客户端 API StartPurchase 的响应中返回,但在此时已经创建了它。 在此阶段,物品和价格存储在该对象中,但是未提交给任何付款提供商。

  • Init - 这是在订单已提交给付款提供商,但是尚未获得玩家授权时返回的状态。

  • Approved - 在玩家批准付款之后,但在 PlayFab 完成将物品置于玩家物品栏这一过程之前短暂出现。 它包含在此列表中只是为了完整性起见,因为游戏中永远不会出现具有此状态的订单

  • Succeeded - 指示订单已成功完成。 玩家已批准付款,并且所有物品栏物品都已添加到该玩家的物品栏。

  • FailedByProvider - 如果提供商出于任何原因拒绝付款(包括玩家拒绝付款),提供商会将订单视为失败。

  • DisputePending - 付款提供商向 PlayFab 通知付款存在尚未解决的争议。

  • RefundPending - 付款提供商向 PlayFab 通知发出了退款请求。 在此阶段,实际上未进行退款。

  • Refunded - 订单已退款。 大多数(如果不是所有)付款提供商还会将此信息直接提供给开发者。 此状态可能因一些因素而异,例如物品是否是易耗品(可能已消耗)。

  • RefundFailed - 请求进行退款,但是付款提供商将其拒绝。 PlayFab 对此无法提供任何其他信息 - 因此如果需要更多详细信息,开发者应直接联系付款提供商。

  • ChargedBack - 此状态因付款提供商而异,可能指示订单存在争议或是已退单。 同样,应联系付款提供商以获取有关订单具体情况的详细信息。

  • FailedByPlayFab - 此状态指示付款过程中发生意外失败。 看到此状态响应时,游戏应重试上一个付款 API 调用。 如果此状态仍然存在,建议在我们的支持论坛上发布此问题,并提供有关问题的所有详细信息(如游戏 ID、PlayFab ID、订单 ID)。

使用 Xsolla 接受付款

对于 Xsolla,此流程有所不同。 它不使用上面的代码流,而是使用 Xsolla 付款流,如此处所述:https://developers.xsolla.com/doc/in-game-store/extensions/playfab-integration/

需要从 Xsolla 获得商家 ID、项目 ID 和商家 API 密钥,以便将游戏配置为使用其付款服务。

  • 若要开始进行,请使用 Add-ons Marketplace's Xsolla 页面中的链接注册 Xsolla 帐户。

  • 设置该配置页面后,下一步是在 Xsolla 服务中创建商品目录。

  • 验证 Xsolla 中每个物品的 item_code 是否与 PlayFab 目录中的物品 ID 匹配。 这些标识符用于完成购买并将正确的物品安全地添加到玩家物品栏。

  • 若要在 Xsolla 目录中设置虚拟物品列表,请参阅其文档(这些说明中之前已提供)。

发起购买 (Xsolla)

准备好进行购买后,可通过将 Xsolla 服务指定为 TokenProvider,使用 GetPaymentToken API 调用请求从该服务获得令牌。

PlayFabClientAPI.GetPaymentToken(new GetPaymentTokenRequest() {
    TokenProvider = "xsolla"
}, result => {
    // Handle success
}, error => {
    // Handle error
});

该调用返回 ProviderToken(这是在下一步中需要的 Xsolla 令牌)和 PlayFab OrderId。 在后台,此调用通过 Xsolla 发起交易。

{
    "code": 200,
    "status": "OK",
    "data": {
        "ProviderToken": "1234567890ABCDEF",
        "OrderId": "1234567890ABCDEF"
    }
}

完成购买 (Xsolla)

接下来,客户端需要将令牌传递给 Xsolla 以开始付款过程。 可以使用以下 URL 之一将该令牌传递给 Xsolla:

沙盒付款

https://sandbox-secure.xsolla.com/paystation3/?access_token=[TOKEN]

实时付款

 https://secure.xsolla.com/paystation3/?access_token=[TOKEN]

执行该操作后,可在 Xsolla 界面中完成购买。

同样,参考本说明中前面提供的链接中的 Xsolla 文档,了解有关使用其界面的任何问题。

完成后,无需进一步执行任何操作。 Xsolla 和 PlayFab 将交换 Webhook 调用。 PlayFab 将购买的物品置于玩家物品栏中并将相应事件发布到 PlayStream,从而完成购买。

可以通过轮询客户端 API GetPurchase 来检查交易状态 (请见上方的完整列表)。

在大多数情况下,此过程 2-3 秒内即可完成。

PlayFabClientAPI.GetPurchase(new GetPurchaseRequest()
{
    OrderId = "1234567890ABCDEF"
}, result =>
{
    // Handle success
}, error =>
{
    // Handle error
});
{
    "code": 200,
    "status": "OK",
    "data": {
        "OrderId": "1234567890ABCDEF",
        "PaymentProvider": "Xsolla",
        "TransactionStatus": "Succeeded",
        "PurchaseDate": "2017-06-07T00:00:00Z"
    }
}

最后提示

需要注意的一点是,使用某些提供商时,付款可能需要较长时间才能完成。

如果发现客户端 API ConfirmPurchase 调用的响应中的购买状态仍然是 Init,则应先进行等待,然后再重新查询。 这里的最佳做法是在过程开始时存储 OrderId,然后在查询结果时使用指数退避。

因此,如果状态是 Init,请在重新尝试调用客户端 API ConfirmPurchase 之前等待分钟,然后等待分钟,然后是分钟、分钟,等等,直到遇到某个最大值。 因而包含检查已完成交易选项可为用户提供一种方式来重置该计时器。

最后值得注意的是,对于某些付款提供商,可以将沙盒模式用于测试购买。

为此,请在 PlayFab Game Manager 的 Title’s Add-on 选项卡中选中 Use sandbox purchase testing 选项。