MonetizationPlatform.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. #if UNITY_EDITOR
  2. using System;
  3. using System.IO;
  4. using System.Net;
  5. using System.Collections.Generic;
  6. #elif UNITY_ANDROID
  7. using System;
  8. using System.Collections.Generic;
  9. using UnityEngine.Advertisements.Purchasing;
  10. #elif UNITY_IOS
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Globalization;
  14. using System.Runtime.InteropServices;
  15. using AOT;
  16. using UnityEngine.Advertisements.Purchasing;
  17. #endif
  18. using UnityEngine.Advertisements;
  19. using UnityEngine.Advertisements.Utilities;
  20. namespace UnityEngine.Monetization
  21. {
  22. #if UNITY_EDITOR
  23. sealed internal class Platform : IMonetizationPlatform
  24. {
  25. public static Placeholder m_Placeholder;
  26. bool isInitialized;
  27. Configuration m_Configuration;
  28. static string s_BaseUrl = "http://editor-support.unityads.unity3d.com/games";
  29. static string s_Version = "3.7.5";
  30. private UnityLifecycleManager _callbackExecutor;
  31. public event EventHandler<PlacementContentReadyEventArgs> OnPlacementContentReady;
  32. public event EventHandler<PlacementContentStateChangeEventArgs> OnPlacementContentStateChange
  33. {
  34. add {}
  35. remove {}
  36. }
  37. public event EventHandler<UnityServicesErrorEventArgs> onError
  38. {
  39. add {}
  40. remove {}
  41. }
  42. public string version { get; } = "3.7.5";
  43. public Platform()
  44. {
  45. var callbackExecutorGameObject = new GameObject("UnityAdsCallbackExecutorObject")
  46. {
  47. hideFlags = HideFlags.HideAndDontSave | HideFlags.HideInInspector
  48. };
  49. _callbackExecutor = new UnityLifecycleManager();
  50. Object.DontDestroyOnLoad(callbackExecutorGameObject);
  51. }
  52. public void Initialize(string gameId, bool testMode)
  53. {
  54. var placeHolderGameObject = new GameObject("UnityMonetizationEditorPlaceHolderObject")
  55. {
  56. hideFlags = HideFlags.HideAndDontSave | HideFlags.HideInInspector
  57. };
  58. m_Placeholder = placeHolderGameObject.AddComponent<Placeholder>();
  59. string configurationUrl = string.Join("/", new string[]
  60. {
  61. s_BaseUrl,
  62. gameId,
  63. string.Join("&", new string[]
  64. {
  65. "configuration?platform=editor",
  66. "unityVersion=" + Uri.EscapeDataString(Application.unityVersion),
  67. "sdkVersionName=" + Uri.EscapeDataString(s_Version)
  68. })
  69. });
  70. WebRequest request = WebRequest.Create(configurationUrl);
  71. request.BeginGetResponse(result =>
  72. {
  73. WebResponse response = request.EndGetResponse(result);
  74. var reader = new StreamReader(response.GetResponseStream());
  75. string responseBody = reader.ReadToEnd();
  76. try
  77. {
  78. m_Configuration = new Configuration(responseBody);
  79. if (!m_Configuration.enabled)
  80. {
  81. Debug.LogWarning("gameId " + gameId + " is not enabled");
  82. }
  83. }
  84. catch (Exception exception)
  85. {
  86. Debug.LogError("Failed to parse configuration for gameId: " + gameId);
  87. Debug.Log(responseBody);
  88. Debug.LogError(exception.Message);
  89. }
  90. reader.Close();
  91. response.Close();
  92. if (m_Configuration != null)
  93. {
  94. foreach (KeyValuePair<string, PlacementContent> entry in m_Configuration.placementContents)
  95. {
  96. _callbackExecutor.Post(() =>
  97. {
  98. OnPlacementContentReady?.Invoke(this, new PlacementContentReadyEventArgs(entry.Key, entry.Value));
  99. });
  100. }
  101. }
  102. }, null);
  103. isInitialized = true;
  104. UnityEngine.Advertisements.Advertisement.Initialize(gameId, testMode);
  105. Debug.Log("UnityMonetizationEditor: Initialize(" + gameId + ", " + testMode + ");");
  106. }
  107. public PlacementContent GetPlacementContent(string placementID)
  108. {
  109. if (m_Configuration != null && m_Configuration.placementContents.ContainsKey(placementID))
  110. {
  111. return m_Configuration.placementContents[placementID];
  112. }
  113. else
  114. {
  115. return new ShowAdPlacementContent("fakeId", new EditorShowAdOperations());
  116. }
  117. }
  118. public INativePromoAdapter CreateNativePromoAdapter(PromoAdPlacementContent placementContent)
  119. {
  120. return new EditorNativePromoAdapter(placementContent);
  121. }
  122. public void SetMetaData(MetaData metaData)
  123. {
  124. }
  125. public void SetPurchasingAdapter(IPurchasingAdapter adapter)
  126. {
  127. }
  128. public bool isSupported
  129. {
  130. get
  131. {
  132. return Application.isEditor;
  133. }
  134. }
  135. public bool IsReady(string placementID)
  136. {
  137. return isInitialized && m_Configuration != null;
  138. }
  139. }
  140. #elif UNITY_ANDROID
  141. sealed internal class Platform : AndroidJavaProxy, IMonetizationPlatform, IPurchasingEventSender
  142. {
  143. private static readonly IDictionary<string, PlacementContentType> PlacementContentTypesMap = new Dictionary<string, PlacementContentType>
  144. {
  145. {"SHOW_AD", PlacementContentType.ShowAd},
  146. {"PROMO_AD", PlacementContentType.PromoAd},
  147. {"SINK_PROMO", PlacementContentType.SinkPromo},
  148. {"CUSTOM", PlacementContentType.Custom}
  149. };
  150. public event EventHandler<PlacementContentReadyEventArgs> OnPlacementContentReady;
  151. public event EventHandler<PlacementContentStateChangeEventArgs> OnPlacementContentStateChange;
  152. public event EventHandler<UnityServicesErrorEventArgs> onError;
  153. readonly AndroidJavaObject m_CurrentActivity;
  154. readonly IUnityLifecycleManager m_CallbackExecutor;
  155. readonly Purchase m_UnityMonetizationPurchase;
  156. readonly AndroidAnalytics m_UnityMonetizationAnalytics;
  157. private IDictionary<string, PlacementContent> m_PlacementContents = new Dictionary<string, PlacementContent>();
  158. private AndroidJavaClass m_unityMonetizationClass = new AndroidJavaClass("com.unity3d.services.monetization.UnityMonetization");
  159. private AndroidJavaClass m_unityServicesClass = new AndroidJavaClass("com.unity3d.services.UnityServices");
  160. private AndroidJavaClass m_unityPurchasingClass = new AndroidJavaClass("com.unity3d.services.purchasing.UnityPurchasing");
  161. public Platform() : base("com.unity3d.services.monetization.IUnityMonetizationListener")
  162. {
  163. var unityPlayerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
  164. this.m_CurrentActivity = unityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity");
  165. this.m_UnityMonetizationPurchase = new Purchase();
  166. this.m_UnityMonetizationAnalytics = new AndroidAnalytics();
  167. m_CallbackExecutor = new UnityLifecycleManager();
  168. }
  169. void onPlacementContentReady(string placementId, AndroidJavaObject placementcontent)
  170. {
  171. PlacementContentType type = this.getPlacementContentTypeForAndroidObject(placementcontent);
  172. IPlacementContentOperations operations = this.getPlacementContentOperationsForType(type, placementcontent);
  173. PlacementContent m_placementcontent = this.getPlacementContentForType(type, placementId, operations);
  174. m_placementcontent.extras = JavaMapUtilities.GetDictionaryForJavaMap(placementcontent.Call<AndroidJavaObject>("getExtras"));
  175. if (m_PlacementContents.ContainsKey(placementId))
  176. {
  177. m_PlacementContents.Remove(placementId);
  178. }
  179. m_PlacementContents.Add(placementId, m_placementcontent);
  180. m_CallbackExecutor.Post(() =>
  181. {
  182. OnPlacementContentReady?.Invoke(this, new PlacementContentReadyEventArgs(placementId, m_placementcontent));
  183. });
  184. }
  185. private IPlacementContentOperations getPlacementContentOperationsForType(PlacementContentType type, AndroidJavaObject placementcontent)
  186. {
  187. switch (type)
  188. {
  189. case PlacementContentType.ShowAd:
  190. return new AndroidShowAdOperations(m_CallbackExecutor, placementcontent);
  191. case PlacementContentType.PromoAd:
  192. return new AndroidPromoAdOperations(m_CallbackExecutor, placementcontent);
  193. default:
  194. return new AndroidPlacementContentOperations(placementcontent);
  195. }
  196. }
  197. private PlacementContent getPlacementContentForType(PlacementContentType type, string placementId, IPlacementContentOperations operations)
  198. {
  199. switch (type)
  200. {
  201. case PlacementContentType.ShowAd:
  202. return new ShowAdPlacementContent(placementId, operations as IShowAdOperations);
  203. case PlacementContentType.PromoAd:
  204. return new PromoAdPlacementContent(placementId, operations as IPromoAdOperations);
  205. default:
  206. return new PlacementContent(placementId, operations);
  207. }
  208. }
  209. private PlacementContentState getPlacementContentStateForJavaPlacementContentState(AndroidJavaObject javaObject)
  210. {
  211. return (PlacementContentState)JavaEnumUtilities.GetEnumOrdinal(javaObject);
  212. }
  213. void onPlacementContentStateChange(string placementId, AndroidJavaObject placementcontent, AndroidJavaObject previousState, AndroidJavaObject newState)
  214. {
  215. if (m_PlacementContents.ContainsKey(placementId))
  216. {
  217. PlacementContent m_placementContent = m_PlacementContents[placementId];
  218. PlacementContentState m_previousState = this.getPlacementContentStateForJavaPlacementContentState(previousState);
  219. PlacementContentState m_newState = this.getPlacementContentStateForJavaPlacementContentState(newState);
  220. m_CallbackExecutor.Post(() =>
  221. {
  222. OnPlacementContentStateChange?.Invoke(this,
  223. new PlacementContentStateChangeEventArgs(placementId, m_placementContent, m_previousState, m_newState));
  224. });
  225. }
  226. }
  227. void onUnityServicesError(AndroidJavaObject error, string message)
  228. {
  229. var errOrdinal = error?.Call<int>("ordinal") ?? -1;
  230. m_CallbackExecutor.Post(() =>
  231. {
  232. onError?.Invoke(this, new UnityServicesErrorEventArgs(errOrdinal, message));
  233. });
  234. }
  235. public void Initialize(string gameId, bool testMode)
  236. {
  237. m_UnityMonetizationPurchase.Initialize(this);
  238. m_UnityMonetizationAnalytics.Initialize();
  239. m_unityMonetizationClass.CallStatic("initialize", this.m_CurrentActivity, gameId, this, testMode);
  240. }
  241. public void SetPurchasingAdapter(IPurchasingAdapter adapter)
  242. {
  243. m_unityPurchasingClass.CallStatic("setAdapter", new AndroidPurchasingAdapter(adapter));
  244. }
  245. public string version => m_unityServicesClass.CallStatic<string>("getVersion");
  246. public bool isSupported => m_unityServicesClass.CallStatic<bool>("isSupported");
  247. private PlacementContentType getPlacementContentTypeForAndroidObject(AndroidJavaObject javaObject)
  248. {
  249. var placementcontentType = javaObject.SafeStringCall("getType");
  250. if (PlacementContentTypesMap.ContainsKey(placementcontentType))
  251. {
  252. return PlacementContentTypesMap[placementcontentType];
  253. }
  254. return PlacementContentType.Custom;
  255. }
  256. public bool IsReady(string placementID)
  257. {
  258. return m_unityMonetizationClass.CallStatic<bool>("isReady", placementID);
  259. }
  260. public PlacementContent GetPlacementContent(string placementID)
  261. {
  262. if (m_PlacementContents.ContainsKey(placementID))
  263. {
  264. return m_PlacementContents[placementID];
  265. }
  266. return null;
  267. }
  268. public INativePromoAdapter CreateNativePromoAdapter(PromoAdPlacementContent placementContent)
  269. {
  270. return new AndroidNativePromoAdapter(placementContent);
  271. }
  272. public void SetMetaData(MetaData metaData)
  273. {
  274. var metaDataObject = new AndroidJavaObject("com.unity3d.ads.metadata.MetaData", m_CurrentActivity);
  275. metaDataObject.Call("setCategory", metaData.category);
  276. foreach (var entry in metaData)
  277. {
  278. metaDataObject.Call<bool>("set", entry.Key, entry.Value);
  279. }
  280. metaDataObject.Call("commit");
  281. }
  282. void IPurchasingEventSender.SendPurchasingEvent(string payload)
  283. {
  284. m_UnityMonetizationPurchase.SendEvent(payload);
  285. }
  286. private class AndroidPurchasingAdapter : AndroidJavaProxy
  287. {
  288. private IPurchasingAdapter adapter;
  289. public AndroidPurchasingAdapter(IPurchasingAdapter adapter) : base("com.unity3d.services.purchasing.core.IPurchasingAdapter")
  290. {
  291. this.adapter = adapter;
  292. }
  293. public void retrieveProducts(AndroidJavaObject listener)
  294. {
  295. this.adapter.RetrieveProducts(new AndroidRetrieveProductsListener(listener));
  296. }
  297. public void onPurchase(string productID, AndroidJavaObject javaListener, AndroidJavaObject javaExtras)
  298. {
  299. IDictionary<string, object> extras = JavaMapUtilities.GetDictionaryForJavaMap(javaExtras);
  300. this.adapter.Purchase(productID, new AndroidTransactionListener(javaListener), extras);
  301. }
  302. private class AndroidRetrieveProductsListener : IRetrieveProductsListener
  303. {
  304. private AndroidJavaObject listener;
  305. public AndroidRetrieveProductsListener(AndroidJavaObject listener)
  306. {
  307. this.listener = listener;
  308. }
  309. public void OnProductsRetrieved(ICollection<Product> products)
  310. {
  311. var productsList = this.getJavaProductList(products);
  312. listener.Call("onProductsRetrieved", productsList);
  313. }
  314. private AndroidJavaObject getJavaProductList(ICollection<Product> products)
  315. {
  316. AndroidJavaObject javaProductList = new AndroidJavaObject("java.util.ArrayList");
  317. foreach (var product in products)
  318. {
  319. javaProductList.Call<bool>("add", this.getJavaProduct(product));
  320. }
  321. return javaProductList;
  322. }
  323. private AndroidJavaObject getJavaProduct(Product product)
  324. {
  325. AndroidJavaObject builder = new AndroidJavaClass("com.unity3d.services.purchasing.core.Product")
  326. .CallStatic<AndroidJavaObject>("newBuilder");
  327. builder.Call<AndroidJavaObject>("withProductId", product.productId);
  328. builder.Call<AndroidJavaObject>("withLocalizedPriceString", product.localizedPriceString);
  329. builder.Call<AndroidJavaObject>("withLocalizedTitle", product.localizedTitle);
  330. builder.Call<AndroidJavaObject>("withIsoCurrencyCode", product.isoCurrencyCode);
  331. builder.Call<AndroidJavaObject>("withLocalizedPrice", (double)product.localizedPrice);
  332. builder.Call<AndroidJavaObject>("withLocalizedDescription", product.localizedDescription);
  333. builder.Call<AndroidJavaObject>("withProductType", product.productType);
  334. return builder.Call<AndroidJavaObject>("build");
  335. }
  336. }
  337. private class AndroidTransactionListener : ITransactionListener
  338. {
  339. private AndroidJavaObject listener;
  340. public AndroidTransactionListener(AndroidJavaObject listener)
  341. {
  342. this.listener = listener;
  343. }
  344. public void OnTransactionComplete(TransactionDetails details)
  345. {
  346. listener.Call("onTransactionComplete", AndroidNativePromoAdapter.getJavaTransactionDetails(details));
  347. }
  348. public void OnTransactionError(TransactionErrorDetails details)
  349. {
  350. listener.Call("onTransactionError", AndroidNativePromoAdapter.getJavaTransactionErrorDetails(details));
  351. }
  352. }
  353. }
  354. }
  355. #elif UNITY_IOS
  356. public class Platform : IMonetizationPlatform
  357. {
  358. private static readonly IDictionary<string, PlacementContentType> PlacementContentTypesMap = new Dictionary<string, PlacementContentType>
  359. {
  360. {"SHOW_AD", PlacementContentType.ShowAd},
  361. {"PROMO_AD", PlacementContentType.PromoAd},
  362. {"SINK_PROMO", PlacementContentType.SinkPromo},
  363. {"CUSTOM", PlacementContentType.Custom}
  364. };
  365. [DllImport("__Internal")]
  366. static extern bool UnityMonetizationIsReady(string placementId);
  367. [DllImport("__Internal")]
  368. static extern void UnityMonetizationInitialize(string gameId, bool isTestMode);
  369. [DllImport("__Internal")]
  370. static extern bool UnityMonetizationIsSupported();
  371. [DllImport("__Internal")]
  372. static extern string UnityMonetizationGetPlacementContentType(IntPtr pPlacementContent);
  373. [DllImport("__Internal")]
  374. [return : MarshalAs(UnmanagedType.LPWStr)]
  375. static extern string UnityMonetizationGetPlacementContentExtras(IntPtr pPlacementContent);
  376. [DllImport("__Internal")]
  377. static extern void UnityAdsSetMetaData(string category, string data);
  378. [DllImport("__Internal")]
  379. static extern string UnityAdsGetVersion();
  380. private readonly IUnityLifecycleManager _callbackExecutor;
  381. private readonly IDictionary<string, PlacementContent> _placementContents = new Dictionary<string, PlacementContent>();
  382. public event EventHandler<PlacementContentReadyEventArgs> OnPlacementContentReady;
  383. public event EventHandler<PlacementContentStateChangeEventArgs> OnPlacementContentStateChange;
  384. public event EventHandler<UnityServicesErrorEventArgs> onError;
  385. public Platform()
  386. {
  387. _callbackExecutor = new UnityLifecycleManager();
  388. }
  389. public void Initialize(string gameId, bool testMode)
  390. {
  391. PlatformCallbacksWrapper.Platform = this;
  392. new IosAnalytics().Initialize();
  393. new PurchasingPlatform().Initialize();
  394. UnityMonetizationInitialize(gameId, testMode);
  395. }
  396. public bool IsReady(string placementId)
  397. {
  398. return UnityMonetizationIsReady(placementId);
  399. }
  400. public void SetPurchasingAdapter(IPurchasingAdapter adapter)
  401. {
  402. PurchasingAdapter.Adapter = adapter;
  403. }
  404. public PlacementContent GetPlacementContent(string placementId)
  405. {
  406. if (_placementContents.ContainsKey(placementId))
  407. {
  408. return _placementContents[placementId];
  409. }
  410. return null;
  411. }
  412. public INativePromoAdapter CreateNativePromoAdapter(PromoAdPlacementContent placementContent)
  413. {
  414. return new IosNativePromoAdapter(placementContent);
  415. }
  416. public void SetMetaData(MetaData metaData)
  417. {
  418. UnityAdsSetMetaData(metaData.category, metaData.ToJSON());
  419. }
  420. public bool isSupported => UnityMonetizationIsSupported();
  421. public string version => UnityAdsGetVersion();
  422. private void OnNativePlacementContentReady(string placementId, IntPtr pPlacementContent)
  423. {
  424. var type = GetPlacementContentTypeForPlacementContentPtr(pPlacementContent);
  425. var operations = GetPlacementContentOperationsForType(type, pPlacementContent);
  426. var placementContent = GetPlacementContentForType(type, placementId, operations);
  427. var extras = GetPlacementContentExtras(pPlacementContent);
  428. placementContent.extras = extras;
  429. if (_placementContents.ContainsKey(placementId))
  430. {
  431. _placementContents.Remove(placementId);
  432. }
  433. _placementContents.Add(placementId, placementContent);
  434. _callbackExecutor.Post(() =>
  435. {
  436. OnPlacementContentReady?.Invoke(this, new PlacementContentReadyEventArgs(placementId, placementContent));
  437. });
  438. }
  439. private IDictionary<string, object> GetPlacementContentExtras(IntPtr pPlacementContent)
  440. {
  441. var json = UnityMonetizationGetPlacementContentExtras(pPlacementContent);
  442. if (json == null)
  443. {
  444. return new Dictionary<string, object>();
  445. }
  446. var deserialized = MiniJSON.Json.Deserialize(json);
  447. if (deserialized is IDictionary<string, object> objects)
  448. {
  449. return objects;
  450. }
  451. return new Dictionary<string, object>();
  452. }
  453. private static PlacementContentType GetPlacementContentTypeForPlacementContentPtr(IntPtr pPlacementContent)
  454. {
  455. string type = UnityMonetizationGetPlacementContentType(pPlacementContent);
  456. return PlacementContentTypesMap[type];
  457. }
  458. private IPlacementContentOperations GetPlacementContentOperationsForType(PlacementContentType type, IntPtr pPlacementContent)
  459. {
  460. switch (type)
  461. {
  462. case PlacementContentType.ShowAd:
  463. return new IosShowAdOperations(pPlacementContent, _callbackExecutor);
  464. case PlacementContentType.PromoAd:
  465. return new IosPromoAdOperations(pPlacementContent, _callbackExecutor);
  466. default:
  467. return new IosPlacementContentOperations(pPlacementContent);
  468. }
  469. }
  470. private PlacementContent GetPlacementContentForType(PlacementContentType type, string placementId, IPlacementContentOperations operations)
  471. {
  472. switch (type)
  473. {
  474. case PlacementContentType.ShowAd:
  475. return new ShowAdPlacementContent(placementId, operations as IShowAdOperations);
  476. case PlacementContentType.PromoAd:
  477. return new PromoAdPlacementContent(placementId, operations as IPromoAdOperations);
  478. default:
  479. return new PlacementContent(placementId, operations);
  480. }
  481. }
  482. internal void OnNativePlacementContentStateChanged(string placementId, IntPtr pPlacementContent, int previousState, int newState)
  483. {
  484. if (_placementContents.ContainsKey(placementId))
  485. {
  486. var placementContent = _placementContents[placementId];
  487. _callbackExecutor.Post(() =>
  488. {
  489. OnPlacementContentStateChange?.Invoke(this, new PlacementContentStateChangeEventArgs(placementId, placementContent, (PlacementContentState)previousState, (PlacementContentState)newState));
  490. });
  491. }
  492. }
  493. private void OnNativeError(long error, string message)
  494. {
  495. onError?.Invoke(this, new UnityServicesErrorEventArgs(error, message));
  496. }
  497. private static class PlatformCallbacksWrapper
  498. {
  499. internal static Platform Platform { get; set; }
  500. delegate void OnPlacementContentReadyCallback(string placementId, IntPtr placementContent);
  501. delegate void OnPlacementContentStateChangedCallback(string placementId, IntPtr placementContent, int previousState, int newState);
  502. delegate void OnErrorCallback(long error, string message);
  503. [StructLayout(LayoutKind.Sequential)]
  504. struct UnityMonetiztionCallbacks
  505. {
  506. public OnPlacementContentReadyCallback onPlacementContentReadyCallback;
  507. public OnPlacementContentStateChangedCallback onPlacementContentStateChangedCallback;
  508. public OnErrorCallback onErrorCallback;
  509. }
  510. [DllImport("__Internal")]
  511. private static extern void UnityMonetizationSetMonetizationCallbacks(ref UnityMonetiztionCallbacks callback);
  512. static PlatformCallbacksWrapper()
  513. {
  514. var callbacks = new UnityMonetiztionCallbacks
  515. {
  516. onPlacementContentReadyCallback = OnPlacementContentReady,
  517. onPlacementContentStateChangedCallback = OnPlacementContentChanged,
  518. onErrorCallback = OnError
  519. };
  520. UnityMonetizationSetMonetizationCallbacks(ref callbacks);
  521. }
  522. [MonoPInvokeCallback(typeof(OnPlacementContentReadyCallback))]
  523. private static void OnPlacementContentReady(string placementId, IntPtr placementContent)
  524. {
  525. Platform?.OnNativePlacementContentReady(placementId, placementContent);
  526. }
  527. [MonoPInvokeCallback(typeof(OnPlacementContentStateChangedCallback))]
  528. private static void OnPlacementContentChanged(string placementId, IntPtr placementContent, int previousState, int newState)
  529. {
  530. Platform?.OnNativePlacementContentStateChanged(placementId, placementContent, previousState, newState);
  531. }
  532. [MonoPInvokeCallback(typeof(OnPlacementContentStateChangedCallback))]
  533. private static void OnError(long error, string message)
  534. {
  535. Platform?.OnNativeError(error, message);
  536. }
  537. }
  538. private static class PurchasingAdapter
  539. {
  540. internal static IPurchasingAdapter Adapter { private get; set; }
  541. delegate void OnRetrieveProductsCallback(IntPtr listener);
  542. delegate void OnPurchaseProductCallback(string productId, IntPtr callbacks);
  543. [StructLayout(LayoutKind.Sequential)]
  544. struct UnityPurchasingCallbacks
  545. {
  546. public OnRetrieveProductsCallback onRetrieveProductsCallback;
  547. public OnPurchaseProductCallback onPurchaseProductCallback;
  548. }
  549. [DllImport("__Internal")]
  550. private static extern void UnityPurchasingSetPurchasingAdapterCallbacks(ref UnityPurchasingCallbacks callback);
  551. static PurchasingAdapter()
  552. {
  553. var callbacks = new UnityPurchasingCallbacks
  554. {
  555. onRetrieveProductsCallback = OnRetrieveProducts,
  556. onPurchaseProductCallback = OnPurchaseProduct
  557. };
  558. UnityPurchasingSetPurchasingAdapterCallbacks(ref callbacks);
  559. }
  560. [MonoPInvokeCallback(typeof(OnRetrieveProductsCallback))]
  561. private static void OnRetrieveProducts(IntPtr pCallback)
  562. {
  563. Adapter?.RetrieveProducts(new IosRetrieveProductsListener(pCallback));
  564. }
  565. [MonoPInvokeCallback(typeof(OnPurchaseProductCallback))]
  566. private static void OnPurchaseProduct(string productId, IntPtr pCallbacks)
  567. {
  568. Adapter?.Purchase(productId, new IosTransactionListener(pCallbacks), new Dictionary<string, object>());
  569. }
  570. }
  571. private class IosRetrieveProductsListener : IRetrieveProductsListener
  572. {
  573. // Note, this struct layout matches UnityMonetizationPurchasingAdapter.mm
  574. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  575. public struct NativeProduct
  576. {
  577. [MarshalAs(UnmanagedType.LPWStr)]
  578. public string productId;
  579. [MarshalAs(UnmanagedType.LPWStr)]
  580. public string localizedTitle;
  581. [MarshalAs(UnmanagedType.LPWStr)]
  582. public string localizedDescription;
  583. [MarshalAs(UnmanagedType.LPWStr)]
  584. public string localizedPriceString;
  585. [MarshalAs(UnmanagedType.LPWStr)]
  586. public string isoCurrencyCode;
  587. [MarshalAs(UnmanagedType.LPWStr)]
  588. public string productType;
  589. [MarshalAs(UnmanagedType.R8)]
  590. public double localizedPrice;
  591. }
  592. [DllImport("__Internal")]
  593. private static extern IntPtr UnityPurchasingAdapterAllocateProductsArray(int num);
  594. [DllImport("__Internal")]
  595. private static extern void UnityPurchasingAddItemToProductsArray(IntPtr pArray, ref NativeProduct product);
  596. [DllImport("__Internal")]
  597. private static extern void UnityPurchasingInvokeRetrieveProductsCallback(IntPtr callback, IntPtr pArray);
  598. private readonly IntPtr _pCallback;
  599. public IosRetrieveProductsListener(IntPtr callback)
  600. {
  601. _pCallback = callback;
  602. }
  603. public void OnProductsRetrieved(ICollection<Product> products)
  604. {
  605. var pProducts = UnityPurchasingAdapterAllocateProductsArray(products.Count);
  606. foreach (var product in products)
  607. {
  608. var nativeProduct = new NativeProduct
  609. {
  610. productId = product.productId,
  611. localizedTitle = product.localizedTitle,
  612. localizedDescription = product.localizedDescription,
  613. localizedPriceString = product.localizedPriceString,
  614. isoCurrencyCode = product.isoCurrencyCode,
  615. productType = product.productType,
  616. localizedPrice = (double)product.localizedPrice
  617. };
  618. UnityPurchasingAddItemToProductsArray(pProducts, ref nativeProduct);
  619. }
  620. UnityPurchasingInvokeRetrieveProductsCallback(_pCallback, pProducts);
  621. }
  622. }
  623. private class IosTransactionListener : ITransactionListener
  624. {
  625. [DllImport("__Internal")]
  626. private static extern void UnityPurchasingInvokeTransactionCompleteCallback(IntPtr pCallbacks, [MarshalAs(UnmanagedType.LPWStr)] string transactionDetails);
  627. [DllImport("__Internal")]
  628. private static extern void UnityPurchasingInvokeTransactionErrorCallback(IntPtr pCallbacks, [MarshalAs(UnmanagedType.LPWStr)] string transactionErrorDetails);
  629. private readonly IntPtr _pCallbacks;
  630. public IosTransactionListener(IntPtr pCallbacks)
  631. {
  632. _pCallbacks = pCallbacks;
  633. }
  634. public void OnTransactionComplete(TransactionDetails details)
  635. {
  636. UnityPurchasingInvokeTransactionCompleteCallback(_pCallbacks, MiniJSON.Json.Serialize(details.ToJsonDictionary()));
  637. }
  638. public void OnTransactionError(TransactionErrorDetails details)
  639. {
  640. UnityPurchasingInvokeTransactionErrorCallback(_pCallbacks, MiniJSON.Json.Serialize(details.ToJsonDictionary()));
  641. }
  642. }
  643. }
  644. #endif
  645. }