将 Google Play 结算库集成到您的应用中

使用 Play 结算服务销售数字商品,通过游戏创收。该 SDK 提供了用于展示可供购买的商品、启动购买流程和处理购买交易的 API。调用这些结算系统 API 时,系统会使用在 Google Play 游戏客户端内启动游戏的 Google 账号,而无需执行任何其他登录步骤。

如果您已集成 Android Play 结算库,这些 Play 结算服务 API 应该会看起来很熟悉。由于 Android 和 PC 平台上的 Play 结算服务相同,因此 PC 游戏可以重复使用与 Play 结算服务的任何服务器端集成。

前提条件

第 1 步:查询之前的购买交易和在您的应用之外完成的购买交易

在应用启动或重新进入前台时,查询购买交易。这对于检测在您的游戏之外发生的购买交易或解锁对用户之前购买的商品的访问权限至关重要。

  1. 使用 BillingClient::QueryPurchases 查询购买交易。

  2. 继续处理购买交易

// Query for purchases when:
// - Application starts up
// - Application window re-enters the foreground
auto promise = std::make_shared<std::promise<QueryPurchasesResult>>();
billing_client.QueryPurchases([promise](QueryPurchasesResult result) {
   promise->set_value(std::move(result));
});

auto query_purchases_result = promise->get_future().get();
if (query_purchases_result.ok()) {
  auto purchases = query_purchases_result.value().product_purchase_details;
  // Process the purchases
} else {
  // Handle the error
}

第 2 步:显示可供购买的商品

您可以查询可售的商品并将其展示给用户了。在将商品展示给用户之前,查询商品详情是非常重要的一步,因为查询会返回本地化的商品信息。

在提供待售商品之前,检查用户是否尚未拥有该商品。如果用户的消耗型商品仍在其购买记录中,您必须先消耗掉该商品,然后用户才能再次购买。

  1. 使用 BillingClient::QueryProductDetails 查询商品详情。传入您在 Google Play 管理中心内注册的商品 ID。
  2. 呈现 ProductDetails,其中包含商品的本地化名称和优惠价格。
  3. 保留对商品的 offer_token 的引用。此方法用于启动优惠的购买流程。
QueryProductDetailsParams params;
params.product_ids.push_back({"example_costmetic_1", ProductType::kTypeInApp});
params.product_ids.push_back({"example_costmetic_1", ProductType::kTypeInApp});
params.product_ids.push_back({"example_battle_pass", ProductType::kTypeInApp});

auto promise = std::make_shared<std::promise<QueryProductDetailsResult>>();
billing_client.QueryProductDetails(params, [promise](QueryProductDetailsResult result) {
   promise->set_value(std::move(result));
});

auto query_product_details_result = promise->get_future().get();
if (query_product_details_result.ok()) {
   auto product_details = query_product_details_result.value().product_details;
   // Display the available products and their offers to the user
} else {
   // Handle the error
}

第 3 步:启动购买流程

当用户表现出购买您向其展示的商品的意图时,您就可以启动购买流程了。

  1. 首先,调用 BillingClient::LaunchPurchaseFlow()。传入查询商品详情时获取的 offer_token
  2. 购买交易完成后,系统会调用传入结果的接续函数。
  3. 如果成功,接续会包含 ProductPurchaseDetails。继续处理购买交易
LaunchPurchaseFlowParams params { product_offer.offer_token };

auto promise = std::make_shared<std::promise<LaunchPurchaseFlowResult>>();
billing_client.LaunchPurchaseFlow(params, [promise](LaunchPurchaseFlowResult result) {
   promise->set_value(std::move(result));
});
// The purchase flow has started and is now in progress.

auto launch_purchase_flow_result = promise->get_future().get();

// The purchase flow has now completed.
if (launch_purchase_flow_result.ok()) {
   auto purchase = launch_purchase_flow_result.value().product_purchase_details;
   // Process the purchase
} else if (launch_purchase_flow_result.code() == BillingError::kUserCanceled) {
   // Handle an error caused by the user canceling the purchase flow
} else {
   // Handle any other error codes
}

第 4 步:处理购买交易

使用后端服务器进行处理

