using System; using System.Collections; using System.Collections.Generic; using com.amazon.device.iap.cpt; using UnityEngine; public class IAPManagerV2 : MonoBehaviour { [SerializeField] private GameObject purchaseLoader; private IAmazonIapV2 _iapService; private Dictionary> _shopItems = new Dictionary>(); private Dictionary _shopItemPrices = new Dictionary(); private Dictionary _amazonResponseReceived = new Dictionary(); private int _amazonRequestTimeout; HashSet fulfilledPurchases = new HashSet(); public Action purchaseCompleteAction; public Action purchaseFailedAction; [SerializeField] string purchaseCompleteEvent; private void Awake() { _iapService = AmazonIapV2Impl.Instance; _iapService.AddGetPurchaseUpdatesResponseListener(EventHandler); _iapService.AddPurchaseResponseListener(EventHandler); _iapService.AddGetProductDataResponseListener(EventHandler); GetPurchaseUpdates(); } private void InvokePurchaseComplete(Product product) { purchaseCompleteAction?.Invoke(product); } private void InvokePurchaseFailed() { purchaseFailedAction?.Invoke(); DestroyLoader(); } private void GetProductData() { SkusInput request = new SkusInput(); List list = new List(); foreach (var item in _shopItems) { list.Add(item.Key); } request.Skus = list; Analytics.anl.SendEvent("AMAZON_FETCH_PRODUCTS"); RequestOutput response = _iapService.GetProductData(request); string requestIdString = response.RequestId; if (requestIdString != null) { _amazonResponseReceived[requestIdString] = false; StartCoroutine(sendFailureEventOnTimeout("AMAZON_FETCH_PRODUCTS_FAILED", requestIdString)); } else Analytics.anl.SendEvent("AMAZON_IAP_FAILURE"); } public void Purchase(string sku, string purchaseName) { SetProductPayoutsToCache(sku, purchaseName); Analytics.anl.SendEvent("PURCHASE_CLICKED", new Dictionary() { { "misc", sku } }); purchaseLoader.SetActive(true); if (Debug.isDebugBuild) { StartCoroutine(DebugDelayedPurchase(sku, 3, simulateSuccessInEditor: true)); return; } SkuInput request = new SkuInput(); request.Sku = sku; RequestOutput response = _iapService.Purchase(request); string requestIdString = response.RequestId; if (requestIdString != null) { _amazonResponseReceived[requestIdString] = false; StartCoroutine(FailsafeManualFulfillPurchase(requestIdString)); StartCoroutine(sendFailureEventOnTimeout("AMAZON_PURCHASE_FAILED", requestIdString)); } else Analytics.anl.SendEvent("AMAZON_IAP_FAILURE"); } private void EventHandler(PurchaseResponse args) { DestroyLoader(); if (args.RequestId != null) _amazonResponseReceived[args.RequestId] = true; if (args.Status == "FAILED" || args.Status == "INVALID_SKU") { Analytics.anl.SendEvent("AMAZON_PURCHASE_FAILED", new Dictionary() { { "misc", "Response received, status " + args.Status } }); InvokePurchaseFailed(); return; } string receiptId = args.PurchaseReceipt.ReceiptId; string sku = args.PurchaseReceipt.Sku; Analytics.anl.SendEvent("AMAZON_PURCHASE_SUCCESS", new Dictionary() { { "purchase_code", sku } }); FulfillPurchase(new Product(sku, receiptId, _shopItems[sku])); } private IEnumerator FailsafeManualFulfillPurchase(string requestIdString) { yield return new WaitForSeconds(_amazonRequestTimeout); if (!_amazonResponseReceived[requestIdString]) GetPurchaseUpdates(); } public void GetPurchaseUpdates() { ResetInput request = new ResetInput(); // false = get new purchases since last time this was called, true = get all purchases for user request.Reset = true; // ServerEventSender.SendEvent("AMAZON_FETCH_UNFULFILLED"); RequestOutput response = _iapService.GetPurchaseUpdates(request); string requestIdString = response.RequestId; if (requestIdString != null) { _amazonResponseReceived[requestIdString] = false; StartCoroutine(sendFailureEventOnTimeout("AMAZON_FETCH_UNFULFILLED_FAILED", requestIdString)); } else Analytics.anl.SendEvent("AMAZON_IAP_FAILURE"); } /// /// /// /// Description: /// userId = args.AmazonUserData.UserId; /// marketplace = args.AmazonUserData.Marketplace; /// hasMore = args.HasMore; /// status = args.Status; /// private void EventHandler(GetPurchaseUpdatesResponse args) { if (args.RequestId != null) _amazonResponseReceived[args.RequestId] = true; List receipts = args.Receipts; Debug.Log("Got events"); int numOfReceipts = 0; if (receipts != null) { Debug.Log("Got reciepts"); numOfReceipts = receipts.Count; } if (numOfReceipts.Equals(0)) { Debug.Log("Got no reciepts"); DestroyLoader(); InvokePurchaseFailed(); } Analytics.anl.SendEvent("AMAZON_UNFULFILLED_RECEIVED", new Dictionary() { { "misc", "unfulfilled purchases: " + numOfReceipts.ToString() } }); foreach (var receipt in receipts) { if (_shopItems.ContainsKey(receipt.Sku)) FulfillPurchase(new Product(receipt.Sku, receipt.ReceiptId, _shopItems[receipt.Sku])); else FulfillPurchase(new Product(receipt.Sku, receipt.ReceiptId, GetLastProductPayoutsFromCache())); } } private Dictionary GetLastProductPayoutsFromCache() { Dictionary payouts = new Dictionary() { { Constants.Coins, long.Parse(PlayerPrefs.GetString(Constants.LastProductClicked_Coins, "0")) } }; return payouts; } private void SetProductPayoutsToCache(string sku, string purchaseName) { long spins = 0, coins = 0; var shortenedShopItems = JsonUtility.ToJson(_shopItems.Keys).Replace("island_lords_prod_", ""); if (!_shopItems.ContainsKey(sku)) Analytics.anl.SendEvent("ERROR", new Dictionary() { { "misc", sku + "-No such product sku in config. Not a crash, but investigate!" }, { "comment", shortenedShopItems } }); else { if (_shopItems[sku].ContainsKey(Constants.Coins)) coins = _shopItems[sku][Constants.Coins]; } PlayerPrefs.SetString(Constants.LastProductClicked_Spins, spins.ToString()); PlayerPrefs.SetString(Constants.LastProductClicked_Coins, coins.ToString()); PlayerPrefs.SetString(Constants.LastProductClicked_Name, purchaseName); PlayerPrefs.Save(); } private void EventHandler(GetProductDataResponse args) { if (args.RequestId != null) _amazonResponseReceived[args.RequestId] = true; Dictionary productDataMap = args.ProductDataMap; List unavailableSkus = args.UnavailableSkus; string status = args.Status; if (productDataMap != null && productDataMap.Count > 0) Analytics.anl.SendEvent("AMAZON_FETCH_PRODUCTS_SUCCESS"); else Analytics.anl.SendEvent("AMAZON_FETCH_PRODUCTS_FAILED"); } private void FulfillPurchase(Product product) { if (!fulfilledPurchases.Contains(product.receiptId)) { DestroyLoader(); InvokePurchaseComplete(product); fulfilledPurchases.Add(product.receiptId); } } public void NotifyFulfillment(Product product, string purchaseName) { // Construct object passed to operation as input NotifyFulfillmentInput request = new NotifyFulfillmentInput(); // Set input values request.ReceiptId = product.receiptId; request.FulfillmentResult = "FULFILLED"; // Call synchronous operation with input object _iapService.NotifyFulfillment(request); string productPayouts = JsonUtility.ToJson(_shopItems[product.sku]); long coinsPurchased = 0; if (productPayouts.Contains(Constants.Coins)) coinsPurchased = _shopItems[product.sku][Constants.Coins]; if (!Application.isEditor) { Analytics.anl.SendEvent("AMAZON_PURCHASE_FULFILLED", new Dictionary() { { "purchase_code", product.sku }, { "purchase_payed", _shopItemPrices[product.sku].ToString() }, { "misc", productPayouts }, { "purchase_comment", purchaseName }, { "win_amount", coinsPurchased.ToString() } }); } } private void DefaultPurchaseFulfillment(Product product) { if (product.payouts.ContainsKey(Constants.Coins)) NotifyFulfillment(product, PlayerPrefs.GetString(Constants.LastProductClicked_Name)); } private IEnumerator sendFailureEventOnTimeout(string eventName, string requestId) { yield return new WaitForSeconds(_amazonRequestTimeout); if (!_amazonResponseReceived[requestId]) { Analytics.anl.SendEvent(eventName, new Dictionary() { { "misc", "After " + _amazonRequestTimeout + " seconds" } }); } } public void InitIapWithConfigs() { _shopItems.Add(GlobalValues.config.gameData.shop.items[0].itemCode, new Dictionary { { "Coins", 500 }}); _shopItems.Add(GlobalValues.config.gameData.shop.items[1].itemCode, new Dictionary { { "RemoveAds", 200 }}); GetProductData(); GetPurchaseUpdates(); } private void DestroyLoader() { if (purchaseLoader != null) purchaseLoader.SetActive(false); } private IEnumerator DebugDelayedPurchase(string sku, float delay, bool simulateSuccessInEditor) { yield return new WaitForSeconds(0); SkuInput request = new SkuInput(); request.Sku = sku; RequestOutput response = _iapService.Purchase(request); string requestIdString = response.RequestId; Product productPurchased = new Product(sku, "fake editor receipt", _shopItems[sku]); if (simulateSuccessInEditor) InvokePurchaseComplete(productPurchased); else InvokePurchaseFailed(); DestroyLoader(); } }