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(); // 如果没有本地挂起的订单号,说明不是通过 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); KRCommonUtil.kr_showToast('购买失败'); 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) { 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); } } // 3. 验证成功后的收尾工作 Future _finalizeTransaction(PurchaseDetails p, String orderNo) async { print('✅ [IAPService] 验证成功,清理流程'); // I. 结束苹果事务(关键:防止重复推送) if (p.pendingCompletePurchase) { await _iap.completePurchase(p); } // II. 清理本地拦截订单号 await IAPPendingOrderService.clearPendingOrderNo(); KRCommonUtil.kr_hideLoading(); // III. 跳转结果页 // 如果已经在结果页了就不跳,或者根据路由判断 if (!Get.currentRoute.contains(Routes.KR_ORDER_STATUS)) { 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 { KRCommonUtil.kr_showLoading(message: '正在查找未完成订单...'); await _iap.restorePurchases(); // 如果 5 秒后没触发流,隐藏 loading Future.delayed(const Duration(seconds: 5), () => KRCommonUtil.kr_hideLoading()); } catch (e) { KRCommonUtil.kr_hideLoading(); KRCommonUtil.kr_showToast("请求失败,请稍后重试"); } } @override void onClose() { _subscription?.cancel(); super.onClose(); } }