Facebook.Unity/Utils/CodelessIAPAutoLog.cs (112 lines of code) (raw):

/** * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. * * You are hereby granted a non-exclusive, worldwide, royalty-free license to use, * copy, modify, and distribute this software in source code or binary form for use * in connection with the web services and APIs provided by Facebook. * * As with any software that integrates with the Facebook platform, your use of * this software is subject to the Facebook Developer Principles and Policies * [http://developers.facebook.com/policy/]. This copyright notice shall be * included in all copies or substantial portions of the software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ namespace Facebook.Unity { using System; using System.Reflection; using UnityEngine; using UnityEngine.Events; using System.Collections.Generic; internal class CodelessIAPAutoLog { internal static void handlePurchaseCompleted(System.Object data) { try { if (!FB.Mobile.IsImplicitPurchaseLoggingEnabled()) { return; } object metadata = CodelessIAPAutoLog.GetProperty(data, "metadata"); object productDefinition = CodelessIAPAutoLog.GetProperty(data, "definition"); if (metadata == null || productDefinition == null) { return; } Decimal price = (Decimal)CodelessIAPAutoLog.GetProperty(metadata, "localizedPrice"); String currency = (String)CodelessIAPAutoLog.GetProperty(metadata, "isoCurrencyCode"); String id = (String)CodelessIAPAutoLog.GetProperty(productDefinition, "id"); FB.LogAppEvent( AppEventName.Purchased, (float)price, new Dictionary<string, object>() { { "_implicitlyLogged", "1" }, { AppEventParameterName.Currency, currency }, { AppEventParameterName.ContentID, id } }); } catch (System.Exception e) { FacebookLogger.Log("Failed to automatically handle Purchase Completed: " + e.Message); } } internal static void addListenerToIAPButtons(object listenerObject) { UnityEngine.Object[] iapButtons = FindObjectsOfTypeByName("IAPButton", "UnityEngine.Purchasing"); if (iapButtons == null) { return; } foreach (UnityEngine.Object btn in iapButtons) { addListenerToGameObject(btn, listenerObject); } } internal static void addListenerToGameObject(UnityEngine.Object gameObject, object listenerObject) { // Code stripping will work here when developer uses Unity IAP service the UnityEngine.Purchasing will be linked // If it's not enabled, this will return null and it's expected and handled in null check Type productType = FindTypeInAssemblies("Product", "UnityEngine.Purchasing"); if (productType == null) { return; } Type eventGeneric = typeof(UnityEvent<>); Type actionGeneric = typeof(UnityAction<>); Type[] typeArgs = { productType }; Type unityEventProduct = eventGeneric.MakeGenericType(typeArgs); Type unityActionProduct = actionGeneric.MakeGenericType(typeArgs); UnityEventBase onPurchaseComplete = (UnityEventBase)GetField(gameObject, "onPurchaseComplete"); MethodInfo addListener = unityEventProduct.GetMethod("AddListener"); MethodInfo removeListener = unityEventProduct.GetMethod("RemoveListener"); MethodInfo method = listenerObject.GetType().GetMethod( "onPurchaseCompleteHandler", BindingFlags.Instance | BindingFlags.Public ); removeListener.Invoke (onPurchaseComplete, new object[] { System.Delegate.CreateDelegate(unityActionProduct, listenerObject, method) }); addListener.Invoke (onPurchaseComplete, new object[] { System.Delegate.CreateDelegate(unityActionProduct, listenerObject, method) }); } private static System.Type FindTypeInAssemblies(string typeName, string nameSpace) { var assemblies = System.AppDomain.CurrentDomain.GetAssemblies(); for(int i = 0; i < assemblies.Length; i++) { var types = assemblies[i].GetTypes(); for(int n = 0; n < types.Length; n++) { if (typeName == types[n].Name && nameSpace == types[n].Namespace) { return types[n]; } } } return null; } private static UnityEngine.Object[] FindObjectsOfTypeByName(string typeName, string nameSpace) { // Code stripping will work here when developer uses Unity IAP service the UnityEngine.Purchasing will be linked // If it's not enabled, this will return null and it's expected and handled in null check Type type = FindTypeInAssemblies(typeName, nameSpace); if (type == null) { return null; } return UnityEngine.Object.FindObjectsOfType(type); } private static object GetField(object inObj, string fieldName) { object ret = null; FieldInfo info = inObj.GetType().GetField(fieldName); if (info != null) ret = info.GetValue(inObj); return ret; } private static object GetProperty(object inObj, string propertyName) { object ret = null; PropertyInfo info = inObj.GetType().GetProperty(propertyName); if (info != null) ret = info.GetValue(inObj, null); return ret; } } }