feat: iap支付方式

This commit is contained in:
speakeloudest 2025-12-16 01:37:59 -08:00
parent f888b772c2
commit 60de644637
11 changed files with 368 additions and 52 deletions

View File

@ -8,8 +8,6 @@ class KRPurchaseOrderNo {
} }
} }
class KRPurchaseOrderUrl { class KRPurchaseOrderUrl {
final String url; final String url;
@ -22,14 +20,16 @@ class KRPurchaseOrderUrl {
/// Checkout Tauri /// Checkout Tauri
class KRCheckoutResponse { class KRCheckoutResponse {
final String type; // "url" | "qr" | "stripe" final String type; // "url" | "qr" | "stripe" | "apple_iap"
final String? checkoutUrl; final String? checkoutUrl;
final KRStripePayment? stripe; final KRStripePayment? stripe;
final KRIpaPayment? ipa;
KRCheckoutResponse({ KRCheckoutResponse({
required this.type, required this.type,
this.checkoutUrl, this.checkoutUrl,
this.stripe, this.stripe,
this.ipa,
}); });
factory KRCheckoutResponse.fromJson(Map<String, dynamic> json) { factory KRCheckoutResponse.fromJson(Map<String, dynamic> json) {
@ -39,6 +39,7 @@ class KRCheckoutResponse {
stripe: json['stripe'] != null stripe: json['stripe'] != null
? KRStripePayment.fromJson(json['stripe']) ? KRStripePayment.fromJson(json['stripe'])
: null, : null,
ipa: json['ipa'] != null ? KRIpaPayment.fromJson(json['ipa']) : null,
); );
} }
} }
@ -63,3 +64,24 @@ class KRStripePayment {
); );
} }
} }
class KRIpaPayment {
final String productId;
final String? applicationUsername;
final String? orderNo;
KRIpaPayment({
required this.productId,
this.applicationUsername,
this.orderNo,
});
factory KRIpaPayment.fromJson(Map<String, dynamic> json) {
return KRIpaPayment(
productId: json['product_id'] ?? json['productId'] ?? '',
applicationUsername:
json['application_username'] ?? json['applicationUsername'],
orderNo: json['order_no'] ?? json['orderNo'],
);
}
}

View File

