import 'dart:async'; import 'dart:io'; import 'package:get/get.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; import 'package:kaer_with_panels/app/services/iap/iap_pending_order_service.dart'; import 'package:kaer_with_panels/app/utils/kr_common_util.dart'; import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart'; import 'package:kaer_with_panels/app/services/api_service/kr_subscribe_api.dart'; import 'package:kaer_with_panels/app/routes/app_pages.dart'; class KRIAPService extends GetxService { static KRIAPService get instance => Get.find(); final KRSubscribeApi _kr_subscribeApi = KRSubscribeApi(); final InAppPurchase _iap = InAppPurchase.instance; StreamSubscription>? _subscription; // 增加标志位,防止多次初始化 bool _hasStarted = false; @override void onInit() { super.onInit(); // 不再这里自动启动,等待 Home 页面手动开启 } /// 【外部调用】在 Home 页面加载完成后调用此方法 void setup() { if (!Platform.isIOS) return; if (_hasStarted) return; _hasStarted = true; _startGlobalListening(); } void _startGlobalListening() { print('💳 [IAPService] ========== 🚀 全局监听流已就绪 =========='); // 确保不会重复监听 _subscription?.cancel(); _subscription = _iap.purchaseStream.listen( _handleIapUpdates, onDone: () => _subscription?.cancel(), onError: (error) { print('💳 [IAPService] ❌ Stream Error: $error'); // 如果发生错误,可以尝试重启流 }, ); } // 1. 监听回调:只负责状态分流 void _handleIapUpdates(List purchases) async { final pendingOrderNo = await IAPPendingOrderService.getPendingOrderNo(); print('🔄 [IAPService] 处理状态222: ${pendingOrderNo}'); // 如果没有本地挂起的订单号,说明不是通过 App 正常发起的支付(或是已处理完的补单) if (pendingOrderNo == null || pendingOrderNo.isEmpty) return; for (final p in purchases) { print('🔄 [IAPService] 处理状态: ${p.status} | ID: ${p.purchaseID}'); if (p.status == PurchaseStatus.pending) continue; if (p.status == PurchaseStatus.error) { if (p.pendingCompletePurchase) { await _iap.completePurchase(p); // ✅ 必须告诉苹果,这个错误我处理了 } // 如果是用户主动取消,在这里清理 orderNo 是安全的 if (p.error?.message.contains('canceled') ?? false) { await IAPPendingOrderService.clearPendingOrderNo(); } KRCommonUtil.kr_hideLoading(); continue; } if (p.status == PurchaseStatus.purchased || p.status == PurchaseStatus.restored) { // 核心:跳转到统一验证逻辑 _verifyWithServer(p, pendingOrderNo); } } } // 2. 统一验证逻辑:负责与后端交互、补单、UI 引导 Future _verifyWithServer(PurchaseDetails p, String orderNo) async { final String jwsData = p.verificationData.serverVerificationData; if (jwsData.isEmpty) { print('❌ [IAPService] JWS 数据为空,跳过验证'); if (p.pendingCompletePurchase) { await InAppPurchase.instance.completePurchase(p); } KRCommonUtil.kr_hideLoading(); return; } KRCommonUtil.kr_showLoading(message: '正在激活会员权益...'); bool ok = false; try { // 根据状态选择对应的后端接口 if (p.status == PurchaseStatus.purchased || p.status == PurchaseStatus.restored) { await IAPPendingOrderService.setPendingJwsData(jwsData); final either = await _kr_subscribeApi.kr_attachAppleIapTransaction(jwsData, orderNo); either.fold((e) => print('❌ [IAPService] 支付激活失败: ${e.msg}'), (v) => ok = true); } // else { // // 注意:restore 接口如果后端要求传 JWS,这里保持一致 // // final either = await _kr_subscribeApi.kr_restoreAppleIapTransaction(jwsData, orderNo); // // either.fold((e) => print('❌ [IAPService] 恢复激活失败: ${e.msg}'), (v) => ok = true); // } } catch (e) { print('🌐 [IAPService] 网络请求异常'); } if (ok) { await _finalizeTransaction(p, orderNo); } else { _handleVerificationFailure(p, orderNo); } } String? findCurrentRoute() { String? routeName; try { // 强制通过 Navigator 的 context 获取栈信息 Get.key.currentState?.popUntil((route) { routeName = route.settings.name; // 打印每一层,看看栈里到底有什么 print('层级检查: $routeName'); // 如果拿到的是有效名且不是 null,就锁定它 if (routeName != null && routeName != '/') return true; return false; }); } catch (e) { print('路由查找异常: $e'); } return routeName; } // 3. 验证成功后的收尾工作 Future _finalizeTransaction(PurchaseDetails p, String orderNo) async { print('✅ [IAPService] 验证成功,清理流程${p.pendingCompletePurchase}'); // I. 结束苹果事务(关键:防止重复推送) if (p.pendingCompletePurchase) { await _iap.completePurchase(p); } await _iap.completePurchase(p); // II. 清理本地拦截订单号 await IAPPendingOrderService.clearPendingOrderNo(); KRCommonUtil.kr_hideLoading(); String? realRoute = findCurrentRoute(); print('🔍 [IAP Fix] Final Found Route: "$realRoute"'); // if (Get.currentRoute.contains(Routes.KR_ORDER_STATUS)) return; // // // 2. 判断是否需要自动跳转 // // 如果当前在“会员购买页”,说明是用户刚刚点的,直接跳 // if (Get.currentRoute.contains(Routes.KR_PURCHASE_MEMBERSHIP)) { // _navigateToStatusPage(orderNo); // } else { // // 3. ⚠️ 如果在 Home 或其他地方(说明是后台自动补单成功) // // 弹窗提示,由用户决定是否查看详情 // HIDialog.show( // title: '订单处理成功', // message: '检测到您有一笔未完成的订单已激活成功,会员权益已到账。', // confirmText: '查看订单', // cancelText: '我知道了', // onConfirm: () => _navigateToStatusPage(orderNo), // ); // } // 拿不到真实的值,不知道为什么路由栈找不到, _navigateToStatusPage(orderNo); } void _navigateToStatusPage(String orderNo) { Get.offNamed( Routes.KR_ORDER_STATUS, arguments: { 'order': orderNo, 'payment_type': 'apple_iap', 'checkout_type': 'ipa', }, ); } // 4. 验证失败后的重试引导 void _handleVerificationFailure(PurchaseDetails p, String orderNo) { KRCommonUtil.kr_hideLoading(); HIDialog.show( title: '激活失败', message: '您的支付已成功,但由于网络原因未能开启权益。', confirmText: '重试激活', cancelText: '关闭', barrierDismissible: false, preventBackDismiss: true, onConfirm: () { // 💡 重点:直接重新调用验证逻辑,不重复触发监听流 _verifyWithServer(p, orderNo); }, ); } // 5. 手动触发(用于 UI 上的恢复购买按钮) Future retryUnfinishedTransactions() async { try { await _iap.restorePurchases(); } catch (e) { print('e'); } } @override void onClose() { _subscription?.cancel(); super.onClose(); } }