import 'package:agile_pay/flutter_pay.dart'; import 'package:carousel_slider/carousel_controller.dart'; import 'package:carousel_slider/carousel_options.dart'; import 'package:flutter/material.dart'; import 'package:injectable/injectable.dart'; import 'package:keyboard/base/base_controller.dart'; import 'package:get/get.dart'; import 'package:keyboard/data/bean/character_info.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/error_code.dart'; import '../../../data/consts/payment_type.dart'; import '../../../data/repository/account_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 '../../../resource/assets.gen.dart'; import '../../../resource/string.gen.dart'; import '../../../utils/async_util.dart'; import '../../../utils/atmob_log.dart'; import '../../../utils/date_util.dart'; import '../../../utils/error_handler.dart'; import '../../../utils/http_handler.dart'; import '../../../utils/payment_status_manager.dart'; import '../../../utils/toast_util.dart'; import '../store_banner_bean.dart'; @injectable class NewDiscountController extends BaseController implements PaymentStatusCallback { final String tag = "NewDiscountController"; final StoreRepository storeRepository; final AccountRepository accountRepository; final PaymentStatusManager paymentStatusManager; final RxList goodsInfoList = [].obs; final Rxn _selectedGoodsInfoItem = Rxn(); GoodsInfo? get selectedGoodsInfoItem => _selectedGoodsInfoItem.value; final Rxn _selectedPayWay = Rxn(); PayWayInfo? get selectedPayWay => _selectedPayWay.value; final RxList payWayList = [].obs; 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; final RxList _charactersList = [].obs; 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, ]; NewDiscountController( this.storeRepository, this.accountRepository, this.paymentStatusManager, ); onBannerChanged(int index, CarouselPageChangedReason reason) { _currentBanner.value = index; } void refreshStoreData() { _storeDataFuture?.cancel(); _storeDataFuture = AsyncUtil.retryWithExponentialBackoff( () => _requestGoodsInfoList(), 4, ); _storeDataFuture?.catchError((error) { ErrorHandler.toastError(error); }); } Future _requestGoodsInfoList() async { try { final response = await storeRepository.getMemberNewUserGoodsList(); goodsInfoList.clear(); payWayList.clear(); _charactersList.clear(); _selectedGoodsInfoItem.value = null; if (response.goodsInfoList?.isNotEmpty == true) { goodsInfoList.addAll(response.goodsInfoList!); _selectedGoodsInfoItem.value = goodsInfoList.first; } if (response.payInfoList?.isNotEmpty == true) { payWayList.addAll(response.payInfoList!); _selectedPayWay.value = payWayList.first; updateFilteredGoodsList(); } if (response.characterInfos?.isNotEmpty == true) { _charactersList.addAll(response.characterInfos!); } } catch (e) { AtmobLog.e(tag, e.toString()); } } 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() { Get.back(); } void clickPayNow() { 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 { ToastUtil.show(StringName.payNotSupport); } }) .catchError((error) { 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(() { 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(); }, ); } 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(); refreshStoreData(); } @override void onClose() { super.onClose(); _storeDataFuture?.cancel(); paymentStatusManager.unregisterPaymentSuccessCallback(this); } @override void onPaymentSuccess( String orderNo, PayWayInfo payWayInfo, GoodsInfo goodsInfo, ) { LoadingDialog.hide(); PaymentSuccessDialog.show( infoText: memberStatusInfo?.permanent == true ? StringName.paySuccessDialogPermanent : "${goodsInfo.name}${DateUtil.fromMillisecondsSinceEpoch('yyyy年MM月dd日', memberStatusInfo?.endTimestamp ?? 0)}${StringName.paySuccessDialogDesc}", btnConfirm: () { AtmobLog.d(tag, 'onGoodsItemClick: ${goodsInfo.toJson()}'); }, ); } }