// ============================================================ // IAPPayManager.cs // IAP 接入使用(MonoBehaviour) // // 将此脚本挂到场景中的任意 GameObject 即可运行示例。 // 请将商品 ID 替换为你在 App Store Connect / Google Play Console 中配置的真实 ID。 // ============================================================ using System; using System.Collections; using System.Collections.Generic; using RedHotRoast; using SDK_IAP; using UnityEngine; using UnityEngine.Purchasing; namespace RedHotRoast { public class IAPPayManager : MonoBehaviour { // ────────────────────────────────────────────────────────── // 单例实例 // ────────────────────────────────────────────────────────── private static IAPPayManager _instance; public static IAPPayManager Instance { get { if (_instance == null) { _instance = FindObjectOfType(); } return _instance; } } // ────────────────────────────────────────────────────────── // 商品 ID 常量(替换为真实 ID) // ────────────────────────────────────────────────────────── public static string PRODUCT_FIRST_GIFT = "comtronwingameredhotroastgift199"; //首充 消耗品 public static string PRODUCT_REMOVE_ADS = "comtronwingameredhotroastremove299"; //移除广告 消耗品 public static string PRODUCT_PASS_BONUS = "comtronwingameredhotroastpass999"; //通行证礼包 消耗品 public static string PRODUCT_SPACE_BONUS = "comtronwingameredhotroastspace24999"; //加一格礼包 非消耗品 public static string PRODUCT_SHOP_1 = "comtronwingameredhotroastshop199"; //商店档位1 消耗品 public static string PRODUCT_SHOP_2 = "comtronwingameredhotroastshop399"; //商店档位2 消耗品 public static string PRODUCT_SHOP_3= "comtronwingameredhotroastshop999"; //商店档位3 消耗品 public static string PRODUCT_SHOP_4 = "comtronwingameredhotroastshop1999"; //商店档位4 消耗品 public static string PRODUCT_SHOP_5 = "comtronwingameredhotroastshop3999"; //商店档位5 消耗品 // public static string PRODUCT_THREE_DAY = "comwackyllamagamethreeday399"; //三天礼包 消耗品 public static string PRODUCT_VIP_WEEK = "comtronwingameredhotroastsub999"; // 周订阅 Subscription public static string PRODUCT_VIP_MONTH = "comtronwingameredhotroastsub2999"; // 月订阅 Subscription public static string PRODUCT_VIP_YEAR = "comtronwingameredhotroastsub9999"; // 年订阅 Subscription // ────────────────────────────────────────────────────────── // 生命周期 // ────────────────────────────────────────────────────────── private void Awake() { // 确保单例唯一性 if (_instance != null && _instance != this) { Debug.LogWarning("[IAP IOS] 检测到多个 IAPGoogleManager 实例,销毁重复对象"); Destroy(gameObject); return; } _instance = this; // 订阅发货事件(持续监听,全局处理发货逻辑) IAPManager.OnDeliver += HandleDeliver; IAPManager.OnInitialized += HandleInitialized; } private void Start() { InitIAP(); //StartCoroutine(Test()); } //private IEnumerator Test() //{ // int index = 0; // while (true) // { // yield return new WaitForSeconds(1f); // Analytics.OnTrackEvent?.Invoke($"test{index}", new Dictionary()); // index++; // } //} private void OnDestroy() { IAPManager.OnDeliver -= HandleDeliver; IAPManager.OnInitialized -= HandleInitialized; IAPManager.Dispose(); } // ────────────────────────────────────────────────────────── // 初始化 // ────────────────────────────────────────────────────────── private void InitIAP() { var products = new List { // 消耗品 ProductDefine.Simple(PRODUCT_FIRST_GIFT, ProductType.Consumable), ProductDefine.Simple(PRODUCT_REMOVE_ADS, ProductType.Consumable), ProductDefine.Simple(PRODUCT_PASS_BONUS, ProductType.Consumable), ProductDefine.Simple(PRODUCT_SHOP_1, ProductType.Consumable), ProductDefine.Simple(PRODUCT_SHOP_2, ProductType.Consumable), ProductDefine.Simple(PRODUCT_SHOP_3, ProductType.Consumable), ProductDefine.Simple(PRODUCT_SHOP_4, ProductType.Consumable), ProductDefine.Simple(PRODUCT_SHOP_5, ProductType.Consumable), // ProductDefine.Simple(PRODUCT_THREE_DAY, ProductType.Consumable), // 非消耗品 ProductDefine.Simple(PRODUCT_SPACE_BONUS, ProductType.NonConsumable), // 订阅 ProductDefine.Simple(PRODUCT_VIP_WEEK, ProductType.Subscription), ProductDefine.Simple(PRODUCT_VIP_MONTH, ProductType.Subscription), ProductDefine.Simple(PRODUCT_VIP_YEAR, ProductType.Subscription), }; Debug.Log("[IAP IOS] 开始初始化..."); IAPManager.Init(products, OnInitCallback); } // Init 完成回调(与 OnInitialized 事件等价,二选一即可) private void OnInitCallback(bool success) { if (success) { Debug.Log("[IAP IOS] 初始化成功!"); ShowProductPrices(); } else { Debug.LogError("[IAP IOS] 初始化失败,请检查网络或 App Store / Google Play 配置。"); } } // 也可以通过静态事件监听(适合多个 Manager 分散处理) private void HandleInitialized(bool success) { // 此处可做更新 UI 等操作 } // ────────────────────────────────────────────────────────── // 显示商品价格(初始化成功后调用) // ────────────────────────────────────────────────────────── public void ShowProductPrices() { var products = IAPManager.GetProducts(); foreach (var p in products) { Debug.Log($"[IAP IOS] 商品: {p.definition.id} | 价格: {p.metadata.localizedPriceString}"); } } // ────────────────────────────────────────────────────────── // 购买 // ────────────────────────────────────────────────────────── /// 通用购买方法 - 适用于消耗品和非消耗品 /// 商品ID /// 商品名称(用于日志) /// 购买成功后的回调 public void BuyProduct(string productId, string productName, System.Action onSuccess = null) { IAPManager.Buy(productId, result => { if (result.success) { Debug.Log($"[IAP IOS] 购买成功: {productName} ({result.productId}) | tid={result.transactionId}"); onSuccess?.Invoke(); } else { Debug.LogWarning($"[IAP IOS] 购买失败: {productName} ({result.productId}) - {result.error}"); PurchasingManager.SendEventClickByName(productId, "open"); } }); } /// 通用订阅方法 - 适用于订阅类型商品 /// 商品ID /// 订阅名称(用于日志) /// 订阅成功后的回调 public void SubscribeProduct(string productId, string subscriptionName, System.Action onSuccess = null) { IAPManager.Buy(productId, result => { if (result.success) { Debug.Log($"[IAP IOS] 订阅成功: {subscriptionName} ({result.productId}) | tid={result.transactionId}"); // ShowSubscriptionInfo(); onSuccess?.Invoke(); } else { Debug.LogWarning($"[IAP IOS] 订阅失败: {subscriptionName} ({result.productId}) - {result.error}"); PurchasingManager.SendEventClickByName(productId, "open"); } }); } // ────────────────────────────────────────────────────────── // 恢复购买(iOS 界面上必须提供此按钮) // ────────────────────────────────────────────────────────── public void OnRestoreButtonClicked() { // UI 控制:仅在需要时显示此按钮 // if (!IAPManager.NeedShowRestoreButton()) return; IAPManager.Restore(result => { if (result.success) { Debug.Log("[IAP IOS] 恢复购买流程完成(实际恢复内容通过 OnDeliver 发放)"); Debug.Log("[IAP IOS] restore success: " + result.success); GameHelper.ShowTips("Restore_pur", true); // SaveData.GetSaveObject().have_slot = success; // DataMgr.VipLevel.Value = 3; // DataMgr.VipExpirationTime.Value = ServerClock.GetCurrentServerTime() + 7 * 24 * 60 * 60; } else { Debug.LogWarning($"[IAP IOS] 恢复购买失败: {result.error}"); GameHelper.ShowTips("recoverable_tips", true); } }); } // ────────────────────────────────────────────────────────── // 权益检查 // ────────────────────────────────────────────────────────── /// 通用权益检查方法 - 适用于非消耗品和订阅 /// 商品ID /// 商品名称(用于日志) /// 检查结果回调 public void CheckEntitlement(string productId, string productName, System.Action onResult = null) { IAPManager.CheckEntitlement(productId, status => { Debug.Log($"[IAP IOS] {productName} 权益状态: {status}"); onResult?.Invoke(status); }); } /// 检查加一格礼包权益 public void CheckSpaceBonusEntitlement() { CheckEntitlement(PRODUCT_SPACE_BONUS, "加一格礼包", state => { if (state == EntitlementStatus.FullyEntitled) { // 激活加一格权限 } }); } /// 检查VIP订阅权益 public void CheckVipEntitlement() { CheckEntitlement(PRODUCT_VIP_MONTH, "VIP月卡", status => { if (status == EntitlementStatus.FullyEntitled) { // 激活VIP权限 } }); } // ────────────────────────────────────────────────────────── // 订阅信息查询 // ────────────────────────────────────────────────────────── /// 显示指定商品的订阅信息 /// 商品ID public void ShowSubscriptionInfo(string productId) { var info = IAPManager.GetSubscriptionInfo(productId); Debug.Log($"[IAP IOS] VIP 订阅状态 ({productId}):"); Debug.Log($" isSubscribed = {info.isSubscribed}"); Debug.Log($" isExpired = {info.isExpired}"); Debug.Log($" expireDate = {info.expireDate}"); Debug.Log($" isAutoRenewing= {info.isAutoRenewing}"); if (info.isSubscribed && !info.isExpired) { int vipLevel = GetVipLevelByProductId(productId); if (vipLevel > 0) { DataMgr.VipLevel.Value = vipLevel; Debug.Log($"[IAP IOS] 设置 VIP 等级: {vipLevel}"); } if (info.expireDate.Year > 1970 && info.expireDate.Year < 10000) { var expireTimestamp = ((DateTimeOffset)info.expireDate).ToUnixTimeSeconds(); Debug.Log($"Expire timestamp: {expireTimestamp}"); DataMgr.VipExpirationTime.Value = Math.Max(DataMgr.VipExpirationTime.Value, expireTimestamp); } else { Debug.LogWarning($"[IAP IOS] 无效的到期时间: {info.expireDate}"); } } else { Debug.Log($"[IAP IOS] 用户未订阅或订阅已过期"); } } /// 根据商品ID获取VIP等级 /// 商品ID /// VIP等级:周订阅=1,月订阅=2,年订阅=3,其他=0 private int GetVipLevelByProductId(string productId) { if (productId == PRODUCT_VIP_WEEK) return 1; else if (productId == PRODUCT_VIP_MONTH) return 2; else if (productId == PRODUCT_VIP_YEAR) return 3; return 0; } // ────────────────────────────────────────────────────────── // 全局发货处理(OnDeliver 事件接收) // ────────────────────────────────────────────────────────── /// /// 统一发货处理入口。 /// 无论是新购买、补单、还是恢复购买,都会触发此方法。 /// ⚠️ 幂等保护已由 DeliverGuardLite 处理,此处无需再判重。 /// private void HandleDeliver(string productId) { Debug.Log($"[IAP IOS] 收到统一发货通知: {productId}"); if (!IsValidProduct(productId)) { Debug.LogWarning($"[IAP IOS] 非法商品ID,拒绝发货: {productId}"); return; } if (productId == PRODUCT_VIP_WEEK || productId == PRODUCT_VIP_MONTH || productId == PRODUCT_VIP_YEAR) { // 订阅商品:需要先获取有效的订阅信息,成功后才会分发支付成功消息 StartCoroutine(DelayedGetSubscriptionInfo(productId)); } else { UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.PayloadingUI_Close); // 非订阅商品(消耗品):直接发货 Debug.Log($"[IAP IOS] 发货通知: {productId}"); GameDispatcher.Instance.Dispatch(GameMsg.IAP_PAY_SUCCESS, productId); PurchasingManager.SendEventClickByName(productId, "open"); PurchasingManager.SendEventClickByName(productId, "success"); } } private IEnumerator DelayedGetSubscriptionInfo(string productId) { Debug.Log($"[IAP IOS] 开始获取订阅信息: {productId}"); // 先尝试立即获取一次 var immediateInfo = IAPManager.GetSubscriptionInfo(productId); // 检查是否是无效数据 bool isInvalid = immediateInfo.expireDate == default(DateTime) || (immediateInfo.expireDate.Year == 1 && !immediateInfo.isSubscribed); if (!isInvalid && immediateInfo.isSubscribed) { // 立即获取到了有效数据,直接处理 bool success = ProcessSubscriptionInfo(immediateInfo, productId); if (success) { DispatchPaySuccess(productId); } yield break; } // 无效数据,开始重试 Debug.Log($"[IAP IOS] 订阅信息未就绪,开始重试..."); int maxRetries = 5; float waitTime = 1.5f; for (int i = 0; i < maxRetries; i++) { yield return new WaitForSeconds(waitTime); // 每次等待0.5秒 var info = IAPManager.GetSubscriptionInfo(productId); if (info.isSubscribed && !info.isExpired && info.expireDate.Year > 1970) { Debug.Log($"[IAP IOS] 订阅信息获取成功 (重试 {i + 1} 次)"); bool success = ProcessSubscriptionInfo(info, productId); if (success) { DispatchPaySuccess(productId); } yield break; } Debug.Log($"[IAP IOS] 第 {i + 1} 次重试: isSubscribed={info.isSubscribed}, isExpired={info.isExpired}, expireDate={info.expireDate}"); } // 所有重试都失败了,使用降级方案 Debug.LogError($"[IAP IOS] 无法获取订阅信息,使用降级方案"); bool fallbackSuccess = ProcessFallbackSubscription(productId); if (fallbackSuccess) { DispatchPaySuccess(productId); } else { // 降级也失败了,上报错误,不分发支付成功 Debug.LogError($"[IAP IOS] 降级方案也失败,支付成功消息将不分发,请检查配置"); PurchasingManager.SendEventClickByName(productId, "open"); // 可选:向用户显示错误提示 } } /// /// 处理订阅信息,设置VIP等级和过期时间 /// /// 是否设置成功 private bool ProcessSubscriptionInfo(SubscriptionInfoLite info, string productId) { try { Debug.Log($"[IAP IOS] VIP 订阅状态 ({productId}):"); Debug.Log($" isSubscribed = {info.isSubscribed}"); Debug.Log($" isExpired = {info.isExpired}"); Debug.Log($" expireDate = {info.expireDate}"); Debug.Log($" isAutoRenewing= {info.isAutoRenewing}"); // 获取VIP等级 int vipLevel = GetVipLevelByProductId(productId); if (vipLevel <= 0) { Debug.LogError($"[IAP IOS] 无法获取VIP等级,商品ID: {productId}"); return false; } // 获取过期时间戳 long expireTimestamp = 0; if (info.expireDate.Year > 1970 && info.expireDate.Year < 10000) { expireTimestamp = ((DateTimeOffset)info.expireDate).ToUnixTimeSeconds(); } else { Debug.LogWarning($"[IAP IOS] 无效的到期时间: {info.expireDate},使用预估时间"); expireTimestamp = GetEstimatedExpireTimestamp(productId); } if (expireTimestamp <= 0) { Debug.LogError($"[IAP IOS] 无法获取有效的过期时间戳"); return false; } // 保存数据(使用前后对比,确保设置成功) int oldVipLevel = DataMgr.VipLevel.Value; long oldExpireTime = DataMgr.VipExpirationTime.Value; DataMgr.VipLevel.Value = Math.Max(DataMgr.VipLevel.Value, vipLevel); DataMgr.VipExpirationTime.Value = Math.Max(DataMgr.VipExpirationTime.Value, expireTimestamp); // 验证设置是否成功 bool vipLevelSuccess = DataMgr.VipLevel.Value >= vipLevel; bool expireTimeSuccess = DataMgr.VipExpirationTime.Value >= expireTimestamp; if (vipLevelSuccess && expireTimeSuccess) { Debug.Log($"[IAP IOS] VIP设置成功 - 等级: {vipLevel} (原:{oldVipLevel}), 过期时间: {expireTimestamp} (原:{oldExpireTime})"); // 可选:触发VIP状态更新事件 return true; } else { Debug.LogError($"[IAP IOS] VIP设置失败 - 等级设置: {vipLevelSuccess}, 过期时间设置: {expireTimeSuccess}"); return false; } } catch (Exception e) { Debug.LogError($"[IAP IOS] 处理订阅信息时发生异常: {e.Message}\n{e.StackTrace}"); return false; } } /// /// 降级方案:当无法从商店获取订阅信息时,根据商品类型估算VIP信息 /// private bool ProcessFallbackSubscription(string productId) { try { Debug.LogWarning($"[IAP IOS] 执行降级方案: {productId}"); int vipLevel = GetVipLevelByProductId(productId); if (vipLevel <= 0) { Debug.LogError($"[IAP IOS] 降级方案失败 - 无法获取VIP等级"); return false; } long expireTimestamp = GetEstimatedExpireTimestamp(productId); if (expireTimestamp <= 0) { Debug.LogError($"[IAP IOS] 降级方案失败 - 无法获取预估过期时间"); return false; } // 设置VIP信息 DataMgr.VipLevel.Value = Math.Max(DataMgr.VipLevel.Value, vipLevel); DataMgr.VipExpirationTime.Value = Math.Max(DataMgr.VipExpirationTime.Value, expireTimestamp); Debug.Log($"[IAP IOS] 降级方案成功 - 等级: {vipLevel}, 过期时间: {expireTimestamp}"); return true; } catch (Exception e) { Debug.LogError($"[IAP IOS] 降级方案异常: {e.Message}"); return false; } } /// /// 获取预估的过期时间戳 /// private long GetEstimatedExpireTimestamp(string productId) { TimeSpan duration; if (productId == PRODUCT_VIP_WEEK) { duration = TimeSpan.FromDays(7); } else if (productId == PRODUCT_VIP_MONTH) { duration = TimeSpan.FromDays(30); } else if (productId == PRODUCT_VIP_YEAR) { duration = TimeSpan.FromDays(365); } else { duration = TimeSpan.FromDays(30); } var expireDate = DateTime.UtcNow.Add(duration); return ((DateTimeOffset)expireDate).ToUnixTimeSeconds(); } /// /// 分发支付成功消息(统一出口) /// private void DispatchPaySuccess(string productId) { UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.PayloadingUI_Close); Debug.Log($"[IAP IOS] 支付成功并完成发货: {productId}"); GameDispatcher.Instance.Dispatch(GameMsg.IAP_PAY_SUCCESS, productId); PurchasingManager.SendEventClickByName(productId, "open"); PurchasingManager.SendEventClickByName(productId, "success"); } /// 验证商品ID是否为已定义的有效商品 /// 商品ID /// 是否为有效商品 private bool IsValidProduct(string productId) { return productId == PRODUCT_FIRST_GIFT || productId == PRODUCT_REMOVE_ADS || productId == PRODUCT_PASS_BONUS || productId == PRODUCT_SPACE_BONUS || productId == PRODUCT_SHOP_1 || productId == PRODUCT_SHOP_2 || productId == PRODUCT_SHOP_3 || productId == PRODUCT_SHOP_4 || productId == PRODUCT_SHOP_5 || // productId == PRODUCT_THREE_DAY || productId == PRODUCT_VIP_WEEK || productId == PRODUCT_VIP_MONTH || productId == PRODUCT_VIP_YEAR; } } }