No Description
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.

StandardPurchasingModule.cs 21KB

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