394 lines
12 KiB
C#
394 lines
12 KiB
C#
using System;
|
|
using UnityEngine;
|
|
using UnityEngine.Purchasing;
|
|
using System.Collections;
|
|
using UnityEngine.Purchasing.Extension;
|
|
using System.Threading.Tasks;
|
|
|
|
public class IAPManager : MonoBehaviour, IDetailedStoreListener
|
|
{
|
|
private static IStoreController storeController;
|
|
private static IExtensionProvider storeExtensionProvider;
|
|
|
|
public const string PRODUCT_1000_COINS = "1000_coins";
|
|
public const string PRODUCT_2150_COINS = "2150_coins";
|
|
public const string PRODUCT_4350_COINS = "4350_coins";
|
|
public const string PRODUCT_6700_COINS = "6700_coins";
|
|
public const string PRODUCT_11500_COINS = "11500_coins";
|
|
|
|
private CoinWalletSystem coinWalletSystem;
|
|
private StoreUIManager storeUIManager;
|
|
|
|
[SerializeField] private GameObject purchaseLoadingUI;
|
|
[SerializeField] private GameObject purchaseSuccessUI;
|
|
[SerializeField] private GameObject purchaseFailedUI;
|
|
|
|
private void Start()
|
|
{
|
|
coinWalletSystem = CoinWalletSystem.Instance;
|
|
storeUIManager = FindAnyObjectByType<StoreUIManager>();
|
|
|
|
|
|
// Deactivate both UIs initially
|
|
if (storeUIManager != null) storeUIManager.gameObject.SetActive(false);
|
|
|
|
|
|
// Check if in development (Editor) or production (build)
|
|
#if UNITY_EDITOR
|
|
// In development, show fake store UI
|
|
|
|
#else
|
|
// In production, show real store UI
|
|
//if (storeUIManager != null) storeUIManager.gameObject.SetActive(true);
|
|
#endif
|
|
|
|
if (storeController == null)
|
|
{
|
|
InitializePurchasing();
|
|
}
|
|
}
|
|
|
|
private void InitializePurchasing()
|
|
{
|
|
if (IsInitialized()) return;
|
|
|
|
try
|
|
{
|
|
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
|
|
builder.AddProduct(PRODUCT_1000_COINS, ProductType.Consumable);
|
|
builder.AddProduct(PRODUCT_2150_COINS, ProductType.Consumable);
|
|
builder.AddProduct(PRODUCT_4350_COINS, ProductType.Consumable);
|
|
builder.AddProduct(PRODUCT_6700_COINS, ProductType.Consumable);
|
|
builder.AddProduct(PRODUCT_11500_COINS, ProductType.Consumable);
|
|
|
|
Debug.Log("Initializing Unity Purchasing...");
|
|
UnityPurchasing.Initialize(this, builder);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError($"Error initializing IAP: {e.Message}");
|
|
}
|
|
}
|
|
|
|
private bool IsInitialized() => storeController != null && storeExtensionProvider != null;
|
|
|
|
public void Buy1000Coins() { BuyProduct(PRODUCT_1000_COINS, 1000); }
|
|
public void Buy2150Coins() { BuyProduct(PRODUCT_2150_COINS, 2150); }
|
|
public void Buy4350Coins() { BuyProduct(PRODUCT_4350_COINS, 4350); }
|
|
public void Buy6700Coins() { BuyProduct(PRODUCT_6700_COINS, 6700); }
|
|
public void Buy11500Coins() { BuyProduct(PRODUCT_11500_COINS, 11500); }
|
|
|
|
public void BuyProduct(string productID, int coinsToAdd)
|
|
{
|
|
try
|
|
{
|
|
ShowPurchaseLoading(true);
|
|
Debug.Log($"Attempting to purchase {coinsToAdd} coins");
|
|
|
|
if (coinWalletSystem == null)
|
|
{
|
|
coinWalletSystem = CoinWalletSystem.Instance;
|
|
if (coinWalletSystem == null)
|
|
{
|
|
Debug.LogError("CoinWalletSystem is not initialized!");
|
|
ShowPurchaseResult(false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if in development (Editor) or production (build)
|
|
#if UNITY_EDITOR
|
|
// In development mode, simulate the purchase
|
|
Debug.Log($"Development mode: Simulating purchase of {coinsToAdd} coins");
|
|
SimulatePurchase(coinsToAdd);
|
|
#else
|
|
// In production mode, trigger the real IAP purchase flow
|
|
if (IsInitialized())
|
|
{
|
|
Product product = storeController.products.WithID(productID);
|
|
if (product != null && product.availableToPurchase)
|
|
{
|
|
storeController.InitiatePurchase(product);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("Product not available for purchase: " + productID);
|
|
ShowPurchaseResult(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("In-App Purchasing is not initialized.");
|
|
ShowPurchaseResult(false);
|
|
}
|
|
#endif
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Debug.LogError($"Error purchasing coins: {e.Message}");
|
|
ShowPurchaseResult(false);
|
|
}
|
|
finally
|
|
{
|
|
ShowPurchaseLoading(false);
|
|
}
|
|
}
|
|
|
|
// Add this new method to handle development mode purchases
|
|
private async void SimulatePurchase(int coinsToAdd)
|
|
{
|
|
if (!gameObject.activeInHierarchy)
|
|
{
|
|
gameObject.SetActive(true);
|
|
}
|
|
|
|
try
|
|
{
|
|
Debug.Log($"Simulating purchase of {coinsToAdd} coins");
|
|
ShowPurchaseLoading(true);
|
|
|
|
// Simulate a short delay to mimic network request
|
|
await Task.Delay(1000);
|
|
|
|
// Add coins directly in development mode
|
|
await coinWalletSystem.AddCoins(coinsToAdd);
|
|
|
|
// Ensure the game object is still active before showing the result
|
|
if (gameObject != null && gameObject.activeInHierarchy)
|
|
{
|
|
ShowPurchaseResult(true);
|
|
}
|
|
|
|
Debug.Log($"Development mode: Successfully added {coinsToAdd} coins");
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError($"Development mode: Failed to simulate purchase: {e.Message}");
|
|
if (gameObject != null && gameObject.activeInHierarchy)
|
|
{
|
|
ShowPurchaseResult(false);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (gameObject != null && gameObject.activeInHierarchy)
|
|
{
|
|
ShowPurchaseLoading(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Called when a purchase completes
|
|
public void OnPurchaseComplete(Product product)
|
|
{
|
|
try
|
|
{
|
|
int coinsToAdd = 0;
|
|
|
|
// Determine the number of coins based on the product ID
|
|
if (product.definition.id == PRODUCT_1000_COINS)
|
|
{
|
|
coinsToAdd = 1000;
|
|
}
|
|
else if (product.definition.id == PRODUCT_2150_COINS)
|
|
{
|
|
coinsToAdd = 2150;
|
|
}
|
|
else if (product.definition.id == PRODUCT_4350_COINS)
|
|
{
|
|
coinsToAdd = 4350;
|
|
}
|
|
else if (product.definition.id == PRODUCT_6700_COINS)
|
|
{
|
|
coinsToAdd = 6700;
|
|
}
|
|
else if (product.definition.id == PRODUCT_11500_COINS)
|
|
{
|
|
coinsToAdd = 11500;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("Unrecognized product ID.");
|
|
ShowPurchaseResult(false);
|
|
return;
|
|
}
|
|
|
|
// Add the coins to the wallet and show the result
|
|
if (coinWalletSystem != null)
|
|
{
|
|
coinWalletSystem.AddCoins(coinsToAdd);
|
|
ShowPurchaseResult(true); // Show success UI
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("CoinWalletSystem not found!");
|
|
ShowPurchaseResult(false); // Show failure UI
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError($"Error processing purchase: {e.Message}");
|
|
ShowPurchaseResult(false); // Show failure UI
|
|
}
|
|
}
|
|
|
|
|
|
// Called when a purchase fails
|
|
public void OnPurchaseFailed(Product product, PurchaseFailureDescription failureDescription)
|
|
{
|
|
Debug.LogError($"Purchase failed for {product.definition.id}: {failureDescription.message}");
|
|
ShowPurchaseResult(false);
|
|
}
|
|
|
|
// This method simulates showing the store UI in the development build
|
|
private void ShowFakeStore(string productID, int coinsToAdd)
|
|
{
|
|
// Show the fake store UI or simulate the purchase process
|
|
Debug.Log($"Fake store: ready to purchase {coinsToAdd} coins.");
|
|
// You may trigger the "Buy" button in the fake store UI to simulate a successful purchase
|
|
}
|
|
|
|
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
|
|
{
|
|
if (!gameObject.activeInHierarchy)
|
|
{
|
|
gameObject.SetActive(true);
|
|
}
|
|
|
|
Debug.Log($"Processing purchase for product: {args.purchasedProduct.definition.id}");
|
|
|
|
if (coinWalletSystem == null)
|
|
{
|
|
coinWalletSystem = CoinWalletSystem.Instance;
|
|
}
|
|
|
|
string productId = args.purchasedProduct.definition.id;
|
|
int coinsToAdd = 0;
|
|
|
|
switch (productId)
|
|
{
|
|
case PRODUCT_1000_COINS: coinsToAdd = 1000; break;
|
|
case PRODUCT_2150_COINS: coinsToAdd = 2150; break;
|
|
case PRODUCT_4350_COINS: coinsToAdd = 4350; break;
|
|
case PRODUCT_6700_COINS: coinsToAdd = 6700; break;
|
|
case PRODUCT_11500_COINS: coinsToAdd = 11500; break;
|
|
default:
|
|
Debug.LogError($"Unrecognized product ID {productId}");
|
|
return PurchaseProcessingResult.Complete;
|
|
}
|
|
|
|
if (coinsToAdd > 0 && coinWalletSystem != null)
|
|
{
|
|
// Use async/await pattern with Task.Run to handle the async operation
|
|
StartCoroutine(ProcessPurchaseAsync(coinsToAdd));
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("Failed to add coins: CoinWalletSystem not found or coins amount is 0");
|
|
ShowPurchaseResult(false);
|
|
}
|
|
|
|
return PurchaseProcessingResult.Complete;
|
|
}
|
|
|
|
private IEnumerator ProcessPurchaseAsync(int coinsToAdd)
|
|
{
|
|
ShowPurchaseLoading(true);
|
|
|
|
var task = coinWalletSystem.AddCoins(coinsToAdd);
|
|
while (!task.IsCompleted)
|
|
{
|
|
yield return null;
|
|
}
|
|
|
|
if (task.Exception == null)
|
|
{
|
|
ShowPurchaseResult(true);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError($"Failed to process purchase: {task.Exception}");
|
|
ShowPurchaseResult(false);
|
|
}
|
|
|
|
ShowPurchaseLoading(false);
|
|
}
|
|
|
|
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
|
|
{
|
|
storeController = controller;
|
|
storeExtensionProvider = extensions;
|
|
}
|
|
|
|
public void OnInitializeFailed(InitializationFailureReason error)
|
|
{
|
|
Debug.LogError($"IAP initialization failed: {error.ToString()}");
|
|
}
|
|
|
|
public void OnInitializeFailed(InitializationFailureReason error, string message)
|
|
{
|
|
Debug.LogError($"IAP initialization failed: {error.ToString()}, Message: {message}");
|
|
}
|
|
|
|
// Add the BuyProductID method here
|
|
public void BuyProductID(string productID)
|
|
{
|
|
if (IsInitialized())
|
|
{
|
|
Product product = storeController.products.WithID(productID);
|
|
if (product != null && product.availableToPurchase)
|
|
{
|
|
storeController.InitiatePurchase(product);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("Product not available for purchase: " + productID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("In-App Purchasing is not initialized.");
|
|
}
|
|
}
|
|
|
|
private void ShowPurchaseLoading(bool show)
|
|
{
|
|
if (purchaseLoadingUI != null)
|
|
purchaseLoadingUI.SetActive(show);
|
|
}
|
|
|
|
private void ShowPurchaseResult(bool success)
|
|
{
|
|
if (!gameObject.activeInHierarchy) return;
|
|
|
|
if (success && purchaseSuccessUI != null)
|
|
{
|
|
purchaseSuccessUI.SetActive(true);
|
|
StartCoroutine(HideUI(purchaseSuccessUI, 2f));
|
|
}
|
|
else if (!success && purchaseFailedUI != null)
|
|
{
|
|
purchaseFailedUI.SetActive(true);
|
|
StartCoroutine(HideUI(purchaseFailedUI, 2f));
|
|
}
|
|
}
|
|
|
|
private IEnumerator HideUI(GameObject ui, float duration)
|
|
{
|
|
if (ui == null) yield break;
|
|
|
|
yield return new WaitForSeconds(duration);
|
|
|
|
if (ui != null && gameObject != null && gameObject.activeInHierarchy)
|
|
{
|
|
ui.SetActive(false);
|
|
}
|
|
}
|
|
|
|
public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
|
|
{
|
|
Debug.LogError($"Purchase failed - Product: {product.definition.id}, Reason: {failureReason}");
|
|
ShowPurchaseResult(false);
|
|
}
|
|
}
|