@ -78,7 +78,7 @@ class KRUserAvailableSubscribeList {
}); });
factory KRUserAvailableSubscribeList.fromJson(Map<String, dynamic> json) { factory KRUserAvailableSubscribeList.fromJson(Map<String, dynamic> json) {
KRLogUtil.kr_i('订阅json列表: ${json}', tag: 'KRUserAvailableSubscribeList'); // KRLogUtil.kr_i('订阅json列表: ${json}', tag: 'KRUserAvailableSubscribeList');
final List<dynamic> listData = (json['list'] as List<dynamic>?) ?? const []; final List<dynamic> listData = (json['list'] as List<dynamic>?) ?? const [];
return KRUserAvailableSubscribeList( return KRUserAvailableSubscribeList(
list: listData list: listData

View File

@ -86,7 +86,7 @@ class _KRHomeViewState extends State<KRHomeView> {
'Hi快VPN-网在我在,网快我快', 'Hi快VPN-网在我在,网快我快',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 14.sp, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
@ -102,7 +102,7 @@ class _KRHomeViewState extends State<KRHomeView> {
// --- --- // --- ---
final normalStyle = TextStyle( final normalStyle = TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 12.sp, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
height: 1.2, height: 1.2,
); );

View File

@ -18,6 +18,9 @@ import '../../../utils/kr_event_bus.dart';
import '../../../network/http_util.dart'; import '../../../network/http_util.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_stripe/flutter_stripe.dart'; import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'dart:async';
import 'package:kaer_with_panels/app/model/response/kr_purchase_order_no.dart';
/// ///
/// ///
@ -53,7 +56,8 @@ class KRPurchaseMembershipController extends GetxController {
@override @override
void onInit() async { void onInit() async {
super.onInit(); super.onInit();
print('💳 [PurchaseMembership] ========== Controller.onInit 被调用 =========='); print(
'💳 [PurchaseMembership] ========== Controller.onInit 被调用 ==========');
print('💳 [PurchaseMembership] 当前时间: ${DateTime.now()}'); print('💳 [PurchaseMembership] 当前时间: ${DateTime.now()}');
// 🔧 Controller被初始化 // 🔧 Controller被初始化
@ -61,11 +65,14 @@ class KRPurchaseMembershipController extends GetxController {
final dir = await getApplicationDocumentsDirectory(); final dir = await getApplicationDocumentsDirectory();
final debugFile = File('${dir.path}/PURCHASE_CONTROLLER_DEBUG.txt'); final debugFile = File('${dir.path}/PURCHASE_CONTROLLER_DEBUG.txt');
await debugFile.writeAsString( await debugFile.writeAsString(
'=' * 60 + '\n' '=' * 60 +
'💳 PurchaseMembershipController.onInit 被调用!\n' '\n'
'时间: ${DateTime.now()}\n' '💳 PurchaseMembershipController.onInit 被调用!\n'
'版本标识: Android15_Fix_v6_Final\n' '时间: ${DateTime.now()}\n'
'=' * 60 + '\n', '版本标识: Android15_Fix_v6_Final\n'
'=' *
60 +
'\n',
mode: FileMode.append, mode: FileMode.append,
); );
print('💳 [PurchaseMembership] ✅ 调试日志已写入文件: ${debugFile.path}'); print('💳 [PurchaseMembership] ✅ 调试日志已写入文件: ${debugFile.path}');
@ -178,7 +185,8 @@ class KRPurchaseMembershipController extends GetxController {
KRLogUtil.kr_w('获取支付方式超时', tag: 'PurchaseMembership'); KRLogUtil.kr_w('获取支付方式超时', tag: 'PurchaseMembership');
}, },
); );
print('💳 [PurchaseMembership] ✓ 步骤4完成支付方式数量: ${kr_paymentMethods.length}'); print(
'💳 [PurchaseMembership] ✓ 步骤4完成支付方式数量: ${kr_paymentMethods.length}');
// //
kr_showPlanSelector.value = kr_plans.length > 1; kr_showPlanSelector.value = kr_plans.length > 1;
@ -193,7 +201,8 @@ class KRPurchaseMembershipController extends GetxController {
bool domainSwitched = await KRDomain.kr_switchToNextDomain(); bool domainSwitched = await KRDomain.kr_switchToNextDomain();
if (domainSwitched) { if (domainSwitched) {
print('💳 [PurchaseMembership] ✓ 域名切换成功,当前域名: ${KRDomain.kr_currentDomain}'); print(
'💳 [PurchaseMembership] ✓ 域名切换成功,当前域名: ${KRDomain.kr_currentDomain}');
print('💳 [PurchaseMembership] 🔄 使用新域名重试...'); print('💳 [PurchaseMembership] 🔄 使用新域名重试...');
// HttpUtil baseUrl // HttpUtil baseUrl
@ -300,7 +309,8 @@ class KRPurchaseMembershipController extends GetxController {
final either = await _kr_subscribeApi.kr_getPublicPaymentMethods(); final either = await _kr_subscribeApi.kr_getPublicPaymentMethods();
either.fold( either.fold(
(error) { (error) {
KRLogUtil.kr_e('获取公开支付方式失败: ${error.msg}', tag: 'PurchaseMembershipController'); KRLogUtil.kr_e('获取公开支付方式失败: ${error.msg}',
tag: 'PurchaseMembershipController');
}, },
(responseData) { (responseData) {
KRLogUtil.kr_i('✅ 获取公开支付方式成功', tag: 'PurchaseMembershipController'); KRLogUtil.kr_i('✅ 获取公开支付方式成功', tag: 'PurchaseMembershipController');
@ -310,14 +320,14 @@ class KRPurchaseMembershipController extends GetxController {
final paymentMethodsData = KRPaymentMethods.fromJson(responseData); final paymentMethodsData = KRPaymentMethods.fromJson(responseData);
kr_paymentMethods.value = paymentMethodsData.list; kr_paymentMethods.value = paymentMethodsData.list;
KRLogUtil.kr_i('📊 支付方式数据已加载,共 ${kr_paymentMethods.length}', tag: 'PurchaseMembershipController'); KRLogUtil.kr_i('📊 支付方式数据已加载,共 ${kr_paymentMethods.length}',
tag: 'PurchaseMembershipController');
// //
for (var method in kr_paymentMethods) { for (var method in kr_paymentMethods) {
KRLogUtil.kr_i( KRLogUtil.kr_i(
'💳 支付方式: ${method.name} (ID: ${method.id}, Platform: ${method.platform})', '💳 支付方式: ${method.name} (ID: ${method.id}, Platform: ${method.platform})',
tag: 'PurchaseMembershipController' tag: 'PurchaseMembershipController');
);
} }
print('═══════════════════════════════════════'); print('═══════════════════════════════════════');
@ -479,7 +489,7 @@ class KRPurchaseMembershipController extends GetxController {
if (isIOS) { if (isIOS) {
// 2. iOS 'stripe' // 2. iOS 'stripe'
final stripeMethod = kr_paymentMethods.firstWhere( final stripeMethod = kr_paymentMethods.firstWhere(
(method) => method.platform == 'Stripe', (method) => method.platform == 'apple_iap',
); );
if (stripeMethod != null) { if (stripeMethod != null) {
@ -516,11 +526,12 @@ class KRPurchaseMembershipController extends GetxController {
.any((subscribe) => subscribe.subscribeId == selectedPlan.kr_id); .any((subscribe) => subscribe.subscribeId == selectedPlan.kr_id);
final subscribeId = isRenewal final subscribeId = isRenewal
? _kr_alreadySubscribe ? _kr_alreadySubscribe
.firstWhere( .firstWhere(
(subscribe) => subscribe.subscribeId == selectedPlan.kr_id, (subscribe) => subscribe.subscribeId == selectedPlan.kr_id,
orElse: () => KRAlreadySubscribe(userSubscribeId: 0, subscribeId: 0), // orElse: () =>
) KRAlreadySubscribe(userSubscribeId: 0, subscribeId: 0), //
.userSubscribeId )
.userSubscribeId
: 0; : 0;
// //
@ -539,7 +550,7 @@ class KRPurchaseMembershipController extends GetxController {
); );
purchaseEither.fold( purchaseEither.fold(
(error) { (error) {
print('❌ 请求失败:'); print('❌ 请求失败:');
print(' 错误码: ${error.code}'); print(' 错误码: ${error.code}');
print(' 错误信息: ${error.msg}'); print(' 错误信息: ${error.msg}');
@ -579,7 +590,8 @@ class KRPurchaseMembershipController extends GetxController {
// //
final discount = plan.kr_discount[discountIndex]; final discount = plan.kr_discount[discountIndex];
// discount 0100% // discount 0100%
final discountRate = discount.kr_discount == 0 ? 100.0 : discount.kr_discount.toDouble(); final discountRate =
discount.kr_discount == 0 ? 100.0 : discount.kr_discount.toDouble();
return (plan.kr_unitPrice / 100) * return (plan.kr_unitPrice / 100) *
discount.kr_quantity * discount.kr_quantity *
(discountRate / 100); (discountRate / 100);
@ -594,8 +606,7 @@ class KRPurchaseMembershipController extends GetxController {
discountIndex < plan.kr_discount.length) { discountIndex < plan.kr_discount.length) {
// //
final discount = plan.kr_discount[discountIndex]; final discount = plan.kr_discount[discountIndex];
return (plan.kr_unitPrice / 100) * return (plan.kr_unitPrice / 100) * (discount.kr_discount / 100);
(discount.kr_discount / 100);
} }
return plan.kr_unitPrice / 100; return plan.kr_unitPrice / 100;
} }
@ -776,20 +787,21 @@ class KRPurchaseMembershipController extends GetxController {
final checkoutEither = await _kr_subscribeApi.kr_checkout(orderNo); final checkoutEither = await _kr_subscribeApi.kr_checkout(orderNo);
checkoutEither.fold( checkoutEither.fold(
(error) { (error) {
print('❌ Checkout 失败:'); print('❌ Checkout 失败:');
print(' 错误码: ${error.code}'); print(' 错误码: ${error.code}');
print(' 错误信息: ${error.msg}'); print(' 错误信息: ${error.msg}');
print('═══════════════════════════════════════'); print('═══════════════════════════════════════');
KRCommonUtil.kr_showToast(error.msg); KRCommonUtil.kr_showToast(error.msg);
}, },
(checkoutResponse) async { (checkoutResponse) async {
print('✅ Checkout 成功:'); print('✅ Checkout 成功:');
print(' 支付类型: ${checkoutResponse.type}'); print(' 支付类型: ${checkoutResponse.type}');
if (checkoutResponse.type == 'url') { if (checkoutResponse.type == 'url') {
// URL // URL
if (checkoutResponse.checkoutUrl != null && checkoutResponse.checkoutUrl!.isNotEmpty) { if (checkoutResponse.checkoutUrl != null &&
checkoutResponse.checkoutUrl!.isNotEmpty) {
print(' 支付链接: ${checkoutResponse.checkoutUrl}'); print(' 支付链接: ${checkoutResponse.checkoutUrl}');
print('🌐 正在用外部浏览器打开支付链接...'); print('🌐 正在用外部浏览器打开支付链接...');
print('═══════════════════════════════════════'); print('═══════════════════════════════════════');
@ -839,7 +851,8 @@ class KRPurchaseMembershipController extends GetxController {
} else if (checkoutResponse.type == 'stripe') { } else if (checkoutResponse.type == 'stripe') {
// Stripe Stripe // Stripe Stripe
print(' Stripe Method: ${checkoutResponse.stripe?.method}'); print(' Stripe Method: ${checkoutResponse.stripe?.method}');
print(' Stripe Client Secret: ${checkoutResponse.stripe?.clientSecret}'); print(
' Stripe Client Secret: ${checkoutResponse.stripe?.clientSecret}');
print('💳 显示 Stripe 支付表单...'); print('💳 显示 Stripe 支付表单...');
print('═══════════════════════════════════════'); print('═══════════════════════════════════════');
@ -854,7 +867,8 @@ class KRPurchaseMembershipController extends GetxController {
KRCommonUtil.kr_showToast('Apple Pay 仅在 iOS 可用'); KRCommonUtil.kr_showToast('Apple Pay 仅在 iOS 可用');
return; return;
} }
print('Platform: ${Platform.operatingSystem} ${Platform.operatingSystemVersion}'); print(
'Platform: ${Platform.operatingSystem} ${Platform.operatingSystemVersion}');
print('Merchant identifier: ${Stripe.merchantIdentifier}'); print('Merchant identifier: ${Stripe.merchantIdentifier}');
// Platform PayApple Pay // Platform PayApple Pay
final supported = await Stripe.instance.isPlatformPaySupported(); final supported = await Stripe.instance.isPlatformPaySupported();
@ -870,10 +884,9 @@ class KRPurchaseMembershipController extends GetxController {
cartItems: [ cartItems: [
ApplePayCartSummaryItem.immediate( ApplePayCartSummaryItem.immediate(
label: 'Hi快VPN 服务', label: 'Hi快VPN 服务',
amount: kr_getPlanPrice( amount: kr_getPlanPrice(kr_plans[kr_selectedPlanIndex.value],
kr_plans[kr_selectedPlanIndex.value], discountIndex: kr_selectedDiscountIndex.value)
discountIndex: kr_selectedDiscountIndex.value .toStringAsFixed(2),
).toStringAsFixed(2),
), ),
], ],
), ),
@ -897,10 +910,11 @@ class KRPurchaseMembershipController extends GetxController {
'stripe': checkoutResponse.stripe, 'stripe': checkoutResponse.stripe,
}, },
); );
} else if (checkoutResponse.type == 'balance') { } else if (checkoutResponse.type == 'balance') {
// Stripe Stripe // Stripe Stripe
print(' Stripe Method: ${checkoutResponse.stripe?.method}'); print(' Stripe Method: ${checkoutResponse.stripe?.method}');
print(' Stripe Client Secret: ${checkoutResponse.stripe?.clientSecret}'); print(
' Stripe Client Secret: ${checkoutResponse.stripe?.clientSecret}');
print('💳 显示 Stripe 支付表单...'); print('💳 显示 Stripe 支付表单...');
print('═══════════════════════════════════════'); print('═══════════════════════════════════════');
@ -913,13 +927,242 @@ class KRPurchaseMembershipController extends GetxController {
'checkout_type': 'balance', 'checkout_type': 'balance',
}, },
); );
} else if (checkoutResponse.type == 'apple_iap') {
if (!Platform.isIOS) {
KRCommonUtil.kr_showToast('仅限在 iOS 设备上购买');
return;
}
final KRIpaPayment iapParams = (checkoutResponse.ipa == null ||
(checkoutResponse.ipa?.productId.isEmpty ?? true))
? KRIpaPayment(
productId: 'com.hifastvpn.vip.day${kr_getSelectedQuantity()}')
: checkoutResponse.ipa!;
final iap = InAppPurchase.instance;
final available = await iap.isAvailable();
if (!available) {
print('IAP 不可用');
KRCommonUtil.kr_showToast('App Store 不可用');
return;
}
final productIds = {iapParams.productId};
final productDetailsResponse =
await iap.queryProductDetails(productIds);
if (productDetailsResponse.error != null) {
final e = productDetailsResponse.error!;
print('IAP 查询商品错误 code: ${e.code}');
print('IAP 查询商品错误 message: ${e.message}');
print('IAP 查询商品错误 iapParams.productId: ${iapParams.productId}');
KRCommonUtil.kr_showToast('无法获取商品信息: ${e.message}');
return;
}
if (productDetailsResponse.productDetails.isEmpty) {
print('IAP 商品列表为空 productId: ${iapParams.productId}');
KRCommonUtil.kr_showToast('未找到对应商品');
return;
}
final product = productDetailsResponse.productDetails.first;
final purchaseParam = PurchaseParam(
productDetails: product,
applicationUserName: iapParams.applicationUsername,
);
// ----------------------------------------------------------------------
// ProductDetails purchaseParam
// ----------------------------------------------------------------------
late StreamSubscription<List<PurchaseDetails>> subscription;
// 1 Stream
print('--- IAP 流程:购买 Stream 监听即将启动 ---');
subscription = iap.purchaseStream.listen(
(purchases) async {
print('IAP Stream 收到 ${purchases.length} 个交易事件');
for (final p in purchases) {
if (p.productID != iapParams.productId) continue;
final status = p.status.toString().split('.').last;
print('处理交易产品ID ${p.productID}, 状态: $status');
// =======================
// 1 Pending
// =======================
if (p.status == PurchaseStatus.pending) {
print('交易状态Pending等待 App Store...');
continue;
}
// =======================
// 2 Error
// =======================
if (p.status == PurchaseStatus.error) {
print('交易失败');
if (p.error != null) {
print('IAP 错误 code: ${p.error!.code}');
print('IAP 错误 message: ${p.error!.message}');
}
// complete
if (p.pendingCompletePurchase) {
await iap.completePurchase(p);
print('Error 状态交易已 complete');
}
KRCommonUtil.kr_showToast('购买失败');
continue;
}
// =======================
// 3 Purchased / Restored
// =======================
if (p.status == PurchaseStatus.purchased ||
p.status == PurchaseStatus.restored) {
// -----------------------
//
// -----------------------
final receiptData = p.verificationData.serverVerificationData;
final transactionId = p.purchaseID;
final productID = p.productID;
print('***************** IAP 交易凭证数据 *****************');
print('产品 ID (ProductID): $productID');
print('交易 ID (PurchaseID): $transactionId');
print('Receipt(Base64):');
print(receiptData);
print('**************************************************');
// -----------------------
// attach
// -----------------------
final attachEither =
await _kr_subscribeApi.kr_attachAppleIapTransaction(
transactionId!,
orderNo,
);
bool attachSuccess = false;
attachEither.fold(
(error) {
print('IAP attach失败 code: ${error.code}');
print('IAP attach失败 msg: ${error.msg}');
},
(ok) {
attachSuccess = true;
print('IAP attach成功: $ok');
},
);
// attach complete Apple
if (!attachSuccess) {
print('attach 失败,暂不 complete等待重试');
continue;
}
// -----------------------
// attach complete
// -----------------------
if (p.pendingCompletePurchase) {
await iap.completePurchase(p);
print('交易已 completePurchase');
}
// -----------------------
//
// -----------------------
Get.toNamed(
Routes.KR_ORDER_STATUS,
arguments: {
'order': orderNo,
'payment_type': paymentPlatform,
'checkout_type': 'ipa',
},
);
// cancel subscription
// Stream
}
}
},
onError: (error) {
print('--- IAP Stream 监听发生错误: $error ---');
},
onDone: () {
print('--- IAP Stream 已关闭 ---');
},
);
// 🔴 buy
await _cleanPendingPurchase(iap, iapParams.productId);
print('--- IAP 流程:即将调用 buyNonConsumable ---');
try {
final result =
await iap.buyNonConsumable(purchaseParam: purchaseParam);
print('--- IAP 流程buyNonConsumable 调用返回。返回值:$result ---');
if (!result) {
// false
print('IAP 购买请求本地失败:请检查 PurchaseParam 或设备设置。');
KRCommonUtil.kr_showToast('购买请求失败,请重试');
// Stream error
}
} catch (e) {
// Stream
print('--- IAP 流程buyNonConsumable 调用发生异常:$e ---');
subscription.cancel();
KRCommonUtil.kr_showToast('购买服务异常,请重试');
}
} else { } else {
print('⚠️ 未知的支付类型: ${checkoutResponse.type}'); print('⚠️ 未知的支付类型: ${checkoutResponse.type}');
print('═══════════════════════════════════════'); print('═══════════════════════════════════════');
// KRCommonUtil.kr_showToast('不支持的支付类型: ${checkoutResponse.type}'); // KRCommonUtil.kr_showToast('不支持的支付类型: ${checkoutResponse.type}');
} }
}, },
); );
} }
/// 🔴 pending
Future<void> _cleanPendingPurchase(
InAppPurchase iap,
String productId,
) async {
print('🔧 IAP 清理 pending 交易开始');
final completer = Completer<void>();
late StreamSubscription<List<PurchaseDetails>> tempSub;
tempSub = iap.purchaseStream.listen((purchases) async {
for (final p in purchases) {
if (p.productID != productId) continue;
if (p.pendingCompletePurchase) {
print('🔧 发现 pending 交易completePurchase');
await iap.completePurchase(p);
}
}
// 👇
await tempSub.cancel();
completer.complete();
});
// StoreKit
await Future.delayed(const Duration(milliseconds: 800));
if (!completer.isCompleted) {
await tempSub.cancel();
completer.complete();
}
print('🔧 IAP 清理 pending 交易完成');
}
} }

View File

@ -30,8 +30,8 @@ class BaseResponse<T> {
if (cipherText.isNotEmpty && nonce.isNotEmpty) { if (cipherText.isNotEmpty && nonce.isNotEmpty) {
try { try {
if (kDebugMode) { if (kDebugMode) {
print('═══════════════════════════════════════'); // print('═══════════════════════════════════════');
print('🔐 检测到加密响应,开始解密...'); // print('🔐 检测到加密响应,开始解密...');
print('📥 加密数据长度: ${cipherText.length} 字符'); print('📥 加密数据长度: ${cipherText.length} 字符');
print('⏰ 时间戳: $nonce'); print('⏰ 时间戳: $nonce');
} }

View File

@ -431,9 +431,6 @@ class _KRSimpleHttpInterceptor extends Interceptor {
if (kDebugMode) { if (kDebugMode) {
print(''); print('');
} }
if (kDebugMode) {
print('🔐 检测到加密请求,正在解密...');
}
// //
final encryptedData = data['data'] as String; final encryptedData = data['data'] as String;
final nonce = data['time'] as String; final nonce = data['time'] as String;

View File

@ -111,6 +111,8 @@ abstract class Api {
/// ///
static const String kr_getPublicPaymentMethods = "/v1/public/payment/methods"; static const String kr_getPublicPaymentMethods = "/v1/public/payment/methods";
static const String kr_attachAppleIapTransaction = "/v1/public/iap/apple/transactions/attach_by_id";
/// ///
static const String kr_getUserInfo = "/v1/public/user/info"; static const String kr_getUserInfo = "/v1/public/user/info";

View File

@ -329,4 +329,23 @@ class KRSubscribeApi {
return right(baseResponse.model); return right(baseResponse.model);
} }
Future<Either<HttpError, bool>> kr_attachAppleIapTransaction(String transactionId, String orderNo) async {
final Map<String, dynamic> data = <String, dynamic>{};
data['transaction_id'] = transactionId;
data['order_no'] = orderNo;
BaseResponse<KRStatus> baseResponse =
await HttpUtil.getInstance().request<KRStatus>(
Api.kr_attachAppleIapTransaction,
data,
method: HttpMethod.POST,
isShowLoading: true,
);
if (!baseResponse.isSuccess) {
return left(HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode));
}
return right(baseResponse.model.kr_bl);
}
} }

View File

@ -250,15 +250,15 @@ class KRSubscribeService {
} }
kr_currentSubscribe.value = updatedSubscribe; kr_currentSubscribe.value = updatedSubscribe;
KRLogUtil.kr_i('更新当前订阅信息', tag: 'SubscribeService'); // KRLogUtil.kr_i('更新当前订阅信息', tag: 'SubscribeService');
// //
kr_availableSubscribes.assignAll(subscribes); kr_availableSubscribes.assignAll(subscribes);
} }
} }
KRLogUtil.kr_i('获取可用订阅列表成功: ${subscribes.length} 个订阅', // KRLogUtil.kr_i('获取可用订阅列表成功: ${subscribes.length} 个订阅',
tag: 'SubscribeService'); // tag: 'SubscribeService');
KRLogUtil.kr_i( KRLogUtil.kr_i(
'订阅列表: ${subscribes.map((s) => '${s.name}(${s.id})').join(', ')}', '订阅列表: ${subscribes.map((s) => '${s.name}(${s.id})').join(', ')}',
tag: 'SubscribeService'); tag: 'SubscribeService');

View File

@ -861,6 +861,38 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.1" version: "2.4.1"
in_app_purchase:
dependency: "direct main"
description:
name: in_app_purchase
sha256: "11a40f148eeb4f681a0572003e2b33432e110c90c1bbb4f9ef83b81ec0c4f737"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
in_app_purchase_android:
dependency: transitive
description:
name: in_app_purchase_android
sha256: "45ae4fe253f85b4fcc58b421fe137f6e48aca16bf8a618cd760cb0542e7f854e"
url: "https://pub.dev"
source: hosted
version: "0.4.0"
in_app_purchase_platform_interface:
dependency: transitive
description:
name: in_app_purchase_platform_interface
sha256: "1d353d38251da5b9fea6635c0ebfc6bb17a2d28d0e86ea5e083bf64244f1fb4c"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
in_app_purchase_storekit:
dependency: transitive
description:
name: in_app_purchase_storekit
sha256: "6ce1361278cacc0481508989ba419b2c9f46a2b0dc54b3fe54f5ee63c2718fef"
url: "https://pub.dev"
source: hosted
version: "0.3.22+1"
intl: intl:
dependency: transitive dependency: transitive
description: description:

View File

@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 0.0.4+107 version: 0.0.4+108
environment: environment:
sdk: ">=3.5.0 <4.0.0" sdk: ">=3.5.0 <4.0.0"
@ -113,6 +113,7 @@ dependencies:
tray_manager: ^0.2.0 tray_manager: ^0.2.0
device_info_plus: ^11.3.0 device_info_plus: ^11.3.0
flutter_stripe: ^10.1.0 flutter_stripe: ^10.1.0
in_app_purchase: 3.2.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter