From 57a47874be91bf5442c0cf750f7916a3c1671ad3 Mon Sep 17 00:00:00 2001 From: speakeloudest Date: Fri, 23 Jan 2026 07:06:36 -0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=A2=9E=E5=8A=A0iap=E7=9A=84=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner.xcodeproj/project.pbxproj | 42 ++++++--- .../kr_invite/views/kr_invite_view.dart | 1 + .../kr_purchase_membership_controller.dart | 86 +++++++++++++++++++ lib/app/services/kr_site_config_service.dart | 36 ++++++++ 4 files changed, 153 insertions(+), 12 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 1198af1..ea138b8 100755 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -762,9 +762,11 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = PacketTunnel/HiddifyPacketTunnel.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = NJRRF427XB; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NJRRF427XB; ENABLE_USER_SCRIPT_SANDBOXING = YES; EXCLUDED_ARCHS = armv7; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -790,6 +792,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord-PacketTunnel"; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -815,9 +818,11 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnelRelease.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = NJRRF427XB; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NJRRF427XB; ENABLE_USER_SCRIPT_SANDBOXING = YES; EXCLUDED_ARCHS = armv7; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -843,6 +848,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord-PacketTunnel"; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -866,9 +872,11 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = PacketTunnel/HiddifyPacketTunnel.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = NJRRF427XB; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NJRRF427XB; ENABLE_USER_SCRIPT_SANDBOXING = YES; EXCLUDED_ARCHS = armv7; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -894,6 +902,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).PacketTunnel"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord-PacketTunnel"; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -971,9 +980,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = NJRRF427XB; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NJRRF427XB; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphoneos*]" = armv7; INFOPLIST_FILE = Runner/Info.plist; @@ -1005,6 +1016,7 @@ "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.taw.hifastvpn; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; @@ -1200,9 +1212,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = NJRRF427XB; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NJRRF427XB; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphoneos*]" = armv7; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; @@ -1234,6 +1248,7 @@ "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.taw.hifastvpn; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; @@ -1255,9 +1270,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = NJRRF427XB; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NJRRF427XB; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphoneos*]" = armv7; INFOPLIST_FILE = Runner/Info.plist; @@ -1289,6 +1306,7 @@ "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.taw.hifastvpn; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "HiFastVPN-iOS-Pord"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; diff --git a/lib/app/modules/kr_invite/views/kr_invite_view.dart b/lib/app/modules/kr_invite/views/kr_invite_view.dart index 24ec2f4..94ce80e 100755 --- a/lib/app/modules/kr_invite/views/kr_invite_view.dart +++ b/lib/app/modules/kr_invite/views/kr_invite_view.dart @@ -176,6 +176,7 @@ class KRInviteView extends GetView { maxLength: 10, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold), decoration: InputDecoration( + counterText: '', hintText: '填码领免费时长...', hintStyle: const TextStyle(color: Color(0xFFA6A6A6)), filled: true, 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 b2a8aad..6f643aa 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 @@ -7,6 +7,7 @@ import 'package:kaer_with_panels/app/utils/kr_log_util.dart'; import 'package:url_launcher/url_launcher.dart'; import 'dart:io'; import 'package:path_provider/path_provider.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import '../../../common/app_run_data.dart'; import '../../../common/app_config.dart'; @@ -19,12 +20,14 @@ import '../../../network/http_util.dart'; import 'package:flutter/foundation.dart'; // import 'package:flutter_stripe/flutter_stripe.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; +import 'package:in_app_purchase_storekit/store_kit_2_wrappers.dart'; import 'dart:async'; import 'package:flutter/services.dart'; import 'package:kaer_with_panels/app/model/response/kr_purchase_order_no.dart'; import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart'; import 'package:kaer_with_panels/app/services/iap/iap_pending_order_service.dart'; import 'package:kaer_with_panels/app/services/iap/iap_service.dart'; +import '../../../services/kr_site_config_service.dart'; /// 会员购买控制器 /// 负责处理会员套餐选择、支付方式选择和订阅流程 @@ -86,6 +89,12 @@ class KRPurchaseMembershipController extends GetxController { print('💳 [PurchaseMembership] ❌ 写入调试日志失败: $e'); } + // 判断是否有 curversion,如果没有先请求下 site/config 接口让配置初始化下 + final siteConfigService = KRSiteConfigService(); + if (siteConfigService.getCurVersion().isEmpty) { + siteConfigService.fetchSiteConfig(); + } + kr_initializeData(); } @@ -1006,6 +1015,60 @@ class KRPurchaseMembershipController extends GetxController { return; } + // 🔧 版本和地区检查逻辑 + try { + final PackageInfo packageInfo = await PackageInfo.fromPlatform(); + final String currentVersion = packageInfo.version; + final String curVersion = KRSiteConfigService().getCurVersion(); + + print('💳 [IAP] 支付检查: App版本=$currentVersion, 服务端参考版本=$curVersion'); + + if (curVersion.isNotEmpty) { + // 如果代码中的version版本小于等于 curversion 时 + if (_kr_compareVersion(currentVersion, curVersion) <= 0) { + // 判断 apple id 账号地区是否在香港(HK)、台湾(TW) + // 使用 StoreKit 的地区代码(SKStorefront),它是根据当前登录的 App Store 账户决定的 + // 使用 StoreKit 2 的地区代码,它是根据当前登录的 App Store 账户决定的 + String storefrontCountryCode = ''; + try { + // StoreKit 2 API + final Storefront storefront = Storefront(); + final String storefrontCountryCode = await storefront.countryCode(); + print('💳 [IAP] Storefront 2 地区检查: countryCode=$storefrontCountryCode'); + } catch (e) { + print('💳 [IAP] 获取 Storefront 2 信息失败, 尝试使用系统地区作为后备: $e'); + storefrontCountryCode = Get.deviceLocale?.countryCode ?? ''; + } + + // 定义 H5 支付白名单 (包含 2 位和 3 位国家代码) + const h5WhiteList = [ + 'HKG', // 香港 + 'TWN', // 台湾 + 'MAC', // 澳门 + 'SGP', // 新加坡 + 'MYS', // 马来西亚 + 'THA', // 泰国 + 'VNM', // 越南 + 'PHL', // 菲律宾 + ]; + + if (h5WhiteList.contains(storefrontCountryCode.toUpperCase())) { + final String token = KRAppRunData.getInstance().kr_token ?? ''; + final String url = 'https://hifastvpn.com/login/$token'; + print('💳 [IAP] 符合跳转条件 (Storefront: $storefrontCountryCode),跳转至网页支付: $url'); + + final Uri uri = Uri.parse(url); + if (await canLaunchUrl(uri)) { + await launchUrl(uri, mode: LaunchMode.externalApplication); + return; // 终止后续苹果内购流程 + } + } + } + } + } catch (e) { + print('💳 [IAP] 版本或地区检查时发生异常: $e'); + } + final KRIpaPayment iapParams = (checkoutResponse.ipa == null || (checkoutResponse.ipa?.productId.isEmpty ?? true)) ? KRIpaPayment( @@ -1115,4 +1178,27 @@ class KRPurchaseMembershipController extends GetxController { return; // 终止后续操作 } } + + /// 比较版本号 + /// 返回值: + /// -1: v1 < v2 + /// 0: v1 == v2 + /// 1: v1 > v2 + int _kr_compareVersion(String v1, String v2) { + if (v1.isEmpty || v2.isEmpty) return 0; + try { + List list1 = v1.split('.'); + List list2 = v2.split('.'); + int len = list1.length > list2.length ? list1.length : list2.length; + for (int i = 0; i < len; i++) { + int n1 = i < list1.length ? int.tryParse(list1[i]) ?? 0 : 0; + int n2 = i < list2.length ? int.tryParse(list2[i]) ?? 0 : 0; + if (n1 > n2) return 1; + if (n1 < n2) return -1; + } + } catch (e) { + print('版本比较失败: $e'); + } + return 0; + } } diff --git a/lib/app/services/kr_site_config_service.dart b/lib/app/services/kr_site_config_service.dart index b852789..88f4802 100644 --- a/lib/app/services/kr_site_config_service.dart +++ b/lib/app/services/kr_site_config_service.dart @@ -299,6 +299,42 @@ class KRSiteConfigService extends ChangeNotifier { return _siteConfig?.site.deviceLimit ?? '0'; } + /// 获取当前版本号 + /// + /// 功能:从站点配置的 customData 中解析并返回 curVersion + /// 参数:无 + /// 返回:String 当前版本号,如未配置返回空字符串 + String getCurVersion() { + try { + final customData = _siteConfig?.site.customData ?? ''; + if (customData.isEmpty) return ''; + final Map jsonMap = + jsonDecode(customData) as Map; + final value = jsonMap['curVersion']; + return value == null ? '' : value.toString(); + } catch (_) { + return ''; + } + } + + /// 获取分享地址 + /// + /// 功能:从站点配置的 customData 中解析并返回 shareUrl + /// 参数:无 + /// 返回:String 分享地址,如未配置返回空字符串 + String getShareUrl() { + try { + final customData = _siteConfig?.site.customData ?? ''; + if (customData.isEmpty) return ''; + final Map jsonMap = + jsonDecode(customData) as Map; + final value = jsonMap['shareUrl']; + return value == null ? '' : value.toString(); + } catch (_) { + return ''; + } + } + /// 重置配置 void reset() { _siteConfig = null;