Lập hóa đơn

API Thanh toán cho phép bạn bán hàng hoá kỹ thuật số và gói thuê bao. Trình bao bọc C# cung cấp một giao diện không đồng bộ và an toàn về kiểu cho Thư viện Google Play Billing cơ bản.

Không gian tên: PlayPcSdkManaged.Billing

Lớp ứng dụng: BillingClient

Tạo ứng dụng

Luôn sử dụng nhà máy để tạo BillingClient. Điều này đảm bảo rằng các lệnh gọi lại an toàn cho Unity sẽ được tự động đăng ký.

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

Truy vấn thông tin chi tiết về sản phẩm

Trước khi hiển thị các mặt hàng để bán, bạn phải truy vấn thông tin chi tiết như giá, tiêu đề và nội dung mô tả trên 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);
    }
}

Bắt đầu quy trình mua

Để bắt đầu giao dịch mua, hãy gọi LaunchPurchaseFlowAsync bằng OfferToken từ bước thông tin chi tiết về sản phẩm.

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

Truy vấn các giao dịch mua hiện có (Khôi phục)

Gọi QueryPurchasesAsync khi trò chơi khởi động để khôi phục các mặt hàng mà bạn đã sở hữu, chẳng hạn như sau khi cài đặt lại trò chơi hoặc để kiểm tra các giao dịch đang chờ xử lý.

Ví dụ này minh hoạ cách chuyển các giao dịch mua đến Acknowledge cho các mặt hàng vĩnh viễn hoặc Consume cho các mặt hàng tiêu hao.

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

Xác nhận giao dịch mua

Bạn phải xác nhận các mặt hàng không tiêu hao (ví dụ: "Bản nâng cấp cao cấp" hoặc "Gói cấp độ"). Điều này cho Google Play biết rằng bạn đã cấp mặt hàng đó cho người dùng.

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

Tiêu thụ giao dịch mua

Bạn phải tiêu thụ các mặt hàng tiêu hao, chẳng hạn như "100 viên ngọc" hoặc "Thuốc hồi máu" để cho phép mua lại.

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