การเรียกเก็บเงิน

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