fix: 增加iap的跳转

This commit is contained in:
2026-01-23 07:06:36 -08:00
parent b96008d6dc
commit 57a47874be
4 changed files with 153 additions and 12 deletions
+30 -12
View File
@@ -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;
@@ -176,6 +176,7 @@ class KRInviteView extends GetView<KRInviteController> {
maxLength: 10,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
decoration: InputDecoration(
counterText: '',
hintText: '填码领免费时长...',
hintStyle: const TextStyle(color: Color(0xFFA6A6A6)),
filled: true,
@@ -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<String> list1 = v1.split('.');
List<String> 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;
}
}
@@ -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<String, dynamic> jsonMap =
jsonDecode(customData) as Map<String, dynamic>;
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<String, dynamic> jsonMap =
jsonDecode(customData) as Map<String, dynamic>;
final value = jsonMap['shareUrl'];
return value == null ? '' : value.toString();
} catch (_) {
return '';
}
}
/// 重置配置
void reset() {
_siteConfig = null;