Integrate the Google Play Billing Library into your app

Monetize your game by selling digital products using Play Billing. The SDK offers APIs to show products available to buy, launch the purchase flow, and process purchases. Calls to these billing APIs are performed using the Google Account that launched the game inside of the Google Play Games client and don't require any additional sign-in steps.

If you have integrated with the Android Play Billing library, these Play Billing APIs should look familiar. Any server-side integrations with Play Billing can be reused by PC titles as they are the same across both Android & PC.

Prerequisites

Step 1: Query for previous purchases & purchases completed outside of your application

When your application starts up or when it re-enters the foreground, query for purchases. This is necessary to detect purchases that occurred outside of your game or to unlock access to purchases previously made by the user.

  1. Query for purchases using BillingClient::QueryPurchases.

  2. Continue by processing the purchases.

// 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
}

Step 2: Show products available to buy

You are ready to query for your available products and display them to your users. Querying for product details is an important step before displaying your products to your users, as it returns localized product information.

Before offering a product for sale, check that the user does not already own the product. If the user has a consumable that is still in their purchase history, you must consume the product before they can buy it again.

  1. Query for product details using BillingClient::QueryProductDetails. Pass in the product IDs that you registered inside of the Google Play Console.
  2. Render the ProductDetails which includes the product's localized name & offer price.
  3. Keep a reference to the product's offer_token. This is used to launch a purchase flow for the offer.
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
}

Step 3: Launch a purchase flow

When the user shows an intent to buy a product you have showed them you're ready to launch the purchase flow.

  1. Start by calling BillingClient::LaunchPurchaseFlow(). Pass in the offer_token obtained when querying the product details.
  2. Once the purchase has been completed the continuation function will be called with the result.
  3. If successful, the continuation contains a ProductPurchaseDetails. Continue by processing the purchase.
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
}

Step 4: Process a purchase

Process with a backend server

For games with a backend server, complete the processing by sending the purchase_token to your backend server. Complete the remainder of processing using the server-side Play Billing APIs. This server-side integration is the same as done for an Android game that has integrated with Play Billing.

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
}

Process without a backend server

  1. Ensure the user's payment is not pending by checking ProductPurchaseDetails::purchase_state is PurchaseState::kPurchaseStatePurchased. If the purchase state is pending notify the user that they need to complete additional steps before they can receive their purchased product.

  2. Grant the user access to the purchased product & update your game's entitlement storage.

  3. For non-consumable purchases (products that may only ever be purchased once) check if the purchase has already been acknowledged using ProductPurchaseDetails::is_acknowledged.

    1. If the purchase has not been acknowledged notify Google that the user is being granted an entitlement to the product by calling BillingClient::AcknowledgePurchase.
  4. For consumable purchases (products that may be purchased more than once) notify Google that the user is being granted an entitlement to the product by calling BillingClient::ConsumePurchase.

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;
}

Step 5: Test your integration

You are now ready to test your integration with Play Billing. To test during the development phase, we recommend leveraging license testers. License testers have access to test payments that avoid charging real money for purchases.

For instructions on how to setup license testers and a suite of manual tests we recommend exercising see the documentation on how to test your Google Play Billing Library integration.