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