Billing API ช่วยให้คุณขายสินค้าดิจิทัลและการสมัครใช้บริการได้ C# Wrapper มีอินเทอร์เฟซแบบไม่พร้อมกันที่ปลอดภัยต่อประเภทสำหรับ Google Play Billing Library ที่อยู่เบื้องหลัง
เนมสเปซ: PlayPcSdkManaged.Billing
คลาสไคลเอ็นต์: BillingClient
สร้างไคลเอ็นต์
ใช้โรงงานเพื่อสร้าง BillingClient เสมอ ซึ่งจะช่วยให้มั่นใจได้ว่าระบบจะลงทะเบียนการเรียกกลับที่ปลอดภัยสำหรับ Unity โดยอัตโนมัติ
using UnityEngine; using System; using System.Threading.Tasks; using System.Collections.Generic; // Required SDK Namespaces using PlayPcSdkManaged.Billing; using PlayPcSdkManaged.Unity; public class BillingManager : MonoBehaviour { private BillingClient _billingClient; public void SetupBilling() { try { // Creates the client with the required UnityBillingCallbacksHandler _billingClient = PlayPcSdkFactory.CreateBillingClient(); Debug.Log("Billing Client created successfully."); } catch (Exception ex) { Debug.LogError($"Failed to create Billing Client: {ex.Message}"); } } private void OnDestroy() { // Always dispose of the client to clean up native C++ resources _billingClient?.Dispose(); } }
ค้นหารายละเอียดผลิตภัณฑ์
ก่อนแสดงรายการเพื่อขาย คุณต้องค้นหารายละเอียด เช่น ราคา ชื่อ และ คำอธิบายจาก Google Play
public async Task GetProductDetailsAsync() { try { // Define the list of products you want to query var productList = new List{ // Use ProductType.InApp for consumables/non-consumables new ProductId { Id = "gem_pack_100", ProductType = ProductType.InApp }, // Use ProductType.Subs for subscriptions new ProductId { Id = "gold_subscription", ProductType = ProductType.InApp } }; var queryParams = new QueryProductDetailsParams { ProductIds = productList }; // Async call var result = await _billingClient.QueryProductDetailsAsync(queryParams); if (result.IsOk) { foreach (var product in result.Value.ProductDetailsList) { // The formatted price (e.g., "$0.99") is inside the ProductOffers list if (product.ProductOffers != null && product.ProductOffers.Count > 0) { var price = product.ProductOffers[0].FormattedPrice; var offerToken = product.ProductOffers[0].OfferToken; Debug.Log($"Product: {product.Title} | Price: {price} | Token: {offerToken}"); } } } else { Debug.LogError($"Query Failed: {result.Code} - {result.ErrorMessage}"); } } catch (Exception ex) { Debug.LogException(ex); } }
เปิดขั้นตอนการซื้อ
หากต้องการเริ่มการซื้อ ให้เรียกใช้ LaunchPurchaseFlowAsync ด้วย OfferToken
จากขั้นตอนรายละเอียดผลิตภัณฑ์
public async Task BuyItemAsync(string offerToken) { try { var purchaseParams = new LaunchPurchaseFlowParams { OfferToken = offerToken, Quantity = 1, // Optional: Attach obfuscated IDs for fraud detection ObfuscatedAccountId = "user_12345_hash", ObfuscatedProfileId = "profile_abcde_hash" }; var result = await _billingClient.LaunchPurchaseFlowAsync(purchaseParams); if (result.IsOk) { var purchase = result.Value.ProductPurchaseDetails; Debug.Log($"Purchase Successful! Order ID: {purchase.OrderId}"); // IMPORTANT: You must now Acknowledge or Consume this purchase. // If you don't, Google Play will refund the user after a few days. if (!purchase.IsAcknowledged) { // Decide based on your game logic if it's consumable or permanent await HandlePurchaseAsync(purchase); } } else if (result.Code == BillingError.UserCanceled) { Debug.Log("User canceled the purchase flow."); } else { Debug.LogError($"Purchase Failed: {result.Code} - {result.ErrorMessage}"); } } catch (Exception ex) { Debug.LogException(ex); } }
ค้นหาการซื้อที่มีอยู่ (กู้คืน)
เรียกใช้ QueryPurchasesAsync เมื่อเริ่มเกมเพื่อคืนค่าไอเทมที่คุณเป็นเจ้าของอยู่แล้ว เช่น หลังจากติดตั้งเกมใหม่ หรือเพื่อตรวจสอบธุรกรรมที่รอดำเนินการ
ตัวอย่างนี้แสดงวิธีกำหนดเส้นทางการซื้อไปยัง Acknowledge สำหรับ รายการถาวรหรือ Consume สำหรับไอเทมที่ใช้แล้วหมด
public async Task CheckExistingPurchasesAsync() { try { // Fetches all purchases owned by the user var result = await _billingClient.QueryPurchasesAsync(); if (result.IsOk) { foreach (var purchase in result.Value.ProductPurchaseDetails) { Debug.Log($"User owns: {purchase.ProductId} | State: {purchase.PurchaseState}"); // Process any purchase that hasn't been acknowledged yet if (purchase.PurchaseState == PurchaseState.Purchased && !purchase.IsAcknowledged) { await HandlePurchaseAsync(purchase); } } } else { Debug.LogError($"Restore Failed: {result.Code} - {result.ErrorMessage}"); } } catch (Exception ex) { Debug.LogException(ex); } } // Helper method to route purchases private async Task HandlePurchaseAsync(ProductPurchaseDetails purchase) { // Example logic: "gem_pack" is consumable, everything else is permanent if (purchase.ProductId.Contains("gem_pack")) { await ConsumeItemAsync(purchase.PurchaseToken); } else { await AcknowledgeItemAsync(purchase.PurchaseToken); } }
รับทราบการซื้อ
ต้องมีการรับทราบไอเทมที่อยู่ตลอดไป (เช่น "การอัปเกรดแบบพรีเมียม" หรือ "แพ็กเลเวล") ซึ่งจะระบุให้ Google Play ทราบว่าคุณได้ให้ไอเทมแก่ ผู้ใช้แล้ว
public async Task AcknowledgeItemAsync(string purchaseToken) { try { var acknowledgeParams = new AcknowledgePurchaseParams { PurchaseToken = purchaseToken }; var result = await _billingClient.AcknowledgePurchaseAsync(acknowledgeParams); if (result.IsOk) { Debug.Log("Purchase Acknowledged. Usage rights granted permanently."); } else { Debug.LogError($"Acknowledge Failed: {result.Code} - {result.ErrorMessage}"); } } catch (Exception ex) { Debug.LogException(ex); } }
ใช้การซื้อ
ไอเทมระยะสั้น เช่น "อัญมณี 100 เม็ด" หรือ "ยาฟื้นพลัง" ต้องใช้เพื่ออนุญาตให้ซื้อซ้ำ
public async Task ConsumeItemAsync(string purchaseToken) { try { var consumeParams = new ConsumePurchaseParams { PurchaseToken = purchaseToken }; var result = await _billingClient.ConsumePurchaseAsync(consumeParams); if (result.IsOk) { Debug.Log("Item Consumed. User can buy it again."); // Add the gems/coins to the user's inventory here } else { Debug.LogError($"Consume Failed: {result.Code} - {result.ErrorMessage}"); } } catch (Exception ex) { Debug.LogException(ex); } }