You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

322 lines
12 KiB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Threading;
  5. using UnityEngine;
  6. using UnityEngine.Purchasing;
  7. using UnityEngine.Purchasing.Security;
  8. namespace CIG
  9. {
  10. public class IAPStore<T> : IStoreListener where T : StoreProduct
  11. {
  12. public delegate void InitializedEventHandler();
  13. public delegate void PurchaseSuccessEventHandler(T purchase);
  14. public delegate void PurchaseFailedEventHandler(T product, IAPStorePurchaseFailedError error);
  15. public delegate void PurchaseDeferredEventHandler(T product);
  16. private static readonly Dictionary<PurchaseFailureReason, IAPStorePurchaseFailedError> _purchaseFailureReasonMapping = new Dictionary<PurchaseFailureReason, IAPStorePurchaseFailedError>
  17. {
  18. {
  19. PurchaseFailureReason.PurchasingUnavailable,
  20. IAPStorePurchaseFailedError.PurchasingUnavailable
  21. },
  22. {
  23. PurchaseFailureReason.ExistingPurchasePending,
  24. IAPStorePurchaseFailedError.ExistingPurchasePending
  25. },
  26. {
  27. PurchaseFailureReason.ProductUnavailable,
  28. IAPStorePurchaseFailedError.ProductUnavailable
  29. },
  30. {
  31. PurchaseFailureReason.SignatureInvalid,
  32. IAPStorePurchaseFailedError.SignatureInvalid
  33. },
  34. {
  35. PurchaseFailureReason.UserCancelled,
  36. IAPStorePurchaseFailedError.UserCancelled
  37. },
  38. {
  39. PurchaseFailureReason.PaymentDeclined,
  40. IAPStorePurchaseFailedError.PaymentDeclined
  41. },
  42. {
  43. PurchaseFailureReason.DuplicateTransaction,
  44. IAPStorePurchaseFailedError.DuplicateTransaction
  45. },
  46. {
  47. PurchaseFailureReason.Unknown,
  48. IAPStorePurchaseFailedError.Unknown
  49. }
  50. };
  51. private CIGWebService _webService;
  52. private IAPValidationManager _iapValidator;
  53. private IAPCatalog<T> _iapCatalog;
  54. private IStoreController _storeController;
  55. private List<T> _unconsumedPurchases = new List<T>();
  56. private List<Product> _unconsumedProducts = new List<Product>();
  57. public T[] UnconsumedPurchases => _unconsumedPurchases.ToArray();
  58. public T[] Products => _iapCatalog.AvailableProducts;
  59. public IAPStoreError LoadingError
  60. {
  61. get;
  62. private set;
  63. }
  64. public event InitializedEventHandler InitializedEvent;
  65. public event PurchaseSuccessEventHandler PurchaseSuccessEvent;
  66. public event PurchaseFailedEventHandler PurchaseFailedEvent;
  67. public event PurchaseDeferredEventHandler PurchaseDeferredEvent;
  68. public IAPStore(CIGWebService webService, Func<Dictionary<string, object>, T> storeProductFactory)
  69. {
  70. _webService = webService;
  71. _iapValidator = new IAPValidationManager(webService);
  72. _iapCatalog = new IAPCatalog<T>(webService, storeProductFactory);
  73. LoadingError = IAPStoreError.ProductsNotLoaded;
  74. }
  75. private void FireInitializedEvent()
  76. {
  77. if (this.InitializedEvent != null)
  78. {
  79. this.InitializedEvent();
  80. }
  81. }
  82. public void FirePurchaseSuccessEvent(T Product)
  83. {
  84. if (this.PurchaseSuccessEvent != null)
  85. {
  86. this.PurchaseSuccessEvent(Product);
  87. }
  88. }
  89. private void FirePurchaseFailedEvent(T product, IAPStorePurchaseFailedError error)
  90. {
  91. if (this.PurchaseFailedEvent != null)
  92. {
  93. this.PurchaseFailedEvent(product, error);
  94. }
  95. }
  96. private void FirePurchaseDeferredEvent(T product)
  97. {
  98. if (this.PurchaseDeferredEvent != null)
  99. {
  100. this.PurchaseDeferredEvent(product);
  101. }
  102. }
  103. public IEnumerator LoadProducts(bool forceReload = false)
  104. {
  105. if (LoadingError == IAPStoreError.ProductsNotLoaded || forceReload)
  106. {
  107. LoadingError = IAPStoreError.ProductsNotLoaded;
  108. yield return _iapCatalog.UpdatePricePoints();
  109. LoadingError = IAPStoreError.StoreInitializing;
  110. InitializeStoreController();
  111. }
  112. }
  113. public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
  114. {
  115. _storeController = controller;
  116. if (Application.platform == RuntimePlatform.IPhonePlayer)
  117. {
  118. IAppleExtensions extension = extensions.GetExtension<IAppleExtensions>();
  119. extension.RegisterPurchaseDeferredListener(OnPurchaseDeferred);
  120. }
  121. _iapCatalog.UpdateProducts(_storeController.products.all);
  122. LoadingError = IAPStoreError.None;
  123. UnityEngine.Debug.Log("IAPStore Initialized successfully.");
  124. FireInitializedEvent();
  125. }
  126. public void OnInitializeFailed(InitializationFailureReason error)
  127. {
  128. switch (error)
  129. {
  130. case InitializationFailureReason.AppNotKnown:
  131. UnityEngine.Debug.LogError("Is your App correctly uploaded on the relevant publisher console?");
  132. LoadingError = IAPStoreError.AppNotKnown;
  133. break;
  134. case InitializationFailureReason.PurchasingUnavailable:
  135. UnityEngine.Debug.LogError("Billing disabled!");
  136. LoadingError = IAPStoreError.PurchasingUnavailable;
  137. break;
  138. case InitializationFailureReason.NoProductsAvailable:
  139. UnityEngine.Debug.LogError("No products available for purchase!");
  140. LoadingError = IAPStoreError.NoProductsAvailable;
  141. break;
  142. default:
  143. UnityEngine.Debug.LogErrorFormat("Unknown Store Initialization Failure Reason: {0}", error.ToString());
  144. LoadingError = IAPStoreError.StoreInitializeFailed;
  145. break;
  146. }
  147. FireInitializedEvent();
  148. }
  149. public void InitiatePurchase(T product)
  150. {
  151. if (product == null)
  152. {
  153. UnityEngine.Debug.LogError("Cannot purchase a product which is null.");
  154. FirePurchaseFailedEvent(product, IAPStorePurchaseFailedError.ProductNull);
  155. return;
  156. }
  157. if (LoadingError != 0)
  158. {
  159. UnityEngine.Debug.LogErrorFormat("Store wasn't loaded correctly. Loading Error: {0}", LoadingError.ToString());
  160. FirePurchaseFailedEvent(product, IAPStorePurchaseFailedError.StoreLoadingError);
  161. return;
  162. }
  163. if (_storeController == null)
  164. {
  165. UnityEngine.Debug.LogError("Store Controller unavailable.");
  166. FirePurchaseFailedEvent(product, IAPStorePurchaseFailedError.StoreNull);
  167. return;
  168. }
  169. if (_webService != null)
  170. {
  171. _webService.PurchaseIntent(product.Identifier);
  172. }
  173. UnityEngine.Debug.LogFormat("Going to start purchase of '{0}'.", product.Identifier);
  174. _storeController.InitiatePurchase(product.Identifier);
  175. }
  176. public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
  177. {
  178. T val = FindStoreProduct(e.purchasedProduct);
  179. if (val == null)
  180. {
  181. UnityEngine.Debug.LogErrorFormat("Couldn't find StoreProduct for '{0}' after purchasing was success!", e.purchasedProduct.definition.id);
  182. return PurchaseProcessingResult.Pending;
  183. }
  184. Purchase<T> purchase = new Purchase<T>(val, e.purchasedProduct.transactionID);
  185. //if (!_unconsumedPurchases.Exists((Purchase<T> p) => p.TransactionID == purchase.TransactionID))
  186. //{
  187. // //_unconsumedPurchases.Add(purchase);
  188. // _unconsumedProducts.Add(e.purchasedProduct);
  189. // _iapValidator.AddPurchase(val.Identifier, e.purchasedProduct.transactionID, e.purchasedProduct.receipt);
  190. // UnityEngine.Debug.LogFormat("Purchase of {0} successfull!", e.purchasedProduct.definition.id);
  191. // FirePurchaseSuccessEvent(purchase);
  192. //}
  193. return PurchaseProcessingResult.Pending;
  194. }
  195. public void OnPurchaseFailed(Product product, PurchaseFailureReason error)
  196. {
  197. if (error != PurchaseFailureReason.UserCancelled)
  198. {
  199. UnityEngine.Debug.LogErrorFormat("Failed to purchase {0}: {1}", product.definition.id, error.ToString());
  200. }
  201. IAPStorePurchaseFailedError value;
  202. if (!_purchaseFailureReasonMapping.TryGetValue(error, out value))
  203. {
  204. value = IAPStorePurchaseFailedError.Unknown;
  205. UnityEngine.Debug.LogErrorFormat("Missing Purchase Failure Reason '{0}' mapping", error.ToString());
  206. }
  207. FirePurchaseFailedEvent(FindStoreProduct(product), value);
  208. }
  209. public void ValidatePurchases()
  210. {
  211. _iapValidator.ValidatePurchases();
  212. }
  213. public void MarkPurchaseAsConsumed(T Product)
  214. {
  215. //if (!_unconsumedPurchases.Contains(Product))
  216. //{
  217. // object[] array = new object[2];
  218. // T product = Product;
  219. // array[0] = product.Identifier;
  220. // array[1] = "";
  221. // UnityEngine.Debug.LogErrorFormat("Cannot mark Purchase of {0} with TransactionID {1} as consumed: purchase not found in the unconsumed list!", array);
  222. // return;
  223. //}
  224. //Product product2 = _unconsumedProducts.Find((Product x) => x.transactionID == purchase.TransactionID);
  225. //if (product2 == null)
  226. //{
  227. // object[] array2 = new object[2];
  228. // T product3 = Product;
  229. // array2[0] = product3.Identifier;
  230. // array2[1] = "";
  231. // UnityEngine.Debug.LogErrorFormat("Cannot mark purchase of {0} with transactionID {1} as consumed: it has no linked Product!", array2);
  232. //}
  233. //else
  234. //{
  235. //_unconsumedPurchases.Remove(Product);
  236. //_unconsumedProducts.Remove(product2);
  237. //_storeController.ConfirmPendingPurchase(product2);
  238. //}
  239. }
  240. public T FindProduct(string identifier)
  241. {
  242. return _iapCatalog.FindProduct(identifier);
  243. }
  244. public T FindProduct(Predicate<T> predicate)
  245. {
  246. return _iapCatalog.FindProduct(predicate);
  247. }
  248. public T[] GetProducts(Predicate<T> predicate)
  249. {
  250. return _iapCatalog.GetAvailableProducts(predicate);
  251. }
  252. private void OnPurchaseDeferred(Product product)
  253. {
  254. UnityEngine.Debug.LogFormat("Purchase Deferred {0}", product.definition.id);
  255. FirePurchaseDeferredEvent(FindStoreProduct(product));
  256. }
  257. private void InitializeStoreController()
  258. {
  259. ConfigurationBuilder standardConfigurationBuilder = GetStandardConfigurationBuilder();
  260. T[] availableProducts = _iapCatalog.AvailableProducts;
  261. int num = availableProducts.Length;
  262. for (int i = 0; i < num; i++)
  263. {
  264. string identifier = availableProducts[i].Identifier;
  265. standardConfigurationBuilder.AddProduct(identifier, ProductType.Consumable);
  266. }
  267. UnityPurchasing.Initialize(this, standardConfigurationBuilder);
  268. }
  269. private ConfigurationBuilder GetStandardConfigurationBuilder()
  270. {
  271. StandardPurchasingModule first = StandardPurchasingModule.Instance();
  272. return ConfigurationBuilder.Instance(first);
  273. }
  274. private T FindStoreProduct(Product product)
  275. {
  276. if (LoadingError != 0)
  277. {
  278. return (T)null;
  279. }
  280. return _iapCatalog.FindProduct(product.definition.id);
  281. }
  282. }
  283. }