StandardPurchasingModule.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. using System;
  2. using System.Collections.Generic;
  3. using Uniject;
  4. using UnityEngine.Purchasing.Extension;
  5. using UnityEngine.Purchasing.Interfaces;
  6. using UnityEngine.Purchasing.Models;
  7. using UnityEngine.Purchasing.Telemetry;
  8. using UnityEngine.Purchasing.Utils;
  9. #if UNITY_PURCHASING_GPBL
  10. using UnityEngine.Purchasing.GooglePlayBilling;
  11. #endif
  12. namespace UnityEngine.Purchasing
  13. {
  14. /// <summary>
  15. /// Module for the standard stores covered by Unity;
  16. /// Apple App store, Google Play and more.
  17. /// </summary>
  18. public class StandardPurchasingModule : AbstractPurchasingModule, IAndroidStoreSelection
  19. {
  20. /// <summary>
  21. /// Obsolete and inaccurate. Do not use.
  22. /// </summary>
  23. [Obsolete("Not accurate. Use Version instead.", false)]
  24. public const string k_PackageVersion = "3.0.1";
  25. internal readonly string k_Version = "4.2.0-pre.1"; // NOTE: Changed using GenerateUnifiedIAP.sh before pack step.
  26. /// <summary>
  27. /// The version of com.unity.purchasing installed and the app was built using.
  28. /// </summary>
  29. public string Version => k_Version;
  30. private AppStore m_AppStorePlatform;
  31. private INativeStoreProvider m_NativeStoreProvider;
  32. private RuntimePlatform m_RuntimePlatform;
  33. private static StandardPurchasingModule ModuleInstance;
  34. internal IUtil util { get; private set; }
  35. internal ILogger logger { get; private set; }
  36. internal StoreInstance storeInstance { get; private set; }
  37. internal ITelemetryMetricsInstanceWrapper telemetryMetricsInstanceWrapper { get; set; }
  38. internal ITelemetryDiagnosticsInstanceWrapper telemetryDiagnosticsInstanceWrapper { get; set; }
  39. // Map Android store enums to their public names.
  40. // Necessary because store enum names and public names almost, but not quite, match.
  41. private static Dictionary<AppStore, string> AndroidStoreNameMap = new Dictionary<AppStore, string>() {
  42. { AppStore.AmazonAppStore, AmazonApps.Name },
  43. { AppStore.GooglePlay, GooglePlay.Name },
  44. { AppStore.UDP, UDP.Name},
  45. { AppStore.NotSpecified, GooglePlay.Name }
  46. };
  47. internal class StoreInstance
  48. {
  49. internal string storeName { get; }
  50. internal IStore instance { get; }
  51. internal StoreInstance(string name, IStore instance)
  52. {
  53. this.storeName = name;
  54. this.instance = instance;
  55. }
  56. }
  57. internal StandardPurchasingModule(IUtil util, ILogger logger, INativeStoreProvider nativeStoreProvider,
  58. RuntimePlatform platform, AppStore android, ITelemetryDiagnosticsInstanceWrapper telemetryDiagnosticsInstanceWrapper, ITelemetryMetricsInstanceWrapper telemetryMetricsInstanceWrapper)
  59. {
  60. this.util = util;
  61. this.logger = logger;
  62. m_NativeStoreProvider = nativeStoreProvider;
  63. m_RuntimePlatform = platform;
  64. useFakeStoreUIMode = FakeStoreUIMode.Default;
  65. useFakeStoreAlways = false;
  66. m_AppStorePlatform = android;
  67. this.telemetryDiagnosticsInstanceWrapper = telemetryDiagnosticsInstanceWrapper;
  68. this.telemetryMetricsInstanceWrapper = telemetryMetricsInstanceWrapper;
  69. }
  70. /// <summary>
  71. /// A property that retrieves the <c>AppStore</c> type.
  72. /// </summary>
  73. public AppStore appStore
  74. {
  75. get
  76. {
  77. return m_AppStorePlatform;
  78. }
  79. }
  80. // At some point we should remove this but to do so will cause a compile error
  81. // for App developers who used this property directly.
  82. private bool usingMockMicrosoft;
  83. /// <summary>
  84. /// The UI mode for the Fake store, if it's in use.
  85. /// </summary>
  86. public FakeStoreUIMode useFakeStoreUIMode { get; set; }
  87. /// <summary>
  88. /// Whether or not to use the Fake store.
  89. /// </summary>
  90. public bool useFakeStoreAlways { get; set; }
  91. /// <summary>
  92. /// Creates an instance of StandardPurchasingModule or retrieves the existing one.
  93. /// </summary>
  94. /// <returns> The existing instance or the one just created. </returns>
  95. public static StandardPurchasingModule Instance()
  96. {
  97. return Instance(AppStore.NotSpecified);
  98. }
  99. /// <summary>
  100. /// Creates an instance of StandardPurchasingModule or retrieves the existing one, specifying a type of App store.
  101. /// </summary>
  102. /// <param name="androidStore"> The type of Android Store with which to create the instance. </param>
  103. /// <returns> The existing instance or the one just created. </returns>
  104. public static StandardPurchasingModule Instance(AppStore androidStore)
  105. {
  106. if (null == ModuleInstance)
  107. {
  108. var logger = UnityEngine.Debug.unityLogger;
  109. var gameObject = new GameObject("IAPUtil");
  110. Object.DontDestroyOnLoad(gameObject);
  111. gameObject.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
  112. var util = gameObject.AddComponent<UnityUtil>();
  113. var textAsset = (Resources.Load("BillingMode") as TextAsset);
  114. StoreConfiguration config = null;
  115. if (null != textAsset)
  116. {
  117. config = StoreConfiguration.Deserialize(textAsset.text);
  118. }
  119. // No Android target specified at runtime, use the build time setting.
  120. if (androidStore == AppStore.NotSpecified)
  121. {
  122. // Default to Google Play if we don't have a build time store selection.
  123. androidStore = AppStore.GooglePlay;
  124. if (null != config)
  125. {
  126. var buildTimeStore = config.androidStore;
  127. if (buildTimeStore != AppStore.NotSpecified)
  128. {
  129. androidStore = buildTimeStore;
  130. }
  131. }
  132. }
  133. ModuleInstance = new StandardPurchasingModule(
  134. util,
  135. logger,
  136. new NativeStoreProvider(),
  137. Application.platform,
  138. androidStore,
  139. new TelemetryDiagnosticsInstanceWrapper(),
  140. new TelemetryMetricsInstanceWrapper());
  141. }
  142. return ModuleInstance;
  143. }
  144. /// <summary>
  145. /// Configures the StandardPurchasingModule.
  146. /// </summary>
  147. public override void Configure()
  148. {
  149. BindConfiguration<IGooglePlayConfiguration>(new FakeGooglePlayStoreConfiguration());
  150. BindExtension<IGooglePlayStoreExtensions>(new FakeGooglePlayStoreExtensions());
  151. BindConfiguration<IAppleConfiguration>(new FakeAppleConfiguation());
  152. BindExtension<IAppleExtensions>(new FakeAppleExtensions());
  153. BindConfiguration<IAmazonConfiguration>(new FakeAmazonExtensions());
  154. BindExtension<IAmazonExtensions>(new FakeAmazonExtensions());
  155. BindConfiguration<IMicrosoftConfiguration>(new MicrosoftConfiguration(this));
  156. BindExtension<IMicrosoftExtensions>(new FakeMicrosoftExtensions());
  157. BindConfiguration<IAndroidStoreSelection>(this);
  158. BindExtension<IUDPExtensions>(new FakeUDPExtension());
  159. BindExtension<ITransactionHistoryExtensions>(new FakeTransactionHistoryExtensions());
  160. // Our store implementations are singletons, we must not attempt to instantiate
  161. // them more than once.
  162. if (null == storeInstance)
  163. {
  164. storeInstance = InstantiateStore();
  165. }
  166. RegisterStore(storeInstance.storeName, storeInstance.instance);
  167. // Moving SetModule from reflection to an interface
  168. var internalStore = storeInstance.instance as IStoreInternal;
  169. if (internalStore != null)
  170. {
  171. // NB: as currently implemented this is also doing Init work for ManagedStore
  172. internalStore.SetModule(this);
  173. }
  174. // If we are using a JSONStore, bind to it to get transaction history.
  175. if ((this.util != null) && this.util.IsClassOrSubclass(typeof(JSONStore), storeInstance.instance.GetType()))
  176. {
  177. JSONStore jsonStore = (JSONStore)storeInstance.instance;
  178. BindExtension<ITransactionHistoryExtensions>(jsonStore);
  179. }
  180. }
  181. private StoreInstance InstantiateStore()
  182. {
  183. if (useFakeStoreAlways)
  184. {
  185. return new StoreInstance(FakeStore.Name, InstantiateFakeStore());
  186. }
  187. switch (m_RuntimePlatform)
  188. {
  189. case RuntimePlatform.OSXPlayer:
  190. m_AppStorePlatform = AppStore.MacAppStore;
  191. return new StoreInstance(MacAppStore.Name, InstantiateApple());
  192. case RuntimePlatform.IPhonePlayer:
  193. case RuntimePlatform.tvOS:
  194. m_AppStorePlatform = AppStore.AppleAppStore;
  195. return new StoreInstance(AppleAppStore.Name, InstantiateApple());
  196. case RuntimePlatform.Android:
  197. switch (m_AppStorePlatform)
  198. {
  199. case AppStore.UDP:
  200. return new StoreInstance(AndroidStoreNameMap[m_AppStorePlatform], InstantiateUDP());
  201. default:
  202. return new StoreInstance(AndroidStoreNameMap[m_AppStorePlatform], InstantiateAndroid());
  203. }
  204. case RuntimePlatform.WSAPlayerARM:
  205. case RuntimePlatform.WSAPlayerX64:
  206. case RuntimePlatform.WSAPlayerX86:
  207. m_AppStorePlatform = AppStore.WinRT;
  208. return new StoreInstance(WindowsStore.Name, instantiateWindowsStore());
  209. }
  210. m_AppStorePlatform = AppStore.fake;
  211. return new StoreInstance(FakeStore.Name, InstantiateFakeStore());
  212. }
  213. private IStore InstantiateAndroid()
  214. {
  215. if (m_AppStorePlatform == AppStore.GooglePlay)
  216. {
  217. return InstantiateGoogleStore();
  218. }
  219. else
  220. {
  221. var telemetryMetrics = new TelemetryMetricsService(telemetryMetricsInstanceWrapper);
  222. var store = new MetricizedJsonStore(telemetryMetrics);
  223. return InstantiateAndroidHelper(store);
  224. }
  225. }
  226. private IStore InstantiateGoogleStore()
  227. {
  228. IGooglePurchaseCallback googlePurchaseCallback = new GooglePlayPurchaseCallback();
  229. var googlePlayStoreService = BuildGooglePlayStoreServiceAar(googlePurchaseCallback);
  230. IGooglePlayStorePurchaseService googlePlayStorePurchaseService = new GooglePlayStorePurchaseService(googlePlayStoreService);
  231. IGooglePlayStoreFinishTransactionService googlePlayStoreFinishTransactionService = new GooglePlayStoreFinishTransactionService(googlePlayStoreService);
  232. IGoogleFetchPurchases googleFetchPurchases = new GoogleFetchPurchases(googlePlayStoreService, googlePlayStoreFinishTransactionService);
  233. var googlePlayConfiguration = BuildGooglePlayStoreConfiguration(googlePlayStoreService, googlePurchaseCallback);
  234. var telemetryDiagnostics = new TelemetryDiagnostics(telemetryDiagnosticsInstanceWrapper);
  235. var telemetryMetrics = new TelemetryMetricsService(telemetryMetricsInstanceWrapper);
  236. IGooglePlayStoreRetrieveProductsService googlePlayStoreRetrieveProductsService = new GooglePlayStoreRetrieveProductsService(
  237. googlePlayStoreService,
  238. googleFetchPurchases,
  239. googlePlayConfiguration);
  240. var googlePlayStoreExtensions = new MetricizedGooglePlayStoreExtensions(
  241. googlePlayStoreService,
  242. googlePlayStoreFinishTransactionService,
  243. telemetryDiagnostics,
  244. telemetryMetrics);
  245. GooglePlayStore googlePlayStore = new GooglePlayStore(
  246. googlePlayStoreRetrieveProductsService,
  247. googlePlayStorePurchaseService,
  248. googleFetchPurchases,
  249. googlePlayStoreFinishTransactionService,
  250. googlePurchaseCallback,
  251. googlePlayConfiguration,
  252. googlePlayStoreExtensions,
  253. util);
  254. util.AddPauseListener(googlePlayStore.OnPause);
  255. BindGoogleConfiguration(googlePlayConfiguration);
  256. BindGoogleExtension(googlePlayStoreExtensions);
  257. return googlePlayStore;
  258. }
  259. void BindGoogleExtension(GooglePlayStoreExtensions googlePlayStoreExtensions)
  260. {
  261. BindExtension<IGooglePlayStoreExtensions>(googlePlayStoreExtensions);
  262. }
  263. static GooglePlayConfiguration BuildGooglePlayStoreConfiguration(IGooglePlayStoreService googlePlayStoreService, IGooglePurchaseCallback googlePurchaseCallback)
  264. {
  265. GooglePlayConfiguration googlePlayConfiguration = new GooglePlayConfiguration(googlePlayStoreService);
  266. googlePurchaseCallback.SetStoreConfiguration(googlePlayConfiguration);
  267. return googlePlayConfiguration;
  268. }
  269. void BindGoogleConfiguration(GooglePlayConfiguration googlePlayConfiguration)
  270. {
  271. BindConfiguration<IGooglePlayConfiguration>(googlePlayConfiguration);
  272. }
  273. IGooglePlayStoreService BuildGooglePlayStoreServiceAar(IGooglePurchaseCallback googlePurchaseCallback)
  274. {
  275. var googleCachedQuerySkuDetailsService = new GoogleCachedQuerySkuDetailsService();
  276. var googleLastKnownProductService = new GoogleLastKnownProductService();
  277. var googlePurchaseStateEnumProvider = new GooglePurchaseStateEnumProvider();
  278. var googlePurchaseUpdatedListener = new GooglePurchaseUpdatedListener(googleLastKnownProductService, googlePurchaseCallback, googleCachedQuerySkuDetailsService, googlePurchaseStateEnumProvider);
  279. var googleBillingClient = new GoogleBillingClient(googlePurchaseUpdatedListener, util);
  280. var skuDetailsConverter = new SkuDetailsConverter();
  281. var retryPolicy = new ExponentialRetryPolicy();
  282. var googleQuerySkuDetailsService = new QuerySkuDetailsService(googleBillingClient, googleCachedQuerySkuDetailsService, skuDetailsConverter, retryPolicy);
  283. var purchaseService = new GooglePurchaseService(googleBillingClient, googlePurchaseCallback, googleQuerySkuDetailsService);
  284. var queryPurchasesService = new GoogleQueryPurchasesService(googleBillingClient, googleCachedQuerySkuDetailsService);
  285. var finishTransactionService = new GoogleFinishTransactionService(googleBillingClient, queryPurchasesService);
  286. var billingClientStateListener = new BillingClientStateListener();
  287. var priceChangeService = new GooglePriceChangeService(googleBillingClient, googleQuerySkuDetailsService);
  288. var telemetryMetrics = new TelemetryMetricsService(telemetryMetricsInstanceWrapper);
  289. googlePurchaseUpdatedListener.SetGoogleQueryPurchaseService(queryPurchasesService);
  290. return new MetricizedGooglePlayStoreService(
  291. googleBillingClient,
  292. googleQuerySkuDetailsService,
  293. purchaseService,
  294. finishTransactionService,
  295. queryPurchasesService,
  296. billingClientStateListener,
  297. priceChangeService,
  298. googleLastKnownProductService,
  299. telemetryMetrics
  300. );
  301. }
  302. private IStore InstantiateUDP()
  303. {
  304. var store = new UDPImpl();
  305. BindExtension<IUDPExtensions>(store);
  306. INativeUDPStore nativeUdpStore = (INativeUDPStore)GetAndroidNativeStore(store);
  307. store.SetNativeStore(nativeUdpStore);
  308. return store;
  309. }
  310. private IStore InstantiateAndroidHelper(JSONStore store)
  311. {
  312. store.SetNativeStore(GetAndroidNativeStore(store));
  313. return store;
  314. }
  315. private INativeStore GetAndroidNativeStore(JSONStore store)
  316. {
  317. return m_NativeStoreProvider.GetAndroidStore(store, m_AppStorePlatform, m_Binder, util);
  318. }
  319. #if UNITY_PURCHASING_GPBL
  320. private IStore InstantiateGooglePlayBilling()
  321. {
  322. var gameObject = new GameObject("GooglePlayBillingUtil");
  323. Object.DontDestroyOnLoad (gameObject);
  324. gameObject.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
  325. var _util = gameObject.AddComponent<GooglePlayBillingUtil>();
  326. var store = new GooglePlayStoreImpl(_util);
  327. BindExtension((IGooglePlayStoreExtensions) store);
  328. BindConfiguration((IGooglePlayConfiguration) store);
  329. return store;
  330. }
  331. #endif
  332. private IStore InstantiateApple()
  333. {
  334. var telemetryDiagnostics = new TelemetryDiagnostics(telemetryDiagnosticsInstanceWrapper);
  335. var telemetryMetrics = new TelemetryMetricsService(telemetryMetricsInstanceWrapper);
  336. var store = new MetricizedAppleStoreImpl(util, telemetryDiagnostics, telemetryMetrics);
  337. var appleBindings = m_NativeStoreProvider.GetStorekit(store);
  338. store.SetNativeStore(appleBindings);
  339. BindExtension<IAppleExtensions>(store);
  340. return store;
  341. }
  342. private WinRTStore windowsStore;
  343. private void UseMockWindowsStore(bool value)
  344. {
  345. if (null != windowsStore)
  346. {
  347. var iap = UnityEngine.Purchasing.Default.Factory.Create(value);
  348. windowsStore.SetWindowsIAP(iap);
  349. }
  350. }
  351. private IStore instantiateWindowsStore()
  352. {
  353. // Create a non mocked store by default.
  354. var iap = UnityEngine.Purchasing.Default.Factory.Create(false);
  355. windowsStore = new WinRTStore(iap, util, logger);
  356. // Microsoft require polling for new purchases on each app foregrounding.
  357. util.AddPauseListener(windowsStore.restoreTransactions);
  358. return windowsStore;
  359. }
  360. private IStore InstantiateFakeStore()
  361. {
  362. FakeStore fakeStore = null;
  363. if (useFakeStoreUIMode != FakeStoreUIMode.Default)
  364. {
  365. // To access class not available due to UnityEngine.UI conflicts with
  366. // unit-testing framework, instantiate via reflection
  367. fakeStore = new UIFakeStore();
  368. fakeStore.UIMode = useFakeStoreUIMode;
  369. }
  370. if (fakeStore == null)
  371. {
  372. fakeStore = new FakeStore();
  373. }
  374. return fakeStore;
  375. }
  376. /// <summary>
  377. /// The MicrosoftConfiguration is used to toggle between simulated
  378. /// and live IAP implementations.
  379. /// The switching is done in the StandardPurchasingModule,
  380. /// but we don't want the to implement IMicrosoftConfiguration since
  381. /// we want that implementation to be private and the module is public.
  382. /// </summary>
  383. private class MicrosoftConfiguration : IMicrosoftConfiguration
  384. {
  385. public MicrosoftConfiguration(StandardPurchasingModule module)
  386. {
  387. this.module = module;
  388. }
  389. private bool useMock;
  390. private StandardPurchasingModule module;
  391. public bool useMockBillingSystem
  392. {
  393. get
  394. {
  395. return useMock;
  396. }
  397. set
  398. {
  399. module.UseMockWindowsStore(value);
  400. useMock = value;
  401. }
  402. }
  403. }
  404. }
  405. }