PageRenderTime 61ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/40_code/Unity/Assets/Plugins/UnityPurchasing/script/IAPDemo.cs

https://gitlab.com/meteora/eaf
C# | 417 lines | 269 code | 68 blank | 80 comment | 37 complexity | 4a12c3ae84054e54f6af9fb483e29b1f MD5 | raw file
  1. #if UNITY_ANDROID || UNITY_IPHONE || UNITY_STANDALONE_OSX || UNITY_TVOS
  2. // You must obfuscate your secrets using Window > Unity IAP > Receipt Validation Obfuscator
  3. // before receipt validation will compile in this sample.
  4. //#define RECEIPT_VALIDATION
  5. #endif
  6. using System;
  7. using System.Collections.Generic;
  8. using UnityEngine;
  9. using UnityEngine.Events;
  10. using UnityEngine.Purchasing;
  11. using UnityEngine.UI;
  12. #if RECEIPT_VALIDATION
  13. using UnityEngine.Purchasing.Security;
  14. #endif
  15. /// <summary>
  16. /// An example of basic Unity IAP functionality.
  17. /// To use with your account, configure the product ids (AddProduct)
  18. /// and Google Play key (SetPublicKey).
  19. /// </summary>
  20. [AddComponentMenu("Unity IAP/Demo")]
  21. public class IAPDemo : MonoBehaviour, IStoreListener
  22. {
  23. // Unity IAP objects
  24. private IStoreController m_Controller;
  25. private IAppleExtensions m_AppleExtensions;
  26. private ISamsungAppsExtensions m_SamsungExtensions;
  27. private int m_SelectedItemIndex = -1; // -1 == no product
  28. private bool m_PurchaseInProgress;
  29. private Selectable m_InteractableSelectable; // Optimization used for UI state management
  30. private bool m_IsSamsungAppsStoreSelected;
  31. #if RECEIPT_VALIDATION
  32. private CrossPlatformValidator validator;
  33. #endif
  34. /// <summary>
  35. /// This will be called when Unity IAP has finished initialising.
  36. /// </summary>
  37. public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
  38. {
  39. m_Controller = controller;
  40. m_AppleExtensions = extensions.GetExtension<IAppleExtensions> ();
  41. m_SamsungExtensions = extensions.GetExtension<ISamsungAppsExtensions> ();
  42. InitUI(controller.products.all);
  43. // On Apple platforms we need to handle deferred purchases caused by Apple's Ask to Buy feature.
  44. // On non-Apple platforms this will have no effect; OnDeferred will never be called.
  45. m_AppleExtensions.RegisterPurchaseDeferredListener(OnDeferred);
  46. Debug.Log("Available items:");
  47. foreach (var item in controller.products.all)
  48. {
  49. if (item.availableToPurchase)
  50. {
  51. Debug.Log(string.Join(" - ",
  52. new[]
  53. {
  54. item.metadata.localizedTitle,
  55. item.metadata.localizedDescription,
  56. item.metadata.isoCurrencyCode,
  57. item.metadata.localizedPrice.ToString(),
  58. item.metadata.localizedPriceString,
  59. item.transactionID,
  60. item.receipt
  61. }));
  62. }
  63. }
  64. // Prepare model for purchasing
  65. if (m_Controller.products.all.Length > 0)
  66. {
  67. m_SelectedItemIndex = 0;
  68. }
  69. // Populate the product menu now that we have Products
  70. for (int t = 0; t < m_Controller.products.all.Length; t++)
  71. {
  72. var item = m_Controller.products.all[t];
  73. var description = string.Format("{0} - {1}", item.metadata.localizedTitle, item.metadata.localizedPriceString);
  74. // NOTE: my options list is created in InitUI
  75. GetDropdown().options[t] = new Dropdown.OptionData(description);
  76. }
  77. // Ensure I render the selected list element
  78. GetDropdown().RefreshShownValue();
  79. // Now that I have real products, begin showing product purchase history
  80. UpdateHistoryUI();
  81. }
  82. /// <summary>
  83. /// This will be called when a purchase completes.
  84. /// </summary>
  85. public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
  86. {
  87. Debug.Log("Purchase OK: " + e.purchasedProduct.definition.id);
  88. Debug.Log("Receipt: " + e.purchasedProduct.receipt);
  89. m_PurchaseInProgress = false;
  90. // Now that my purchase history has changed, update its UI
  91. UpdateHistoryUI();
  92. #if RECEIPT_VALIDATION
  93. if (Application.platform == RuntimePlatform.Android ||
  94. Application.platform == RuntimePlatform.IPhonePlayer ||
  95. Application.platform == RuntimePlatform.OSXPlayer) {
  96. try {
  97. var result = validator.Validate(e.purchasedProduct.receipt);
  98. Debug.Log("Receipt is valid. Contents:");
  99. foreach (IPurchaseReceipt productReceipt in result) {
  100. Debug.Log(productReceipt.productID);
  101. Debug.Log(productReceipt.purchaseDate);
  102. Debug.Log(productReceipt.transactionID);
  103. GooglePlayReceipt google = productReceipt as GooglePlayReceipt;
  104. if (null != google) {
  105. Debug.Log(google.purchaseState);
  106. Debug.Log(google.purchaseToken);
  107. }
  108. AppleInAppPurchaseReceipt apple = productReceipt as AppleInAppPurchaseReceipt;
  109. if (null != apple) {
  110. Debug.Log(apple.originalTransactionIdentifier);
  111. Debug.Log(apple.subscriptionExpirationDate);
  112. Debug.Log(apple.cancellationDate);
  113. Debug.Log(apple.quantity);
  114. }
  115. }
  116. } catch (IAPSecurityException) {
  117. Debug.Log("Invalid receipt, not unlocking content");
  118. return PurchaseProcessingResult.Complete;
  119. }
  120. }
  121. #endif
  122. // You should unlock the content here.
  123. // Indicate we have handled this purchase, we will not be informed of it again.x
  124. return PurchaseProcessingResult.Complete;
  125. }
  126. /// <summary>
  127. /// This will be called is an attempted purchase fails.
  128. /// </summary>
  129. public void OnPurchaseFailed(Product item, PurchaseFailureReason r)
  130. {
  131. Debug.Log("Purchase failed: " + item.definition.id);
  132. Debug.Log(r);
  133. m_PurchaseInProgress = false;
  134. }
  135. public void OnInitializeFailed(InitializationFailureReason error)
  136. {
  137. Debug.Log("Billing failed to initialize!");
  138. switch (error)
  139. {
  140. case InitializationFailureReason.AppNotKnown:
  141. Debug.LogError("Is your App correctly uploaded on the relevant publisher console?");
  142. break;
  143. case InitializationFailureReason.PurchasingUnavailable:
  144. // Ask the user if billing is disabled in device settings.
  145. Debug.Log("Billing disabled!");
  146. break;
  147. case InitializationFailureReason.NoProductsAvailable:
  148. // Developer configuration error; check product metadata.
  149. Debug.Log("No products available for purchase!");
  150. break;
  151. }
  152. }
  153. public void Awake()
  154. {
  155. var module = StandardPurchasingModule.Instance();
  156. // The FakeStore supports: no-ui (always succeeding), basic ui (purchase pass/fail), and
  157. // developer ui (initialization, purchase, failure code setting). These correspond to
  158. // the FakeStoreUIMode Enum values passed into StandardPurchasingModule.useFakeStoreUIMode.
  159. module.useFakeStoreUIMode = FakeStoreUIMode.StandardUser;
  160. var builder = ConfigurationBuilder.Instance(module);
  161. // This enables the Microsoft IAP simulator for local testing.
  162. // You would remove this before building your release package.
  163. builder.Configure<IMicrosoftConfiguration>().useMockBillingSystem = true;
  164. // Define our products.
  165. // In this case our products have the same identifier across all the App stores,
  166. // except on the Mac App store where product IDs cannot be reused across both Mac and
  167. // iOS stores.
  168. // So on the Mac App store our products have different identifiers,
  169. // and we tell Unity IAP this by using the IDs class.
  170. builder.AddProduct("100.gold.coins", ProductType.Consumable, new IDs
  171. {
  172. {"100.gold.coins.mac", MacAppStore.Name},
  173. {"000000596586", TizenStore.Name},
  174. });
  175. builder.AddProduct("500.gold.coins", ProductType.Consumable, new IDs
  176. {
  177. {"500.gold.coins.mac", MacAppStore.Name},
  178. {"000000596581", TizenStore.Name},
  179. });
  180. builder.AddProduct("sword", ProductType.NonConsumable, new IDs
  181. {
  182. {"sword.mac", MacAppStore.Name},
  183. {"000000596583", TizenStore.Name},
  184. });
  185. builder.AddProduct("subscription", ProductType.Subscription, new IDs
  186. {
  187. {"subscription.mac", MacAppStore.Name}
  188. });
  189. // Write Amazon's JSON description of our products to storage when using Amazon's local sandbox.
  190. // This should be removed from a production build.
  191. builder.Configure<IAmazonConfiguration>().WriteSandboxJSON(builder.products);
  192. // This enables simulated purchase success for Samsung IAP.
  193. // You would remove this, or set to SamsungAppsMode.Production, before building your release package.
  194. builder.Configure<ISamsungAppsConfiguration>().SetMode(SamsungAppsMode.AlwaysSucceed);
  195. // This records whether we are using Samsung IAP. Currently ISamsungAppsExtensions.RestoreTransactions
  196. // displays a blocking Android Activity, so:
  197. // A) Unity IAP does not automatically restore purchases on Samsung Galaxy Apps
  198. // B) IAPDemo (this) displays the "Restore" GUI button for Samsung Galaxy Apps
  199. m_IsSamsungAppsStoreSelected = module.androidStore == AndroidStore.SamsungApps;
  200. // This selects the GroupId that was created in the Tizen Store for this set of products
  201. // An empty or non-matching GroupId here will result in no products available for purchase
  202. builder.Configure<ITizenStoreConfiguration>().SetGroupId("100000085616");
  203. #if RECEIPT_VALIDATION
  204. validator = new CrossPlatformValidator(GooglePlayTangle.Data(), AppleTangle.Data(), Application.bundleIdentifier);
  205. #endif
  206. // Now we're ready to initialize Unity IAP.
  207. UnityPurchasing.Initialize(this, builder);
  208. }
  209. /// <summary>
  210. /// This will be called after a call to IAppleExtensions.RestoreTransactions().
  211. /// </summary>
  212. private void OnTransactionsRestored(bool success)
  213. {
  214. Debug.Log("Transactions restored.");
  215. }
  216. /// <summary>
  217. /// iOS Specific.
  218. /// This is called as part of Apple's 'Ask to buy' functionality,
  219. /// when a purchase is requested by a minor and referred to a parent
  220. /// for approval.
  221. ///
  222. /// When the purchase is approved or rejected, the normal purchase events
  223. /// will fire.
  224. /// </summary>
  225. /// <param name="item">Item.</param>
  226. private void OnDeferred(Product item)
  227. {
  228. Debug.Log("Purchase deferred: " + item.definition.id);
  229. }
  230. private void InitUI(IEnumerable<Product> items)
  231. {
  232. // Disable the UI while IAP is initializing
  233. // See also UpdateInteractable()
  234. m_InteractableSelectable = GetDropdown(); // References any one of the disabled components
  235. // Show Restore button on Apple platforms
  236. if (! (Application.platform == RuntimePlatform.IPhonePlayer ||
  237. Application.platform == RuntimePlatform.OSXPlayer ||
  238. m_IsSamsungAppsStoreSelected ) )
  239. {
  240. GetRestoreButton().gameObject.SetActive(false);
  241. }
  242. foreach (var item in items)
  243. {
  244. // Add initial pre-IAP-initialization content. Update later in OnInitialized.
  245. var description = string.Format("{0} - {1}", item.definition.id, item.definition.type);
  246. GetDropdown().options.Add(new Dropdown.OptionData(description));
  247. }
  248. // Ensure I render the selected list element
  249. GetDropdown().RefreshShownValue();
  250. GetDropdown().onValueChanged.AddListener((int selectedItem) => {
  251. Debug.Log("OnClickDropdown item " + selectedItem);
  252. m_SelectedItemIndex = selectedItem;
  253. });
  254. // Initialize my button event handling
  255. GetBuyButton().onClick.AddListener(() => {
  256. if (m_PurchaseInProgress == true) {
  257. return;
  258. }
  259. m_Controller.InitiatePurchase(m_Controller.products.all[m_SelectedItemIndex]);
  260. // Don't need to draw our UI whilst a purchase is in progress.
  261. // This is not a requirement for IAP Applications but makes the demo
  262. // scene tidier whilst the fake purchase dialog is showing.
  263. m_PurchaseInProgress = true;
  264. });
  265. if (GetRestoreButton() != null)
  266. {
  267. GetRestoreButton().onClick.AddListener(() =>
  268. {
  269. if (m_IsSamsungAppsStoreSelected)
  270. {
  271. m_SamsungExtensions.RestoreTransactions(OnTransactionsRestored);
  272. }
  273. else
  274. {
  275. m_AppleExtensions.RestoreTransactions(OnTransactionsRestored);
  276. }
  277. });
  278. }
  279. }
  280. public void UpdateHistoryUI()
  281. {
  282. if (m_Controller == null)
  283. {
  284. return;
  285. }
  286. var itemText = "Item\n\n";
  287. var countText = "Purchased\n\n";
  288. for (int t = 0; t < m_Controller.products.all.Length; t++)
  289. {
  290. var item = m_Controller.products.all [t];
  291. // Collect history status report
  292. itemText += "\n\n" + item.definition.id;
  293. countText += "\n\n" + item.hasReceipt.ToString();
  294. }
  295. // Show history
  296. GetText(false).text = itemText;
  297. GetText(true).text = countText;
  298. }
  299. protected void UpdateInteractable()
  300. {
  301. if (m_InteractableSelectable == null)
  302. {
  303. return;
  304. }
  305. bool interactable = m_Controller != null;
  306. if (interactable != m_InteractableSelectable.interactable)
  307. {
  308. if (GetRestoreButton() != null)
  309. {
  310. GetRestoreButton().interactable = interactable;
  311. }
  312. GetBuyButton().interactable = interactable;
  313. GetDropdown().interactable = interactable;
  314. }
  315. }
  316. public void Update()
  317. {
  318. UpdateInteractable();
  319. }
  320. private Dropdown GetDropdown()
  321. {
  322. return GameObject.Find("Dropdown").GetComponent<Dropdown>();
  323. }
  324. private Button GetBuyButton()
  325. {
  326. return GameObject.Find("Buy").GetComponent<Button>();
  327. }
  328. /// <summary>
  329. /// Gets the restore button when available
  330. /// </summary>
  331. /// <returns><c>null</c> or the restore button.</returns>
  332. private Button GetRestoreButton()
  333. {
  334. GameObject restoreButtonGameObject = GameObject.Find("Restore");
  335. if (restoreButtonGameObject != null)
  336. {
  337. return restoreButtonGameObject.GetComponent<Button>();
  338. }
  339. else
  340. {
  341. return null;
  342. }
  343. }
  344. private Text GetText(bool right)
  345. {
  346. var which = right ? "TextR" : "TextL";
  347. return GameObject.Find(which).GetComponent<Text>();
  348. }
  349. }