using System; using System.Collections; using System.Collections.Generic; using System.Threading; using UnityEngine; using UnityEngine.Purchasing; using UnityEngine.Purchasing.Security; namespace CIG { public class IAPStore : IStoreListener where T : StoreProduct { public delegate void InitializedEventHandler(); public delegate void PurchaseSuccessEventHandler(T purchase); public delegate void PurchaseFailedEventHandler(T product, IAPStorePurchaseFailedError error); public delegate void PurchaseDeferredEventHandler(T product); private static readonly Dictionary _purchaseFailureReasonMapping = new Dictionary { { PurchaseFailureReason.PurchasingUnavailable, IAPStorePurchaseFailedError.PurchasingUnavailable }, { PurchaseFailureReason.ExistingPurchasePending, IAPStorePurchaseFailedError.ExistingPurchasePending }, { PurchaseFailureReason.ProductUnavailable, IAPStorePurchaseFailedError.ProductUnavailable }, { PurchaseFailureReason.SignatureInvalid, IAPStorePurchaseFailedError.SignatureInvalid }, { PurchaseFailureReason.UserCancelled, IAPStorePurchaseFailedError.UserCancelled }, { PurchaseFailureReason.PaymentDeclined, IAPStorePurchaseFailedError.PaymentDeclined }, { PurchaseFailureReason.DuplicateTransaction, IAPStorePurchaseFailedError.DuplicateTransaction }, { PurchaseFailureReason.Unknown, IAPStorePurchaseFailedError.Unknown } }; private CIGWebService _webService; private IAPValidationManager _iapValidator; private IAPCatalog _iapCatalog; private IStoreController _storeController; private List _unconsumedPurchases = new List(); private List _unconsumedProducts = new List(); public T[] UnconsumedPurchases => _unconsumedPurchases.ToArray(); public T[] Products => _iapCatalog.AvailableProducts; public IAPStoreError LoadingError { get; private set; } public event InitializedEventHandler InitializedEvent; public event PurchaseSuccessEventHandler PurchaseSuccessEvent; public event PurchaseFailedEventHandler PurchaseFailedEvent; public event PurchaseDeferredEventHandler PurchaseDeferredEvent; public IAPStore(CIGWebService webService, Func, T> storeProductFactory) { _webService = webService; _iapValidator = new IAPValidationManager(webService); _iapCatalog = new IAPCatalog(webService, storeProductFactory); LoadingError = IAPStoreError.ProductsNotLoaded; } private void FireInitializedEvent() { if (this.InitializedEvent != null) { this.InitializedEvent(); } } public void FirePurchaseSuccessEvent(T Product) { if (this.PurchaseSuccessEvent != null) { this.PurchaseSuccessEvent(Product); } } private void FirePurchaseFailedEvent(T product, IAPStorePurchaseFailedError error) { if (this.PurchaseFailedEvent != null) { this.PurchaseFailedEvent(product, error); } } private void FirePurchaseDeferredEvent(T product) { if (this.PurchaseDeferredEvent != null) { this.PurchaseDeferredEvent(product); } } public IEnumerator LoadProducts(bool forceReload = false) { if (LoadingError == IAPStoreError.ProductsNotLoaded || forceReload) { LoadingError = IAPStoreError.ProductsNotLoaded; yield return _iapCatalog.UpdatePricePoints(); LoadingError = IAPStoreError.StoreInitializing; InitializeStoreController(); } } public void OnInitialized(IStoreController controller, IExtensionProvider extensions) { _storeController = controller; if (Application.platform == RuntimePlatform.IPhonePlayer) { IAppleExtensions extension = extensions.GetExtension(); extension.RegisterPurchaseDeferredListener(OnPurchaseDeferred); } _iapCatalog.UpdateProducts(_storeController.products.all); LoadingError = IAPStoreError.None; UnityEngine.Debug.Log("IAPStore Initialized successfully."); FireInitializedEvent(); } public void OnInitializeFailed(InitializationFailureReason error) { switch (error) { case InitializationFailureReason.AppNotKnown: UnityEngine.Debug.LogError("Is your App correctly uploaded on the relevant publisher console?"); LoadingError = IAPStoreError.AppNotKnown; break; case InitializationFailureReason.PurchasingUnavailable: UnityEngine.Debug.LogError("Billing disabled!"); LoadingError = IAPStoreError.PurchasingUnavailable; break; case InitializationFailureReason.NoProductsAvailable: UnityEngine.Debug.LogError("No products available for purchase!"); LoadingError = IAPStoreError.NoProductsAvailable; break; default: UnityEngine.Debug.LogErrorFormat("Unknown Store Initialization Failure Reason: {0}", error.ToString()); LoadingError = IAPStoreError.StoreInitializeFailed; break; } FireInitializedEvent(); } public void InitiatePurchase(T product) { if (product == null) { UnityEngine.Debug.LogError("Cannot purchase a product which is null."); FirePurchaseFailedEvent(product, IAPStorePurchaseFailedError.ProductNull); return; } if (LoadingError != 0) { UnityEngine.Debug.LogErrorFormat("Store wasn't loaded correctly. Loading Error: {0}", LoadingError.ToString()); FirePurchaseFailedEvent(product, IAPStorePurchaseFailedError.StoreLoadingError); return; } if (_storeController == null) { UnityEngine.Debug.LogError("Store Controller unavailable."); FirePurchaseFailedEvent(product, IAPStorePurchaseFailedError.StoreNull); return; } if (_webService != null) { _webService.PurchaseIntent(product.Identifier); } UnityEngine.Debug.LogFormat("Going to start purchase of '{0}'.", product.Identifier); _storeController.InitiatePurchase(product.Identifier); } public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e) { T val = FindStoreProduct(e.purchasedProduct); if (val == null) { UnityEngine.Debug.LogErrorFormat("Couldn't find StoreProduct for '{0}' after purchasing was success!", e.purchasedProduct.definition.id); return PurchaseProcessingResult.Pending; } Purchase purchase = new Purchase(val, e.purchasedProduct.transactionID); //if (!_unconsumedPurchases.Exists((Purchase p) => p.TransactionID == purchase.TransactionID)) //{ // //_unconsumedPurchases.Add(purchase); // _unconsumedProducts.Add(e.purchasedProduct); // _iapValidator.AddPurchase(val.Identifier, e.purchasedProduct.transactionID, e.purchasedProduct.receipt); // UnityEngine.Debug.LogFormat("Purchase of {0} successfull!", e.purchasedProduct.definition.id); // FirePurchaseSuccessEvent(purchase); //} return PurchaseProcessingResult.Pending; } public void OnPurchaseFailed(Product product, PurchaseFailureReason error) { if (error != PurchaseFailureReason.UserCancelled) { UnityEngine.Debug.LogErrorFormat("Failed to purchase {0}: {1}", product.definition.id, error.ToString()); } IAPStorePurchaseFailedError value; if (!_purchaseFailureReasonMapping.TryGetValue(error, out value)) { value = IAPStorePurchaseFailedError.Unknown; UnityEngine.Debug.LogErrorFormat("Missing Purchase Failure Reason '{0}' mapping", error.ToString()); } FirePurchaseFailedEvent(FindStoreProduct(product), value); } public void ValidatePurchases() { _iapValidator.ValidatePurchases(); } public void MarkPurchaseAsConsumed(T Product) { //if (!_unconsumedPurchases.Contains(Product)) //{ // object[] array = new object[2]; // T product = Product; // array[0] = product.Identifier; // array[1] = ""; // UnityEngine.Debug.LogErrorFormat("Cannot mark Purchase of {0} with TransactionID {1} as consumed: purchase not found in the unconsumed list!", array); // return; //} //Product product2 = _unconsumedProducts.Find((Product x) => x.transactionID == purchase.TransactionID); //if (product2 == null) //{ // object[] array2 = new object[2]; // T product3 = Product; // array2[0] = product3.Identifier; // array2[1] = ""; // UnityEngine.Debug.LogErrorFormat("Cannot mark purchase of {0} with transactionID {1} as consumed: it has no linked Product!", array2); //} //else //{ //_unconsumedPurchases.Remove(Product); //_unconsumedProducts.Remove(product2); //_storeController.ConfirmPendingPurchase(product2); //} } public T FindProduct(string identifier) { return _iapCatalog.FindProduct(identifier); } public T FindProduct(Predicate predicate) { return _iapCatalog.FindProduct(predicate); } public T[] GetProducts(Predicate predicate) { return _iapCatalog.GetAvailableProducts(predicate); } private void OnPurchaseDeferred(Product product) { UnityEngine.Debug.LogFormat("Purchase Deferred {0}", product.definition.id); FirePurchaseDeferredEvent(FindStoreProduct(product)); } private void InitializeStoreController() { ConfigurationBuilder standardConfigurationBuilder = GetStandardConfigurationBuilder(); T[] availableProducts = _iapCatalog.AvailableProducts; int num = availableProducts.Length; for (int i = 0; i < num; i++) { string identifier = availableProducts[i].Identifier; standardConfigurationBuilder.AddProduct(identifier, ProductType.Consumable); } UnityPurchasing.Initialize(this, standardConfigurationBuilder); } private ConfigurationBuilder GetStandardConfigurationBuilder() { StandardPurchasingModule first = StandardPurchasingModule.Instance(); return ConfigurationBuilder.Instance(first); } private T FindStoreProduct(Product product) { if (LoadingError != 0) { return (T)null; } return _iapCatalog.FindProduct(product.definition.id); } } }