diff --git a/lib/app/modules/kr_home/bindings/kr_home_binding.dart b/lib/app/modules/kr_home/bindings/kr_home_binding.dart index b210f21..dd93d15 100755 --- a/lib/app/modules/kr_home/bindings/kr_home_binding.dart +++ b/lib/app/modules/kr_home/bindings/kr_home_binding.dart @@ -1,15 +1,21 @@ import 'package:get/get.dart'; - import '../controllers/kr_home_controller.dart'; +// 导入你的 IAP Service 路径 +import 'package:kaer_with_panels/app/services/iap/iap_service.dart'; class KRHomeBinding extends Bindings { @override void dependencies() { + // 1. 注入 Home 控制器 (只保留一个) Get.lazyPut( - () => KRHomeController(), + () => KRHomeController(), ); - Get.lazyPut( - () => KRHomeController(), + + // 2. ✅ 在 Home 绑定中注入 IAP Service + // 使用 fenix: true 是关键,它允许 Service 在页面销毁重建时自动恢复 + Get.lazyPut( + () => KRIAPService(), + fenix: true, ); } -} +} \ No newline at end of file diff --git a/lib/app/modules/kr_home/controllers/kr_home_controller.dart b/lib/app/modules/kr_home/controllers/kr_home_controller.dart index e34152a..03ae614 100755 --- a/lib/app/modules/kr_home/controllers/kr_home_controller.dart +++ b/lib/app/modules/kr_home/controllers/kr_home_controller.dart @@ -1879,6 +1879,12 @@ class KRHomeController extends GetxController with WidgetsBindingObserver { print('ok'); }); }); + + Future.delayed(const Duration(milliseconds: 500), () async { + if (Get.isRegistered()) { + KRIAPService.instance.setup(); + } + }); } @override diff --git a/lib/app/modules/kr_purchase_membership/controllers/kr_purchase_membership_controller.dart b/lib/app/modules/kr_purchase_membership/controllers/kr_purchase_membership_controller.dart index 38bd9ce..c5ecc37 100755 --- a/lib/app/modules/kr_purchase_membership/controllers/kr_purchase_membership_controller.dart +++ b/lib/app/modules/kr_purchase_membership/controllers/kr_purchase_membership_controller.dart @@ -450,10 +450,11 @@ class KRPurchaseMembershipController extends GetxController { // ✅ 1. iOS 核心拦截:检测本地是否有“挂起”的订单号 if (Platform.isIOS) { - final existingOrderNo = await IAPPendingOrderService.getPendingOrderNo(); + final getPendingJwsData = await IAPPendingOrderService.getPendingJwsData(); + final getPendingOrderNo = await IAPPendingOrderService.getPendingOrderNo(); - if (existingOrderNo != null && existingOrderNo.isNotEmpty) { - print('🛑 [IAP] 拦截成功:发现未结单 $existingOrderNo'); + if (getPendingJwsData != null && getPendingJwsData.isNotEmpty!) { + print('🛑 [IAP] 拦截成功:发现未结单 $getPendingJwsData'); await HIDialog.show( title: '检测到待激活订单', @@ -461,10 +462,22 @@ class KRPurchaseMembershipController extends GetxController { confirmText: '立即激活', cancelText: '取消', barrierDismissible: false, - onConfirm: () { + onConfirm: () async{ // 🚀 调用全局 Service 的重试逻辑 - // 这会触发苹果重新推送 PurchaseStream,进而走验证流程 - KRIAPService.instance.retryUnfinishedTransactions(); + try{ + await _kr_subscribeApi.kr_attachAppleIapTransaction(getPendingJwsData, getPendingOrderNo!); + Get.offNamed( + Routes.KR_ORDER_STATUS, + arguments: { + 'order': getPendingOrderNo, + 'payment_type': 'apple_iap', + 'checkout_type': 'ipa', + }, + ); + }catch (e){ + KRCommonUtil.kr_showToast('激活失败,请重试'); + } + }, ); return; // 🛑 关键:必须 return,阻止创建重复的新订单 @@ -962,7 +975,7 @@ class KRPurchaseMembershipController extends GetxController { final KRIpaPayment iapParams = (checkoutResponse.ipa == null || (checkoutResponse.ipa?.productId.isEmpty ?? true)) ? KRIpaPayment( - productId: 'com.hifastvpn.plan.day${kr_getSelectedQuantity()}') + productId: 'com.hifastvpn.vip.day${kr_getSelectedQuantity()}') : checkoutResponse.ipa!; final iap = InAppPurchase.instance; diff --git a/lib/app/services/iap/iap_pending_order_service.dart b/lib/app/services/iap/iap_pending_order_service.dart index be20964..5060cef 100644 --- a/lib/app/services/iap/iap_pending_order_service.dart +++ b/lib/app/services/iap/iap_pending_order_service.dart @@ -1,17 +1,29 @@ import 'package:flutter_keychain/flutter_keychain.dart'; class IAPPendingOrderService { - static const _key = 'hi_iap_pending_order_no'; + static const _keyOrderNo = 'hi_iap_pending_order_no'; + static const _keyJwsData = 'hi_iap_pending_jws_data'; // 新增 Key static Future setPendingOrderNo(String orderNo) async { - await FlutterKeychain.put(key: _key, value: orderNo); + await FlutterKeychain.put(key: _keyOrderNo, value: orderNo); } static Future getPendingOrderNo() async { - return await FlutterKeychain.get(key: _key); + return await FlutterKeychain.get(key: _keyOrderNo); + } + + /// 存储 JWS 凭证数据 + static Future setPendingJwsData(String jwsData) async { + await FlutterKeychain.put(key: _keyJwsData, value: jwsData); + } + + /// 获取存储的 JWS 凭证数据 + static Future getPendingJwsData() async { + return await FlutterKeychain.get(key: _keyJwsData); } static Future clearPendingOrderNo() async { - await FlutterKeychain.remove(key: _key); + await FlutterKeychain.remove(key: _keyOrderNo); + await FlutterKeychain.remove(key: _keyJwsData); } } diff --git a/lib/app/services/iap/iap_service.dart b/lib/app/services/iap/iap_service.dart index e13f44a..4125d61 100644 --- a/lib/app/services/iap/iap_service.dart +++ b/lib/app/services/iap/iap_service.dart @@ -50,7 +50,7 @@ class KRIAPService extends GetxService { // 1. 监听回调:只负责状态分流 void _handleIapUpdates(List purchases) async { final pendingOrderNo = await IAPPendingOrderService.getPendingOrderNo(); - + print('🔄 [IAPService] 处理状态222: ${pendingOrderNo}'); // 如果没有本地挂起的订单号,说明不是通过 App 正常发起的支付(或是已处理完的补单) if (pendingOrderNo == null || pendingOrderNo.isEmpty) return; @@ -92,13 +92,15 @@ class KRIAPService extends GetxService { 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); } + // 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] 网络请求异常'); } @@ -110,31 +112,70 @@ class KRIAPService extends GetxService { } } + 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] 验证成功,清理流程'); + print('✅ [IAPService] 验证成功,清理流程${p.pendingCompletePurchase}'); // I. 结束苹果事务(关键:防止重复推送) if (p.pendingCompletePurchase) { await _iap.completePurchase(p); } - + 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', - }, - ); - } + 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. 验证失败后的重试引导 diff --git a/lib/app/utils/account_guard.dart b/lib/app/utils/account_guard.dart index b2d350c..145ec85 100644 --- a/lib/app/utils/account_guard.dart +++ b/lib/app/utils/account_guard.dart @@ -5,7 +5,6 @@ import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart'; import 'package:kaer_with_panels/app/routes/app_pages.dart'; import 'package:kaer_with_panels/app/utils/kr_common_util.dart'; import 'package:kaer_with_panels/app/services/kr_site_config_service.dart'; -import 'package:kaer_with_panels/app/services/iap/iap_service.dart'; Future ensureAccountExists() async { final app = KRAppRunData.getInstance(); @@ -28,7 +27,6 @@ Future ensureAccountExists() async { return false; } - KRIAPService.instance.setup(); var crispId = config.kr_website_id; var deviceLimitText = config.device_limit; var deviceLimit = int.tryParse(deviceLimitText) ?? 0;