对于具有后端服务器的游戏,请将 purchase_token 发送到后端服务器以完成处理。使用服务器端 Play 结算 API 完成剩余的处理。此服务器端集成与已与 Play 结算服务集成的 Android 游戏相同。

void ProcessPurchasesWithServer(std::vector<ProductPurchaseDetails> purchases) {
   std::vector<std::string> purchase_tokens;
   for (const auto& purchase : purchases) {
      purchase_tokens.push_back(purchase.purchase_token);
   }

   // Send purchase tokens to backend server for processing
}

无需后端服务器即可处理

  1. 检查 ProductPurchaseDetails::purchase_state 是否为 PurchaseState::kPurchaseStatePurchased,确保用户的付款未待处理。如果购买交易状态为“待处理”,请通知用户需要完成其他步骤才能收到所购商品。

  2. 向用户授予对所购商品的访问权限,并更新游戏的权限存储空间。

  3. 对于非消耗型购买交易(可能只能购买一次的商品),请使用 ProductPurchaseDetails::is_acknowledged 检查是否已确认购买交易。

    1. 如果购买交易尚未确认,请通过调用 BillingClient::AcknowledgePurchase 通知 Google 向用户授予对相应商品的使用权。
  4. 对于消耗型购买交易(可多次购买的商品),请通过调用 BillingClient::ConsumePurchase 通知 Google 向用户授予该商品的使用权。

void ProcessPurchasesWithoutServer(std::vector<ProductPurchaseDetails> purchases) {
   std::vector<std::string> entitled_product_ids;
   for (const auto& purchase : purchases) {
      auto was_successful = ProcessPurchasePurchaseWithoutServer(purchase);
      if (was_successful) {
         entitled_product_ids.push_back(purchase.product_id);
      }
   }

   // Note that non-consumable products that were previously purchased may have
   // been refunded. These purchases will stop being returned by
   // `QueryPurchases()`. If your game has given a user access to one of these
   // products storage they should be revoked.
   //
   // ...
}

bool ProcessPurchasePurchaseWithoutServer(ProductPurchaseDetails purchase) {
   auto is_purchase_completed =
      purchase.purchase_state == PurchaseState::kPurchaseStatePurchased;
   if (!is_purchase_completed) {
      // Notify the user that they need to take additional steps to complete
      // this purchase.
      return false;
   }

   // Determine if the product ID is associated with a consumable product.
   auto is_consumable = IsConsumableProductId(purchase.product_id);
   if (is_consumable) {
      // Grant an entitlement to the product to the user.
      // ...
      // Then, notify Google by consuming the purchase.

      ConsumePurchaseParams params { purchase.purchase_token };
      auto promise = std::make_shared<std::promise<ConsumePurchaseResult>>();
      billing_client.ConsumePurchase(params, [promise](ConsumePurchaseResult result) {
         promise->set_value(std::move(result));
      });

      auto consume_purchase_result = promise->get_future().get();
      if (!consume_purchase_result.ok()) {
         // Examine the failure code & message for more details & notify user
         // of failure.
         // ...
         return false;
      }

      return true;
   }

   // Otherwise the product is assumed to be a non-consumable.

   // Grant an entitlement to the product to the user.
   // ...
   // Then, notify Google by acknowledging the purchase (if not already done).

   if (purchase.is_acknowledged) {
      return true;
   }

   AcknowledgePurchaseParams params { purchase.purchase_token };
   auto promise = std::make_shared<std::promise<AcknowledgePurchaseResult>>();
   billing_client.AcknowledgePurchase(params, [promise](AcknowledgePurchaseResult result) {
      promise->set_value(std::move(result));
   });

   auto acknowledge_purchase_result = promise->get_future().get();
   if (!acknowledge_purchase_result.ok()) {
      // Examine the failure code & message for more details & notify user
      // of failure.
      // ...
      return false;
   }

   return true;
}

第 5 步:测试集成

现在,您可以测试与 Play 结算服务的集成了。如需在开发阶段进行测试,我们建议您让许可测试人员进行测试。许可测试人员可以使用测试用付款方式,该方式不会针对测试人员的购买交易收取真实费用。

如需了解如何设置许可测试人员和一套我们建议执行的手动测试,请参阅有关如何测试 Google Play 结算库集成的文档。