IAPDemo.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  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. //#define DELAY_CONFIRMATION // Returns PurchaseProcessingResult.Pending from ProcessPurchase, then calls ConfirmPendingPurchase after a delay
  7. using System;
  8. using System.Collections;
  9. using System.Collections.Generic;
  10. using UnityEngine;
  11. using UnityEngine.Events;
  12. using UnityEngine.Purchasing;
  13. using UnityEngine.UI;
  14. #if RECEIPT_VALIDATION
  15. using UnityEngine.Purchasing.Security;
  16. #endif
  17. /// <summary>
  18. /// An example of basic Unity IAP functionality.
  19. /// To use with your account, configure the product ids (AddProduct)
  20. /// and Google Play key (SetPublicKey).
  21. /// </summary>
  22. [AddComponentMenu("Unity IAP/Demo")]
  23. public class IAPDemo : MonoBehaviour, IStoreListener
  24. {
  25. // Unity IAP objects
  26. private IStoreController m_Controller;
  27. private IAppleExtensions m_AppleExtensions;
  28. private IMoolahExtension m_MoolahExtensions;
  29. private ISamsungAppsExtensions m_SamsungExtensions;
  30. private IMicrosoftExtensions m_MicrosoftExtensions;
  31. #pragma warning disable 0414
  32. private bool m_IsGooglePlayStoreSelected;
  33. #pragma warning restore 0414
  34. private bool m_IsSamsungAppsStoreSelected;
  35. private bool m_IsCloudMoolahStoreSelected;
  36. private string m_LastTransationID;
  37. private string m_LastReceipt;
  38. private string m_CloudMoolahUserName;
  39. private bool m_IsLoggedIn = false;
  40. private int m_SelectedItemIndex = -1; // -1 == no product
  41. private bool m_PurchaseInProgress;
  42. private Selectable m_InteractableSelectable; // Optimization used for UI state management
  43. #if RECEIPT_VALIDATION
  44. private CrossPlatformValidator validator;
  45. #endif
  46. /// <summary>
  47. /// This will be called when Unity IAP has finished initialising.
  48. /// </summary>
  49. public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
  50. {
  51. m_Controller = controller;
  52. m_AppleExtensions = extensions.GetExtension<IAppleExtensions> ();
  53. m_SamsungExtensions = extensions.GetExtension<ISamsungAppsExtensions> ();
  54. m_MoolahExtensions = extensions.GetExtension<IMoolahExtension> ();
  55. m_MicrosoftExtensions = extensions.GetExtension<IMicrosoftExtensions> ();
  56. InitUI(controller.products.all);
  57. // On Apple platforms we need to handle deferred purchases caused by Apple's Ask to Buy feature.
  58. // On non-Apple platforms this will have no effect; OnDeferred will never be called.
  59. m_AppleExtensions.RegisterPurchaseDeferredListener(OnDeferred);
  60. Debug.Log("Available items:");
  61. foreach (var item in controller.products.all)
  62. {
  63. if (item.availableToPurchase)
  64. {
  65. Debug.Log(string.Join(" - ",
  66. new[]
  67. {
  68. item.metadata.localizedTitle,
  69. item.metadata.localizedDescription,
  70. item.metadata.isoCurrencyCode,
  71. item.metadata.localizedPrice.ToString(),
  72. item.metadata.localizedPriceString,
  73. item.transactionID,
  74. item.receipt
  75. }));
  76. }
  77. }
  78. // Prepare model for purchasing
  79. if (m_Controller.products.all.Length > 0)
  80. {
  81. m_SelectedItemIndex = 0;
  82. }
  83. // Populate the product menu now that we have Products
  84. for (int t = 0; t < m_Controller.products.all.Length; t++)
  85. {
  86. var item = m_Controller.products.all[t];
  87. var description = string.Format("{0} | {1} => {2}", item.metadata.localizedTitle, item.metadata.localizedPriceString, item.metadata.localizedPrice);
  88. // NOTE: my options list is created in InitUI
  89. GetDropdown().options[t] = new Dropdown.OptionData(description);
  90. }
  91. // Ensure I render the selected list element
  92. GetDropdown().RefreshShownValue();
  93. // Now that I have real products, begin showing product purchase history
  94. UpdateHistoryUI();
  95. LogProductDefinitions();
  96. }
  97. /// <summary>
  98. /// This will be called when a purchase completes.
  99. /// </summary>
  100. public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
  101. {
  102. Debug.Log("Purchase OK: " + e.purchasedProduct.definition.id);
  103. Debug.Log("Receipt: " + e.purchasedProduct.receipt);
  104. m_LastTransationID = e.purchasedProduct.transactionID;
  105. m_LastReceipt = e.purchasedProduct.receipt;
  106. m_PurchaseInProgress = false;
  107. #if RECEIPT_VALIDATION
  108. // Local validation is available for GooglePlay and Apple stores
  109. if (m_IsGooglePlayStoreSelected ||
  110. Application.platform == RuntimePlatform.IPhonePlayer ||
  111. Application.platform == RuntimePlatform.OSXPlayer ||
  112. Application.platform == RuntimePlatform.tvOS) {
  113. try {
  114. var result = validator.Validate(e.purchasedProduct.receipt);
  115. Debug.Log("Receipt is valid. Contents:");
  116. foreach (IPurchaseReceipt productReceipt in result) {
  117. Debug.Log(productReceipt.productID);
  118. Debug.Log(productReceipt.purchaseDate);
  119. Debug.Log(productReceipt.transactionID);
  120. GooglePlayReceipt google = productReceipt as GooglePlayReceipt;
  121. if (null != google) {
  122. Debug.Log(google.purchaseState);
  123. Debug.Log(google.purchaseToken);
  124. }
  125. AppleInAppPurchaseReceipt apple = productReceipt as AppleInAppPurchaseReceipt;
  126. if (null != apple) {
  127. Debug.Log(apple.originalTransactionIdentifier);
  128. Debug.Log(apple.subscriptionExpirationDate);
  129. Debug.Log(apple.cancellationDate);
  130. Debug.Log(apple.quantity);
  131. }
  132. }
  133. } catch (IAPSecurityException) {
  134. Debug.Log("Invalid receipt, not unlocking content");
  135. return PurchaseProcessingResult.Complete;
  136. }
  137. }
  138. #endif
  139. // CloudMoolah purchase completion / finishing currently requires using the API
  140. // extension IMoolahExtension.RequestPayout to finish a transaction.
  141. if (m_IsCloudMoolahStoreSelected)
  142. {
  143. // Finish transaction with CloudMoolah server
  144. m_MoolahExtensions.RequestPayOut(e.purchasedProduct.transactionID,
  145. (string transactionID, RequestPayOutState state, string message) => {
  146. if (state == RequestPayOutState.RequestPayOutSucceed) {
  147. // Finally, finish transaction with Unity IAP's local
  148. // transaction log, recording the transaction id there
  149. m_Controller.ConfirmPendingPurchase(e.purchasedProduct);
  150. // Unlock content here.
  151. } else {
  152. Debug.Log("RequestPayOut: failed. transactionID: " + transactionID +
  153. ", state: " + state + ", message: " + message);
  154. // Finishing failed. Retry later.
  155. }
  156. });
  157. }
  158. // You should unlock the content here.
  159. // Indicate if we have handled this purchase.
  160. // PurchaseProcessingResult.Complete: ProcessPurchase will not be called
  161. // with this product again, until next purchase.
  162. // PurchaseProcessingResult.Pending: ProcessPurchase will be called
  163. // again with this product at next app launch. Later, call
  164. // m_Controller.ConfirmPendingPurchase(Product) to complete handling
  165. // this purchase. Use to transactionally save purchases to a cloud
  166. // game service.
  167. #if DELAY_CONFIRMATION
  168. StartCoroutine(ConfirmPendingPurchaseAfterDelay(e.purchasedProduct));
  169. return PurchaseProcessingResult.Pending;
  170. #else
  171. UpdateHistoryUI();
  172. return PurchaseProcessingResult.Complete;
  173. #endif
  174. }
  175. #if DELAY_CONFIRMATION
  176. private HashSet<string> m_PendingProducts = new HashSet<string>();
  177. private IEnumerator ConfirmPendingPurchaseAfterDelay(Product p)
  178. {
  179. m_PendingProducts.Add(p.definition.id);
  180. Debug.Log("Delaying confirmation of " + p.definition.id + " for 5 seconds.");
  181. UpdateHistoryUI();
  182. yield return new WaitForSeconds(5f);
  183. Debug.Log("Confirming purchase of " + p.definition.id);
  184. m_Controller.ConfirmPendingPurchase(p);
  185. m_PendingProducts.Remove(p.definition.id);
  186. UpdateHistoryUI();
  187. }
  188. #endif
  189. /// <summary>
  190. /// This will be called is an attempted purchase fails.
  191. /// </summary>
  192. public void OnPurchaseFailed(Product item, PurchaseFailureReason r)
  193. {
  194. Debug.Log("Purchase failed: " + item.definition.id);
  195. Debug.Log(r);
  196. m_PurchaseInProgress = false;
  197. }
  198. public void OnInitializeFailed(InitializationFailureReason error)
  199. {
  200. Debug.Log("Billing failed to initialize!");
  201. switch (error)
  202. {
  203. case InitializationFailureReason.AppNotKnown:
  204. Debug.LogError("Is your App correctly uploaded on the relevant publisher console?");
  205. break;
  206. case InitializationFailureReason.PurchasingUnavailable:
  207. // Ask the user if billing is disabled in device settings.
  208. Debug.Log("Billing disabled!");
  209. break;
  210. case InitializationFailureReason.NoProductsAvailable:
  211. // Developer configuration error; check product metadata.
  212. Debug.Log("No products available for purchase!");
  213. break;
  214. }
  215. }
  216. public void Awake()
  217. {
  218. var module = StandardPurchasingModule.Instance();
  219. // The FakeStore supports: no-ui (always succeeding), basic ui (purchase pass/fail), and
  220. // developer ui (initialization, purchase, failure code setting). These correspond to
  221. // the FakeStoreUIMode Enum values passed into StandardPurchasingModule.useFakeStoreUIMode.
  222. module.useFakeStoreUIMode = FakeStoreUIMode.StandardUser;
  223. var builder = ConfigurationBuilder.Instance(module);
  224. // This enables the Microsoft IAP simulator for local testing.
  225. // You would remove this before building your release package.
  226. builder.Configure<IMicrosoftConfiguration>().useMockBillingSystem = true;
  227. builder.Configure<IGooglePlayConfiguration>().SetPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2O/9/H7jYjOsLFT/uSy3ZEk5KaNg1xx60RN7yWJaoQZ7qMeLy4hsVB3IpgMXgiYFiKELkBaUEkObiPDlCxcHnWVlhnzJBvTfeCPrYNVOOSJFZrXdotp5L0iS2NVHjnllM+HA1M0W2eSNjdYzdLmZl1bxTpXa4th+dVli9lZu7B7C2ly79i/hGTmvaClzPBNyX+Rtj7Bmo336zh2lYbRdpD5glozUq+10u91PMDPH+jqhx10eyZpiapr8dFqXl5diMiobknw9CgcjxqMTVBQHK6hS0qYKPmUDONquJn280fBs1PTeA6NMG03gb9FLESKFclcuEZtvM8ZwMMRxSLA9GwIDAQAB");
  228. m_IsGooglePlayStoreSelected = Application.platform == RuntimePlatform.Android && module.androidStore == AndroidStore.GooglePlay;
  229. // CloudMoolah Configuration setings
  230. // All games must set the configuration. the configuration need to apply on the CloudMoolah Portal.
  231. // CloudMoolah APP Key
  232. builder.Configure<IMoolahConfiguration>().appKey = "d93f4564c41d463ed3d3cd207594ee1b";
  233. // CloudMoolah Hash Key
  234. builder.Configure<IMoolahConfiguration>().hashKey = "cc";
  235. // This enables the CloudMoolah test mode for local testing.
  236. // You would remove this, or set to CloudMoolahMode.Production, before building your release package.
  237. builder.Configure<IMoolahConfiguration>().SetMode(CloudMoolahMode.AlwaysSucceed);
  238. // This records whether we are using Cloud Moolah IAP.
  239. // Cloud Moolah requires logging in to access your Digital Wallet, so:
  240. // A) IAPDemo (this) displays the Cloud Moolah GUI button for Cloud Moolah
  241. m_IsCloudMoolahStoreSelected = Application.platform == RuntimePlatform.Android && module.androidStore == AndroidStore.CloudMoolah;
  242. // Define our products.
  243. // In this case our products have the same identifier across all the App stores,
  244. // except on the Mac App store where product IDs cannot be reused across both Mac and
  245. // iOS stores.
  246. // So on the Mac App store our products have different identifiers,
  247. // and we tell Unity IAP this by using the IDs class.
  248. builder.AddProduct("100.gold.coins", ProductType.Consumable, new IDs
  249. {
  250. {"100.gold.coins.mac", MacAppStore.Name},
  251. {"000000596586", TizenStore.Name},
  252. {"com.ff", MoolahAppStore.Name},
  253. });
  254. builder.AddProduct("500.gold.coins", ProductType.Consumable, new IDs
  255. {
  256. {"500.gold.coins.mac", MacAppStore.Name},
  257. {"000000596581", TizenStore.Name},
  258. {"com.ee", MoolahAppStore.Name},
  259. });
  260. builder.AddProduct("sword", ProductType.NonConsumable, new IDs
  261. {
  262. {"sword.mac", MacAppStore.Name},
  263. {"000000596583", TizenStore.Name},
  264. });
  265. builder.AddProduct("subscription", ProductType.Subscription, new IDs
  266. {
  267. {"subscription.mac", MacAppStore.Name}
  268. });
  269. // Write Amazon's JSON description of our products to storage when using Amazon's local sandbox.
  270. // This should be removed from a production build.
  271. builder.Configure<IAmazonConfiguration>().WriteSandboxJSON(builder.products);
  272. // This enables simulated purchase success for Samsung IAP.
  273. // You would remove this, or set to SamsungAppsMode.Production, before building your release package.
  274. builder.Configure<ISamsungAppsConfiguration>().SetMode(SamsungAppsMode.AlwaysSucceed);
  275. // This records whether we are using Samsung IAP. Currently ISamsungAppsExtensions.RestoreTransactions
  276. // displays a blocking Android Activity, so:
  277. // A) Unity IAP does not automatically restore purchases on Samsung Galaxy Apps
  278. // B) IAPDemo (this) displays the "Restore" GUI button for Samsung Galaxy Apps
  279. m_IsSamsungAppsStoreSelected = Application.platform == RuntimePlatform.Android && module.androidStore == AndroidStore.SamsungApps;
  280. // This selects the GroupId that was created in the Tizen Store for this set of products
  281. // An empty or non-matching GroupId here will result in no products available for purchase
  282. builder.Configure<ITizenStoreConfiguration>().SetGroupId("100000085616");
  283. #if RECEIPT_VALIDATION
  284. string appIdentifier;
  285. #if UNITY_5_6_OR_NEWER
  286. appIdentifier = Application.identifier;
  287. #else
  288. appIdentifier = Application.bundleIdentifier;
  289. #endif
  290. validator = new CrossPlatformValidator(GooglePlayTangle.Data(), AppleTangle.Data(), appIdentifier);
  291. #endif
  292. // Now we're ready to initialize Unity IAP.
  293. UnityPurchasing.Initialize(this, builder);
  294. }
  295. /// <summary>
  296. /// This will be called after a call to IAppleExtensions.RestoreTransactions().
  297. /// </summary>
  298. private void OnTransactionsRestored(bool success)
  299. {
  300. Debug.Log("Transactions restored.");
  301. }
  302. /// <summary>
  303. /// iOS Specific.
  304. /// This is called as part of Apple's 'Ask to buy' functionality,
  305. /// when a purchase is requested by a minor and referred to a parent
  306. /// for approval.
  307. ///
  308. /// When the purchase is approved or rejected, the normal purchase events
  309. /// will fire.
  310. /// </summary>
  311. /// <param name="item">Item.</param>
  312. private void OnDeferred(Product item)
  313. {
  314. Debug.Log("Purchase deferred: " + item.definition.id);
  315. }
  316. private void InitUI(IEnumerable<Product> items)
  317. {
  318. // Disable the UI while IAP is initializing
  319. // See also UpdateInteractable()
  320. m_InteractableSelectable = GetDropdown(); // References any one of the disabled components
  321. // Show Restore button on supported platforms
  322. if (! (Application.platform == RuntimePlatform.IPhonePlayer ||
  323. Application.platform == RuntimePlatform.OSXPlayer ||
  324. Application.platform == RuntimePlatform.tvOS ||
  325. Application.platform == RuntimePlatform.WSAPlayerX86 ||
  326. Application.platform == RuntimePlatform.WSAPlayerX64 ||
  327. Application.platform == RuntimePlatform.WSAPlayerARM ||
  328. m_IsSamsungAppsStoreSelected || m_IsCloudMoolahStoreSelected) )
  329. {
  330. GetRestoreButton().gameObject.SetActive(false);
  331. }
  332. // Show Register, Login, and Validate buttons on CloudMoolah platform
  333. GetRegisterButton().gameObject.SetActive(m_IsCloudMoolahStoreSelected);
  334. GetLoginButton().gameObject.SetActive(m_IsCloudMoolahStoreSelected);
  335. GetValidateButton().gameObject.SetActive(m_IsCloudMoolahStoreSelected);
  336. foreach (var item in items)
  337. {
  338. // Add initial pre-IAP-initialization content. Update later in OnInitialized.
  339. var description = string.Format("{0} - {1}", item.definition.id, item.definition.type);
  340. GetDropdown().options.Add(new Dropdown.OptionData(description));
  341. }
  342. // Ensure I render the selected list element
  343. GetDropdown().RefreshShownValue();
  344. GetDropdown().onValueChanged.AddListener((int selectedItem) => {
  345. Debug.Log("OnClickDropdown item " + selectedItem);
  346. m_SelectedItemIndex = selectedItem;
  347. });
  348. // Initialize my button event handling
  349. GetBuyButton().onClick.AddListener(() => {
  350. if (m_PurchaseInProgress == true) {
  351. Debug.Log("Please wait, purchasing ...");
  352. return;
  353. }
  354. // For CloudMoolah, games utilizing a connected backend game server may wish to login.
  355. // Standalone games may not need to login.
  356. if (m_IsCloudMoolahStoreSelected && m_IsLoggedIn == false)
  357. {
  358. Debug.LogWarning("CloudMoolah purchase notifications will not be forwarded server-to-server. Login incomplete.");
  359. }
  360. // Don't need to draw our UI whilst a purchase is in progress.
  361. // This is not a requirement for IAP Applications but makes the demo
  362. // scene tidier whilst the fake purchase dialog is showing.
  363. m_PurchaseInProgress = true;
  364. m_Controller.InitiatePurchase(m_Controller.products.all[m_SelectedItemIndex], "aDemoDeveloperPayload");
  365. });
  366. if (GetRestoreButton() != null)
  367. {
  368. GetRestoreButton().onClick.AddListener(() => {
  369. if (m_IsCloudMoolahStoreSelected)
  370. {
  371. if (m_IsLoggedIn == false)
  372. {
  373. Debug.LogError("CloudMoolah purchase restoration aborted. Login incomplete.");
  374. }
  375. else
  376. {
  377. // Restore abnornal transaction identifer, if Client don't receive transaction identifer.
  378. m_MoolahExtensions.RestoreTransactionID((RestoreTransactionIDState restoreTransactionIDState) => {
  379. Debug.Log("restoreTransactionIDState = " + restoreTransactionIDState.ToString());
  380. bool success =
  381. restoreTransactionIDState != RestoreTransactionIDState.RestoreFailed &&
  382. restoreTransactionIDState != RestoreTransactionIDState.NotKnown;
  383. OnTransactionsRestored(success);
  384. });
  385. }
  386. }
  387. else if (m_IsSamsungAppsStoreSelected)
  388. {
  389. m_SamsungExtensions.RestoreTransactions(OnTransactionsRestored);
  390. }
  391. else if (Application.platform == RuntimePlatform.WSAPlayerX86 ||
  392. Application.platform == RuntimePlatform.WSAPlayerX64 ||
  393. Application.platform == RuntimePlatform.WSAPlayerARM)
  394. {
  395. m_MicrosoftExtensions.RestoreTransactions();
  396. }
  397. else
  398. {
  399. m_AppleExtensions.RestoreTransactions(OnTransactionsRestored);
  400. }
  401. });
  402. }
  403. // CloudMoolah requires user registration and supports login to manage the user's
  404. // digital wallet. The CM store also supports remote receipt validation.
  405. // CloudMoolah user registration extension, to establish digital wallet
  406. // This is a "fast" registration, requiring only a password. Users may provide
  407. // more detail including an email address during the purchase flow, a "slow" registration, if desired.
  408. if (GetRegisterButton() != null)
  409. {
  410. GetRegisterButton().onClick.AddListener (() => {
  411. // Provide a unique password to establish the user's account.
  412. // Typically, connected games (with backend game servers), may already
  413. // have available a user-token, which could be supplied here.
  414. m_MoolahExtensions.FastRegister("CMPassword", RegisterSucceeded, RegisterFailed);
  415. });
  416. }
  417. // CloudMoolah user login extension, to access existing digital wallet
  418. if (GetLoginButton() != null)
  419. {
  420. GetLoginButton().onClick.AddListener (() => {
  421. m_MoolahExtensions.Login(m_CloudMoolahUserName, "CMPassword", LoginResult);
  422. });
  423. }
  424. // CloudMoolah remote purchase receipt validation, to determine if the purchase is fraudulent
  425. // NOTE: Remote validation only available for CloudMoolah currently. For local validation,
  426. // see ProcessPurchase.
  427. if (GetValidateButton() != null)
  428. {
  429. GetValidateButton ().onClick.AddListener (() => {
  430. // Remotely validate the last transaction and receipt.
  431. m_MoolahExtensions.ValidateReceipt(m_LastTransationID, m_LastReceipt,
  432. (string transactionID, ValidateReceiptState state, string message) => {
  433. Debug.Log("ValidtateReceipt transactionID:" + transactionID
  434. + ", state:" + state.ToString() + ", message:" + message);
  435. });
  436. });
  437. }
  438. }
  439. public void LoginResult (LoginResultState state, string errorMsg)
  440. {
  441. if(state == LoginResultState.LoginSucceed)
  442. {
  443. m_IsLoggedIn = true;
  444. }
  445. else
  446. {
  447. m_IsLoggedIn = false;
  448. }
  449. Debug.Log ("LoginResult: state: " + state.ToString () + " errorMsg: " + errorMsg);
  450. }
  451. public void RegisterSucceeded(string cmUserName)
  452. {
  453. Debug.Log ("RegisterSucceeded: cmUserName = " + cmUserName);
  454. m_CloudMoolahUserName = cmUserName;
  455. }
  456. public void RegisterFailed (FastRegisterError error, string errorMessage)
  457. {
  458. Debug.Log ("RegisterFailed: error = " + error.ToString() + ", errorMessage = " + errorMessage);
  459. }
  460. public void UpdateHistoryUI()
  461. {
  462. if (m_Controller == null)
  463. {
  464. return;
  465. }
  466. var itemText = "Item\n\n";
  467. var countText = "Purchased\n\n";
  468. foreach (var item in m_Controller.products.all) {
  469. // Collect history status report
  470. itemText += "\n\n" + item.definition.id;
  471. countText += "\n\n";
  472. #if DELAY_CONFIRMATION
  473. if (m_PendingProducts.Contains(item.definition.id))
  474. countText += "(Pending) ";
  475. #endif
  476. countText += item.hasReceipt.ToString();
  477. }
  478. // Show history
  479. GetText(false).text = itemText;
  480. GetText(true).text = countText;
  481. }
  482. protected void UpdateInteractable()
  483. {
  484. if (m_InteractableSelectable == null)
  485. {
  486. return;
  487. }
  488. bool interactable = m_Controller != null;
  489. if (interactable != m_InteractableSelectable.interactable)
  490. {
  491. if (GetRestoreButton() != null)
  492. {
  493. GetRestoreButton().interactable = interactable;
  494. }
  495. GetBuyButton().interactable = interactable;
  496. GetDropdown().interactable = interactable;
  497. GetRegisterButton().interactable = interactable;
  498. GetLoginButton().interactable = interactable;
  499. }
  500. }
  501. public void Update()
  502. {
  503. UpdateInteractable();
  504. }
  505. private Dropdown GetDropdown()
  506. {
  507. return GameObject.Find("Dropdown").GetComponent<Dropdown>();
  508. }
  509. private Button GetBuyButton()
  510. {
  511. return GameObject.Find("Buy").GetComponent<Button>();
  512. }
  513. /// <summary>
  514. /// Gets the restore button when available
  515. /// </summary>
  516. /// <returns><c>null</c> or the restore button.</returns>
  517. private Button GetRestoreButton()
  518. {
  519. return GetButton ("Restore");
  520. }
  521. private Button GetRegisterButton()
  522. {
  523. return GetButton ("Register");
  524. }
  525. private Button GetLoginButton()
  526. {
  527. return GetButton ("Login");
  528. }
  529. private Button GetValidateButton()
  530. {
  531. return GetButton ("Validate");
  532. }
  533. private Button GetButton(string buttonName)
  534. {
  535. GameObject obj = GameObject.Find(buttonName);
  536. if (obj != null)
  537. {
  538. return obj.GetComponent <Button>();
  539. }
  540. else
  541. {
  542. return null;
  543. }
  544. }
  545. private Text GetText(bool right)
  546. {
  547. var which = right ? "TextR" : "TextL";
  548. return GameObject.Find(which).GetComponent<Text>();
  549. }
  550. private void LogProductDefinitions()
  551. {
  552. var products = m_Controller.products.all;
  553. foreach (var product in products) {
  554. #if UNITY_5_6_OR_NEWER
  555. Debug.Log(string.Format("id: {0}\nstore-specific id: {1}\ntype: {2}\nenabled: {3}\n", product.definition.id, product.definition.storeSpecificId, product.definition.type.ToString(), product.definition.enabled ? "enabled" : "disabled"));
  556. #else
  557. Debug.Log(string.Format("id: {0}\nstore-specific id: {1}\ntype: {2}\n", product.definition.id, product.definition.storeSpecificId, product.definition.type.ToString()));
  558. #endif
  559. }
  560. }
  561. }