Google Play for Native PC Billing Library をアプリに統合する

Play 請求サービスを使用してデジタル商品を販売し、ゲームを収益化しましょう。この SDK には、購入可能な商品を表示する API、購入フローを開始する API、購入を処理する API が用意されています。これらの課金システム API の呼び出しは、Google Play Games クライアント内でゲームを起動した Google アカウントを使用して行われるため、追加のログイン手順は必要ありません。

Android Play Billing Library を統合している場合は、これらの Play Billing API は使い慣れているはずです。Android と PC の両方で同じであるため、Google Play の課金システムとのサーバーサイド統合は、PC タイトルで再利用できます。

前提条件

ステップ 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 Console で登録した商品 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 をバックエンド サーバーに送信して処理を完了します。サーバーサイドの Google 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_statePurchaseState::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 Billing Library の統合をテストする方法に関するドキュメントをご覧ください。