ผสานรวม Google Play for Native PC Billing Library เข้ากับแอป

สร้างรายได้จากเกมด้วยการขายผลิตภัณฑ์ดิจิทัลโดยใช้ Play Billing SDK มี API สำหรับแสดงผลิตภัณฑ์ที่พร้อมจำหน่าย เปิดขั้นตอนการซื้อ และ ประมวลผลการซื้อ การเรียก API การเรียกเก็บเงินเหล่านี้จะดำเนินการโดยใช้ บัญชี Google ที่เปิดเกมภายในไคลเอ็นต์ Google Play Games และไม่จำเป็นต้องมีขั้นตอนการลงชื่อเข้าใช้เพิ่มเติม

หากคุณผสานรวมกับ ไลบรารีการเรียกเก็บเงินของ Android Play API การเรียกเก็บเงินของ Play เหล่านี้ ควรดูคุ้นเคย เกมบน PC สามารถใช้การผสานรวมฝั่งเซิร์ฟเวอร์กับ Play Billing ซ้ำได้เนื่องจากเหมือนกันทั้งใน Android และ 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
  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 ไปยังเซิร์ฟเวอร์แบ็กเอนด์ ประมวลผลส่วนที่เหลือโดยใช้ API การเรียกเก็บเงินของ Play ฝั่งเซิร์ฟเวอร์ การผสานรวมฝั่งเซิร์ฟเวอร์นี้ เหมือนกับการผสานรวมสำหรับเกม Android ที่ผสานรวมกับ 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
}

ประมวลผลโดยไม่มีเซิร์ฟเวอร์แบ็กเอนด์

  1. ตรวจสอบว่าการชำระเงินของผู้ใช้ไม่ได้อยู่ระหว่างรอดำเนินการโดยไปที่ ProductPurchaseDetails::purchase_state PurchaseState::kPurchaseStatePurchased หากสถานะการซื้ออยู่ระหว่างรอดำเนินการ ให้แจ้งผู้ใช้ว่าต้องทำตาม ขั้นตอนเพิ่มเติมก่อนจึงจะรับผลิตภัณฑ์ที่ซื้อได้

  2. ให้สิทธิ์ผู้ใช้เข้าถึงผลิตภัณฑ์ที่ซื้อและอัปเดต ที่เก็บข้อมูลการให้สิทธิ์ของเกม

  3. สำหรับการซื้อไอเทมที่อยู่ตลอดไป (ผลิตภัณฑ์ที่ซื้อได้เพียงครั้งเดียว) ให้ตรวจสอบว่าได้รับข้อมูลการซื้อแล้วหรือยังโดยใช้ ProductPurchaseDetails::is_acknowledged

    1. หากยังไม่ได้รับการรับทราบการซื้อ ให้แจ้ง Google ว่าผู้ใช้ ได้รับสิทธิ์ใช้ผลิตภัณฑ์โดยการเรียกใช้ BillingClient::AcknowledgePurchase
  4. สำหรับการซื้อไอเทมที่ใช้แล้วหมดไป (ผลิตภัณฑ์ที่อาจซื้อได้มากกว่า 1 ครั้ง) ให้แจ้ง Google ว่าผู้ใช้ได้รับสิทธิ์เข้าถึงผลิตภัณฑ์โดย เรียกใช้ 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;
}

ขั้นตอนที่ 5: ทดสอบการผสานรวม

ตอนนี้คุณพร้อมที่จะทดสอบการผสานรวมกับ Play Billing แล้ว เราขอแนะนำให้ใช้ผู้ทดสอบที่ได้รับอนุญาตเพื่อทดสอบในระหว่าง ระยะการพัฒนา ผู้ทดสอบใบอนุญาต มีสิทธิ์เข้าถึงการชำระเงินทดสอบซึ่งจะหลีกเลี่ยงการเรียกเก็บเงินจริงสำหรับการซื้อ

ดูวิธีการตั้งค่าผู้ทดสอบใบอนุญาตและชุดการทดสอบด้วยตนเองที่เราแนะนำให้ทำได้ในเอกสารประกอบเกี่ยวกับวิธีทดสอบการผสานรวม Google Play Billing Library