import 'dart:io'; import 'package:agile_pay/flutter_pay.dart'; import 'package:apple_pay/apple_pay.dart'; import 'package:carousel_slider/carousel_controller.dart'; import 'package:carousel_slider/carousel_options.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:injectable/injectable.dart'; import 'package:keyboard/base/base_controller.dart'; import 'package:keyboard/data/bean/character_info.dart'; import 'package:keyboard/module/store/new_discount/member_card_bean.dart'; import 'package:keyboard/widget/platform_util.dart'; import '../../../data/api/response/user_info_response.dart'; import '../../../data/bean/goods_info.dart'; import '../../../data/bean/member_info.dart'; import '../../../data/bean/pay_way_info.dart'; import '../../../data/bean/wechat_payment_sign_bean.dart'; import '../../../data/consts/constants.dart'; import '../../../data/consts/error_code.dart'; import '../../../data/consts/event_report.dart'; import '../../../data/consts/payment_type.dart'; import '../../../data/repository/account_repository.dart'; import '../../../data/repository/config_repository.dart'; import '../../../data/repository/store_repository.dart'; import '../../../dialog/alipay_qr_code_dialog.dart'; import '../../../dialog/loading_dialog.dart'; import '../../../dialog/login/login_dialog.dart'; import '../../../dialog/member_agreement_dialog.dart'; import '../../../dialog/payment_fail_dialog.dart'; import '../../../dialog/payment_success_dialog.dart'; import '../../../dialog/wechat_qr_code_dialog.dart'; import '../../../handler/event_handler.dart'; import '../../../resource/assets.gen.dart'; import '../../../resource/string.gen.dart'; import '../../../utils/async_util.dart'; import '../../../utils/atmob_log.dart'; import '../../../utils/http_handler.dart'; import '../../../utils/payment_status_manager.dart'; import '../../../utils/toast_util.dart'; import '../store_banner_bean.dart'; import '../subscribe/recover_subscribe_dialog.dart'; import '../suprise/surprise_dialog.dart'; import '../ticket/discount_ticket_dialog.dart'; import 'new_discount_page.dart'; @injectable class NewDiscountController extends BaseController implements PaymentStatusCallback { final String tag = "NewDiscountController"; final StoreRepository storeRepository; final AccountRepository accountRepository; final ConfigRepository configRepository; final PaymentStatusManager paymentStatusManager; RxList get goodsInfoList => storeRepository.newDiscountGoodsInfoList; RxList get payWayList => storeRepository.newDiscountPayWayList; final Rxn _selectedGoodsInfoItem = Rxn(); GoodsInfo? get selectedGoodsInfoItem => _selectedGoodsInfoItem.value; final Rxn _selectedPayWay = Rxn(); PayWayInfo? get selectedPayWay => _selectedPayWay.value; CancelableFuture? _storeDataFuture; bool get isLogin => accountRepository.isLogin.value; final isAgree = false.obs; MemberInfo? get memberStatusInfo => accountRepository.memberStatusInfo.value; UserInfoResponse? get userInfo => accountRepository.userInfo.value; final RxList filteredGoodsList = [].obs; RxList get _charactersList => storeRepository.charactersList; List get charactersList => _charactersList; final CarouselSliderController carouselSliderController = CarouselSliderController(); final RxInt _currentBanner = 0.obs; int get currentBannerIndex => _currentBanner.value; /// 轮播图控制器 final List bannerList = [ Assets.images.iconNewDiscountBanner1, Assets.images.iconNewDiscountBanner2, Assets.images.iconNewDiscountBanner3, ]; final List memberCardList = [ MemberCardBean( imageUrl: Assets.images.iconNewDiscountMembershipCard1, title: StringName.newDiscountMemberCard1Title, desc: StringName.newDiscountMemberCard1Desc, gradient: LinearGradient( begin: Alignment(0.50, -0.00), end: Alignment(0.50, 1.00), colors: [const Color(0xFFF1EDFF), const Color(0xFFFAF8FF)], ), ), MemberCardBean( imageUrl: Assets.images.iconNewDiscountMembershipCard2, title: StringName.newDiscountMemberCard2Title, desc: StringName.newDiscountMemberCard2Desc, gradient: LinearGradient( begin: Alignment(0.50, -0.00), end: Alignment(0.50, 1.00), colors: [const Color(0xFFDCF4C0), const Color(0xFFFAFFF3)], ), ), MemberCardBean( imageUrl: Assets.images.iconNewDiscountMembershipCard3, title: StringName.newDiscountMemberCard3Title, desc: StringName.newDiscountMemberCard3Desc, gradient: LinearGradient( begin: Alignment(0.50, -0.00), end: Alignment(0.50, 1.00), colors: [const Color(0xFFF9DBFF), const Color(0xFFFFFBFE)], ), ), MemberCardBean( imageUrl: Assets.images.iconNewDiscountMembershipCard4, title: StringName.newDiscountMemberCard4Title, desc: StringName.newDiscountMemberCard4Desc, gradient: LinearGradient( begin: Alignment(0.50, -0.00), end: Alignment(0.50, 1.00), colors: [const Color(0xFFDBECFF), const Color(0xFFFBFBFF)], ), ), MemberCardBean( imageUrl: Assets.images.iconNewDiscountMembershipCard5, title: StringName.newDiscountMemberCard5Title, desc: StringName.newDiscountMemberCard5Desc, gradient: LinearGradient( begin: Alignment(0.50, -0.00), end: Alignment(0.50, 1.00), colors: [const Color(0xFFFFF7DB), const Color(0xFFFFFDFB)], ), ), ]; NewDiscountController( this.storeRepository, this.accountRepository, this.configRepository, this.paymentStatusManager, ); onBannerChanged(int index, CarouselPageChangedReason reason) { _currentBanner.value = index; } void refreshStoreData() { storeRepository.refreshNewDiscountGoodsInfoList(); } void updateFilteredGoodsList() { if (_selectedPayWay.value == null) { return; } _selectedGoodsInfoItem.value = null; int selectedPayWayId = _selectedPayWay.value!.id; filteredGoodsList.assignAll( goodsInfoList.where((goods) { return goods.payOptionIds == null || goods.payOptionIds!.contains(selectedPayWayId); }).toList(), ); if (filteredGoodsList.isNotEmpty) { _selectedGoodsInfoItem.value = filteredGoodsList.first; } } void onGoodsItemClick(GoodsInfo goodsInfo) { _selectedGoodsInfoItem.value = goodsInfo; debugPrint("'item: ${goodsInfo.toJson()}"); } void clickPayWaySwitch() { if (payWayList.isNotEmpty) { int currentIndex = payWayList.indexOf( _selectedPayWay.value ?? payWayList.first, ); int nextIndex = (currentIndex + 1) % payWayList.length; _selectedPayWay.value = payWayList[nextIndex]; updateFilteredGoodsList(); // 切换支付方式后,更新商品列表 } } void clickBack() { EventHandler.report(EventId.event_02002); Get.back(); } void clickPayNow() { EventHandler.report(EventId.event_02001); if (selectedGoodsInfoItem == null) { ToastUtil.show(StringName.memberPleaseChoiceGoods); return; } if (selectedPayWay == null) { ToastUtil.show(StringName.memberPleaseChoicePayment); return; } if (!isAgree.value) { MemberAgreementDialog.show( btnConfirm: () { isAgree.value = true; clickPayNow(); }, ); return; } AtmobLog.d(tag, 'clickPayNow: ${selectedGoodsInfoItem!.toJson()}'); final buyGoods = selectedGoodsInfoItem!; final buyPayWay = selectedPayWay!; int goodsId = buyGoods.id; int payPlatform = buyPayWay.payPlatform; int payMethod = buyPayWay.payMethod; int payWayType = getPayWayType( payMethod: payMethod, payPlatform: payPlatform, ); LoadingDialog.show(StringName.payLoading); storeRepository .submitAndRequestPay( goodsId: goodsId, payPlatform: payPlatform, payMethod: payMethod, ) .then((response) { if (payWayType == PayWayType.paymentWayWechat) { _onWeChatPay( response.outTradeNo, response.wechatPayPrepayJson!, payMethod, buyGoods, buyPayWay, ); } else if (payWayType == PayWayType.paymentWayWechatScan) { _onWechatScanPay( response.outTradeNo, response.wechatPayQrcodeUrl!, buyPayWay, buyGoods, ); } else if (payWayType == PayWayType.paymentWayAlipay) { _onAliPay( response.outTradeNo, response.alipayOrderString!, payMethod, buyGoods, buyPayWay, ); } else if (payWayType == PayWayType.paymentWayAlipayScan) { _onAliScanPay( response.outTradeNo, response.alipayQrcodeHtml!, buyPayWay, buyGoods, ); } else if (payWayType == PayWayType.paymentWayApple) { _onApplePay( response.outTradeNo, response.appAccountToken ?? "", buyPayWay, buyGoods, ); } else { ToastUtil.show(StringName.payNotSupport); } }) .catchError((error) { LoadingDialog.hide(); if (error is ServerErrorException) { if (error.code == ErrorCode.payOrderError) { refreshStoreData(); ToastUtil.show(error.message); } else if (error.code == ErrorCode.noLoginError) { ToastUtil.show(StringName.accountNoLogin); LoginDialog.show(); } else { ToastUtil.show(error.message); paymentFail(); } } else { ToastUtil.show(StringName.memberPaymentFailed); paymentFail(); } }) .whenComplete(() { if (PlatformUtil.isAndroid) { LoadingDialog.hide(); } }); } void paymentFail() { PaymentFailDialog.show( btnConfirm: () { clickPayNow(); }, ); } void _onAliScanPay( String outTradeNo, String qrHtml, PayWayInfo payWayInfo, GoodsInfo goodsInfo, ) { AlipayQrCodeDialog.show( qrCodeHtml: qrHtml, loadSuccessCallback: () { checkPaymentStatus(outTradeNo, payWayInfo, goodsInfo); }, onCloseCallback: () async { //关闭后再持续查询几秒 CustomLoadingDialog.show(); await Future.delayed(Duration(seconds: 4)); paymentStatusManager.removePollingSubscription(outTradeNo); CustomLoadingDialog.hide(); }, ); } void _onAliPay( String outTradeNo, String payJson, int payMethod, GoodsInfo buyGoods, PayWayInfo buyPayWay, ) { final payInfo = AliPayInfo(payJson); requestSdkPay(payInfo, outTradeNo, payMethod, buyGoods, buyPayWay); } void _onWeChatPay( String outTradeNo, String payJson, int payMethod, GoodsInfo buyGoods, PayWayInfo buyPayWay, ) { final bean = WechatPaymentSignBean.stringToBean(payJson); final payInfo = WechatPayInfo( appId: bean.appId, partnerId: bean.partnerId, prepayId: bean.prepayId, package: bean.package, noncestr: bean.nonceStr, timestamp: bean.timeStamp, sign: bean.sign, ); requestSdkPay(payInfo, outTradeNo, payMethod, buyGoods, buyPayWay); } void _onWechatScanPay( String outTradeNo, String qrCodeUrl, PayWayInfo payWayInfo, GoodsInfo goodsInfo, ) { WechatQrCodeDialog.show( qrCodeUrl: qrCodeUrl, loadSuccessCallback: () { checkPaymentStatus(outTradeNo, payWayInfo, goodsInfo); }, onCloseCallback: () async { //关闭后再持续查询几秒 CustomLoadingDialog.show(); await Future.delayed(Duration(seconds: 4)); paymentStatusManager.removePollingSubscription(outTradeNo); CustomLoadingDialog.hide(); }, ); } Future _onApplePay( String outTradeNo, String appAccountToken, PayWayInfo payWayInfo, GoodsInfo goodsInfo, ) async { final result = await ApplePay().purchase( productId: goodsInfo.appleGoodsId, appAccountToken: appAccountToken, ); if (result["success"] == true) { var receipt = result['receipt']; print('购买成功: ${result['receipt']}'); checkPaymentStatus( outTradeNo, payWayInfo, goodsInfo, receiptData: receipt, ); } else { LoadingDialog.hide(); ToastUtil.show("支付失败,请稍后重试"); print('购买失败: ${result['error']}'); } } void checkPaymentStatus( String orderNo, PayWayInfo payWayInfo, GoodsInfo goodsInfo, { String? receiptData, }) { paymentStatusManager.registerPaymentSuccessCallback(orderNo, this); paymentStatusManager.checkPaymentStatus( orderNo, payWayInfo, goodsInfo, receiptData: receiptData, ); } void requestSdkPay( dynamic payInfo, String outTradeNo, int payMethod, GoodsInfo buyGoods, PayWayInfo buyPayWay, ) { AgilePay.startPay( payInfo, success: (String? result) { LoadingDialog.show(StringName.payQuerypayState); checkPaymentStatus( outTradeNo, buyPayWay, buyGoods, receiptData: result, ); }, payError: (int error, String? errorMessage) { debugPrint('zk---payError: $error, $errorMessage'); paymentFail(); errorPayToast(error); errorEventReport(payMethod); }, error: (int errno, String? error) { debugPrint('zk---error: $errno, $error'); errorPayToast(errno); errorEventReport(payMethod); }, ); } void errorEventReport(int payMethod) { if (payMethod == PayMethod.wechat) { // EventHandler.report(); } else if (payMethod == PayMethod.alipay) { // EventHandler.report(); } else if (payMethod == PayMethod.apple) { // EventHandler.report(); } } void errorPayToast(int errno) { if (errno == AgilePayCode.payCodeNotSupport) { ToastUtil.show(StringName.payNotSupport); } else if (errno == AgilePayCode.payCodeCancelError) { ToastUtil.show(StringName.payUserCancel); } else if (errno == AgilePayCode.payCodeWxEnvError) { ToastUtil.show(StringName.payWxEvnError); } else if (errno == AgilePayCode.payCodeNotConnectStore) { ToastUtil.show(StringName.payNotConnectStore); } else { ToastUtil.show(StringName.payError); } } @override void onReady() { super.onReady(); EventHandler.report(EventId.event_02000); // refreshStoreData(); } @override void onInit() { super.onInit(); if (goodsInfoList.isNotEmpty) { _selectedGoodsInfoItem.value = goodsInfoList.first; } if (payWayList.isNotEmpty) { _selectedPayWay.value = payWayList.first; updateFilteredGoodsList(); } ever>(goodsInfoList, (list) { if (list.isNotEmpty) { _selectedGoodsInfoItem.value = list.first; } }); ever>(payWayList, (list) { if (list.isNotEmpty) { _selectedPayWay.value = list.first; updateFilteredGoodsList(); } }); } @override void onClose() { super.onClose(); _storeDataFuture?.cancel(); paymentStatusManager.unregisterPaymentSuccessCallback(this); if (!isFirstIntoDiscount() && !accountRepository.isVipUser) { if (PlatformUtil.isAndroid) { SurpriseDialog.show( clickConfirm: () { NewDiscountPage.start(); }, clickCancel: () {}, ); } if (PlatformUtil.isIOS) { DiscountTicketDialog.show( clickConfirm: () { NewDiscountPage.start(); }, clickCancel: () {}, ); } } else { setFirstIntoDiscount(false); } } @override void onPaymentSuccess( String orderNo, PayWayInfo payWayInfo, GoodsInfo goodsInfo, ) { if (PlatformUtil.isIOS) { accountRepository.refreshUserInfo(); // 300ms后关闭弹窗 Future.delayed(Duration(seconds: 2), () { LoadingDialog.hide(); AtmobLog.d(tag, 'onPaymentSuccess: $orderNo'); Get.back(); PaymentSuccessDialog.show( goodsInfo: goodsInfo, btnConfirm: () { AtmobLog.d(tag, 'onGoodsItemClick: ${goodsInfo.toJson()}'); }, ); }); } else { LoadingDialog.hide(); AtmobLog.d(tag, 'onPaymentSuccess: $orderNo'); Get.back(); PaymentSuccessDialog.show( goodsInfo: goodsInfo, btnConfirm: () { AtmobLog.d(tag, 'onGoodsItemClick: ${goodsInfo.toJson()}'); }, ); } } @override void onPaymentError(Error error) { // TODO: implement onPaymentError if (PlatformUtil.isIOS) { LoadingDialog.hide(); ErrorHandler.toastError(error); } } /// 恢复订阅 Future clickRestore() async { PayWayInfo? paymentWay = _selectedPayWay.value; if (paymentWay == null) { return; } int payPlatform = paymentWay.payPlatform; int payMethod = paymentWay.payMethod; CustomLoadingDialog.show(); Future.delayed(const Duration(seconds: 20), () { CustomLoadingDialog.hide(); ToastUtil.show("没有发现可恢复的记录"); }); final result = await ApplePay().restore(); if (result["success"] == true) { // CustomLoadingDialog.hide(); var receipt = result['receipt']; print('查找恢复记录成功: ${result['receipt']}'); checkRestoreStatus(receipt); } else { CustomLoadingDialog.hide(); ToastUtil.show("恢复失败"); print('恢复失败: ${result['error']}'); } // 显示恢复订阅弹窗 // RecoverSubscribeDialog.show("周会员2025年3月6日到期。", () { // AtmobLog.d(tag, "恢复订阅弹窗 => 点击确认"); // }); } /// 检查恢复订阅结果 Future checkRestoreStatus(String? receiptData) async { PayWayInfo? paymentWay = _selectedPayWay.value; if (paymentWay == null) { // ToastUtil.showToast(StringName.storeChoicePayment.tr); return; } if (receiptData == null) { return; } int payPlatform = paymentWay.payPlatform; int payMethod = paymentWay.payMethod; // var code = await storeRepository.resume(payPlatform, payMethod, receiptData); storeRepository .subscribeResume(payPlatform, payMethod, receiptData) .then((data) { CustomLoadingDialog.hide(); ToastUtil.show("恢复成功"); accountRepository.getUserInfo(); Get.back(); }) .catchError((error) { CustomLoadingDialog.hide(); ToastUtil.show("恢复失败"); }); // if (code == 0) { // CustomLoadingDialog.hide(); // ToastUtil.show("Restore success"); // userRepository.getUserInfo(); // Get.back(); // } else { // CustomLoadingDialog.hide(); // ToastUtil.show("Restore fail"); // } } /// 恢复订阅 Future clickRestore() async { PayWayInfo? paymentWay = _selectedPayWay.value; if (paymentWay == null) { return; } int payPlatform = paymentWay.payPlatform; int payMethod = paymentWay.payMethod; CustomLoadingDialog.show(); Future.delayed(const Duration(seconds: 20), () { CustomLoadingDialog.hide(); ToastUtil.show("Restore record not found"); }); final result = await ApplePay().restore(); if (result["success"] == true) { // CustomLoadingDialog.hide(); var receipt = result['receipt']; print('查找恢复记录成功: ${result['receipt']}'); checkRestoreStatus(receipt); } else { CustomLoadingDialog.hide(); ToastUtil.show("Pay Error"); print('恢复失败: ${result['error']}'); } // 显示恢复订阅弹窗 // RecoverSubscribeDialog.show("周会员2025年3月6日到期。", () { // AtmobLog.d(tag, "恢复订阅弹窗 => 点击确认"); // }); } /// 检查恢复订阅结果 Future checkRestoreStatus(String? receiptData) async { PayWayInfo? paymentWay = _selectedPayWay.value; if (paymentWay == null) { // ToastUtil.showToast(StringName.storeChoicePayment.tr); return; } if (receiptData == null) { return; } int payPlatform = paymentWay.payPlatform; int payMethod = paymentWay.payMethod; // var code = await storeRepository.resume(payPlatform, payMethod, receiptData); storeRepository.subscribeResume(payPlatform, payMethod, receiptData).then((data) { CustomLoadingDialog.hide(); ToastUtil.show("Restore success"); accountRepository.getUserInfo(); Get.back(); }).catchError((error) { CustomLoadingDialog.hide(); ToastUtil.show("Restore fail"); }); // if (code == 0) { // CustomLoadingDialog.hide(); // ToastUtil.show("Restore success"); // userRepository.getUserInfo(); // Get.back(); // } else { // CustomLoadingDialog.hide(); // ToastUtil.show("Restore fail"); // } } /// 恢复订阅 Future clickRestore() async { PayWayInfo? paymentWay = _selectedPayWay.value; if (paymentWay == null) { return; } int payPlatform = paymentWay.payPlatform; int payMethod = paymentWay.payMethod; CustomLoadingDialog.show(); Future.delayed(const Duration(seconds: 20), () { CustomLoadingDialog.hide(); ToastUtil.show("Restore record not found"); }); final result = await ApplePay().restore(); if (result["success"] == true) { // CustomLoadingDialog.hide(); var receipt = result['receipt']; print('查找恢复记录成功: ${result['receipt']}'); checkRestoreStatus(receipt); } else { CustomLoadingDialog.hide(); ToastUtil.show("Pay Error"); print('恢复失败: ${result['error']}'); } // 显示恢复订阅弹窗 // RecoverSubscribeDialog.show("周会员2025年3月6日到期。", () { // AtmobLog.d(tag, "恢复订阅弹窗 => 点击确认"); // }); } /// 检查恢复订阅结果 Future checkRestoreStatus(String? receiptData) async { PayWayInfo? paymentWay = _selectedPayWay.value; if (paymentWay == null) { // ToastUtil.showToast(StringName.storeChoicePayment.tr); return; } if (receiptData == null) { return; } int payPlatform = paymentWay.payPlatform; int payMethod = paymentWay.payMethod; // var code = await storeRepository.resume(payPlatform, payMethod, receiptData); storeRepository.subscribeResume(payPlatform, payMethod, receiptData).then((data) { CustomLoadingDialog.hide(); ToastUtil.show("Restore success"); accountRepository.getUserInfo(); Get.back(); }).catchError((error) { CustomLoadingDialog.hide(); ToastUtil.show("Restore fail"); }); // if (code == 0) { // CustomLoadingDialog.hide(); // ToastUtil.show("Restore success"); // userRepository.getUserInfo(); // Get.back(); // } else { // CustomLoadingDialog.hide(); // ToastUtil.show("Restore fail"); // } } /// 恢复订阅 Future clickRestore() async { PayWayInfo? paymentWay = _selectedPayWay.value; if (paymentWay == null) { return; } int payPlatform = paymentWay.payPlatform; int payMethod = paymentWay.payMethod; CustomLoadingDialog.show(); Future.delayed(const Duration(seconds: 20), () { CustomLoadingDialog.hide(); ToastUtil.show("没有发现可恢复的记录"); }); final result = await ApplePay().restore(); if (result["success"] == true) { // CustomLoadingDialog.hide(); var receipt = result['receipt']; print('查找恢复记录成功: ${result['receipt']}'); checkRestoreStatus(receipt); } else { CustomLoadingDialog.hide(); ToastUtil.show("恢复失败"); print('恢复失败: ${result['error']}'); } // 显示恢复订阅弹窗 // RecoverSubscribeDialog.show("周会员2025年3月6日到期。", () { // AtmobLog.d(tag, "恢复订阅弹窗 => 点击确认"); // }); } /// 检查恢复订阅结果 Future checkRestoreStatus(String? receiptData) async { PayWayInfo? paymentWay = _selectedPayWay.value; if (paymentWay == null) { // ToastUtil.showToast(StringName.storeChoicePayment.tr); return; } if (receiptData == null) { return; } int payPlatform = paymentWay.payPlatform; int payMethod = paymentWay.payMethod; // var code = await storeRepository.resume(payPlatform, payMethod, receiptData); storeRepository .subscribeResume(payPlatform, payMethod, receiptData) .then((data) { CustomLoadingDialog.hide(); ToastUtil.show("恢复成功"); accountRepository.getUserInfo(); Get.back(); }) .catchError((error) { CustomLoadingDialog.hide(); ToastUtil.show("恢复失败"); }); // if (code == 0) { // CustomLoadingDialog.hide(); // ToastUtil.show("Restore success"); // userRepository.getUserInfo(); // Get.back(); // } else { // CustomLoadingDialog.hide(); // ToastUtil.show("Restore fail"); // } } /// 恢复订阅 void clickRestore() { // 显示恢复订阅弹窗 // RecoverSubscribeDialog.show("周会员2025年3月6日到期。", () { // AtmobLog.d(tag, "恢复订阅弹窗 => 点击确认"); // }); } }