Jelajahi Sumber

[new]增加支付状态查询流程

zk 1 tahun lalu
induk
melakukan
79ef9704b4

+ 2 - 0
assets/string/base/string.xml

@@ -120,4 +120,6 @@
     <string name="store_pay_wx_evn_error">微信未安装或微信版本不支持</string>
     <string name="store_pay_not_connect_store">无法连接到商店</string>
     <string name="store_pay_error">支付失败,请稍后重试</string>
+    <string name="store_query_pay_state">正在查询订单状态..</string>
+    <string name="store_pay_success">订购成功</string>
 </resources>

+ 6 - 0
lib/data/api/atmob_api.dart

@@ -9,6 +9,7 @@ import 'package:electronic_assistant/data/api/request/agenda_update_request.dart
 import 'package:electronic_assistant/data/api/request/chat_history_request.dart';
 import 'package:electronic_assistant/data/api/request/login_request.dart';
 import 'package:electronic_assistant/data/api/request/order_pay_request.dart';
+import 'package:electronic_assistant/data/api/request/order_status_request.dart';
 import 'package:electronic_assistant/data/api/request/talk_create_request.dart';
 import 'package:electronic_assistant/data/api/request/talk_delete_request.dart';
 import 'package:electronic_assistant/data/api/request/talk_generate_request.dart';
@@ -26,6 +27,7 @@ import 'package:electronic_assistant/data/api/response/example_info_response.dar
 import 'package:electronic_assistant/data/api/response/home_info_response.dart';
 import 'package:electronic_assistant/data/api/response/login_response.dart';
 import 'package:electronic_assistant/data/api/response/order_pay_response.dart';
+import 'package:electronic_assistant/data/api/response/order_status_response.dart';
 import 'package:electronic_assistant/data/api/response/store_index_response.dart';
 import 'package:electronic_assistant/data/api/response/talk_check_electric_response.dart';
 import 'package:electronic_assistant/data/api/response/talk_info_response.dart';
@@ -140,6 +142,10 @@ abstract class AtmobApi {
   @POST("/project/secretary/v1/order/pay")
   Future<BaseResponse<OrderPayResponse>> orderPay(
       @Body() OrderPayRequest request);
+
+  @POST("/project/secretary/v1/order/status")
+  Future<BaseResponse<OrderStatusResponse>> orderStatus(
+      @Body() OrderStatusRequest request);
 }
 
 final atmobApi = AtmobApi(defaultDio, baseUrl: Constants.baseUrl);

+ 18 - 0
lib/data/api/request/order_status_request.dart

@@ -0,0 +1,18 @@
+import 'package:electronic_assistant/base/app_base_request.dart';
+import 'package:json_annotation/json_annotation.dart';
+
+part 'order_status_request.g.dart';
+
+@JsonSerializable()
+class OrderStatusRequest extends AppBaseRequest {
+  @JsonKey(name: 'outTradeNo')
+  String outTradeNo;
+
+  @JsonKey(name: 'receiptData')
+  String? receiptData;
+
+  OrderStatusRequest(this.outTradeNo, this.receiptData);
+
+  @override
+  Map<String, dynamic> toJson() => _$OrderStatusRequestToJson(this);
+}

+ 14 - 0
lib/data/api/response/order_status_response.dart

@@ -0,0 +1,14 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'order_status_response.g.dart';
+
+@JsonSerializable()
+class OrderStatusResponse {
+  @JsonKey(name: 'payStatus')
+  int payStatus;
+
+  OrderStatusResponse(this.payStatus);
+
+  factory OrderStatusResponse.fromJson(Map<String, dynamic> json) =>
+      _$OrderStatusResponseFromJson(json);
+}

+ 4 - 6
lib/data/repositories/account_repository.dart

@@ -2,7 +2,6 @@ import 'dart:async';
 
 import 'package:electronic_assistant/base/app_base_request.dart';
 import 'package:electronic_assistant/data/api/atmob_api.dart';
-import 'package:electronic_assistant/data/bean/member_info.dart';
 import 'package:electronic_assistant/data/repositories/task_repository.dart';
 import 'package:electronic_assistant/utils/async_util.dart';
 import 'package:electronic_assistant/utils/cancel_future.dart';
@@ -10,7 +9,6 @@ import 'package:electronic_assistant/utils/event_bus.dart';
 import 'package:electronic_assistant/utils/mmkv_util.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:get/get.dart';
-
 import '../../resource/string.gen.dart';
 import '../../utils/http_handler.dart';
 import '../api/request/login_request.dart';
@@ -31,7 +29,7 @@ class AccountRepository {
   String? _phone;
   final isLogin = false.obs;
 
-  UserInfoResponse? _userInfo;
+  final Rxn<UserInfoResponse> _userInfo = Rxn<UserInfoResponse>();
 
   CancelableFuture? _getUserInfoFuture;
 
@@ -48,7 +46,7 @@ class AccountRepository {
     }
   }
 
-  UserInfoResponse? get userInfo => _userInfo;
+  Rxn<UserInfoResponse> get userInfo => _userInfo;
 
   String? get phone => _phone;
 
@@ -94,7 +92,7 @@ class AccountRepository {
         .userInfo(AppBaseRequest())
         .then(HttpHandler.handle(false))
         .then((response) {
-      _userInfo = response;
+      _userInfo.value = response;
       return response;
     });
   }
@@ -128,7 +126,7 @@ class AccountRepository {
     KVUtil.putString(ACCOUNT_PHONE, null);
     isLogin.value = false;
     taskRepository.stopTask();
-    _userInfo = null;
+    _userInfo.value = null;
 
     eventBus.emit(EventUserLogout);
   }

+ 10 - 2
lib/data/repositories/store_repository.dart

@@ -1,10 +1,9 @@
 import 'package:electronic_assistant/data/api/atmob_api.dart';
 import 'package:electronic_assistant/data/api/response/store_index_response.dart';
-import 'package:electronic_assistant/data/bean/store_item.dart';
 import 'package:electronic_assistant/utils/http_handler.dart';
-
 import '../../base/app_base_request.dart';
 import '../api/request/order_pay_request.dart';
+import '../api/request/order_status_request.dart';
 import '../api/response/order_pay_response.dart';
 
 class StoreRepository {
@@ -26,6 +25,15 @@ class StoreRepository {
         .orderPay(OrderPayRequest(itemId, payPlatform, payMethod))
         .then(HttpHandler.handle(false));
   }
+
+  Future<int> orderStatus(String outTradeNo, {String? receiptData}) {
+    return atmobApi
+        .orderStatus(OrderStatusRequest(outTradeNo, receiptData))
+        .then(HttpHandler.handle(false))
+        .then((data) {
+      return data.payStatus;
+    });
+  }
 }
 
 final StoreRepository storeRepository = StoreRepository._();

+ 2 - 2
lib/module/chat/controller.dart

@@ -42,8 +42,8 @@ class ChatController extends BaseController {
   @override
   void onReady() {
     super.onReady();
-    if (accountRepository.userInfo?.profession == null ||
-        accountRepository.userInfo?.post == null) {
+    if (accountRepository.userInfo.value?.profession == null ||
+        accountRepository.userInfo.value?.post == null) {
       showStartSheet();
     }
   }

+ 2 - 2
lib/module/chat/start/controller.dart

@@ -17,11 +17,11 @@ class ChatStartController extends BaseController {
   @override
   void onReady() {
     super.onReady();
-    String? profession = accountRepository.userInfo?.profession;
+    String? profession = accountRepository.userInfo.value?.profession;
     if (profession != null) {
       professionController.text = profession;
     }
-    String? post = accountRepository.userInfo?.post;
+    String? post = accountRepository.userInfo.value?.post;
     if (post != null) {
       postController.text = post;
     }

+ 47 - 22
lib/module/store/controller.dart

@@ -1,28 +1,29 @@
 import 'package:electronic_assistant/base/base_controller.dart';
 import 'package:electronic_assistant/data/bean/payment_way.dart';
 import 'package:electronic_assistant/data/repositories/store_repository.dart';
+import 'package:electronic_assistant/module/store/payment_status_manager.dart';
 import 'package:electronic_assistant/resource/string.gen.dart';
 import 'package:electronic_assistant/router/app_pages.dart';
 import 'package:electronic_assistant/sdk/pay/agile_pay.dart';
 import 'package:electronic_assistant/sdk/pay/alipay/ali_pay_info.dart';
 import 'package:electronic_assistant/sdk/pay/applepay/apple_pay_info.dart';
 import 'package:electronic_assistant/sdk/pay/code/agile_pay_code.dart';
-import 'package:electronic_assistant/utils/error_handler.dart';
 import 'package:electronic_assistant/utils/http_handler.dart';
 import 'package:electronic_assistant/utils/toast_util.dart';
 import 'package:get/get.dart';
 
 import '../../data/api/response/order_pay_response.dart';
+import '../../data/api/response/user_info_response.dart';
 import '../../data/bean/store_item.dart';
 import '../../data/bean/wechat_payment_sign_bean.dart';
 import '../../data/consts/constants.dart';
 import '../../data/consts/error_code.dart';
+import '../../data/repositories/account_repository.dart';
 import '../../dialog/loading_dialog.dart';
 import '../../sdk/pay/assist/product_type.dart';
-import '../../sdk/pay/listener/agile_pay_state.dart';
 import '../../sdk/pay/wxpay/wechat_pay_info.dart';
 
-class StoreController extends BaseController {
+class StoreController extends BaseController implements PaymentStatusCallback {
   final isExpanded = false.obs;
 
   final RxList<StoreItem> storeItems = <StoreItem>[].obs;
@@ -35,6 +36,10 @@ class StoreController extends BaseController {
 
   final RxBool isPaymentWayExpanded = false.obs;
 
+  final Rxn<UserInfoResponse> _userInfo = accountRepository.userInfo;
+
+  UserInfoResponse? get userInfo => _userInfo.value;
+
   @override
   void onInit() {
     super.onInit();
@@ -82,7 +87,7 @@ class StoreController extends BaseController {
           await storeRepository.orderPay(storeItem.id, payPlatform, payMethod);
 
       dynamic payInfo;
-
+      String outTradeNo = response.outTradeNo;
       if (payPlatform == PayPlatform.android) {
         if (payMethod == PayMethod.alipay) {
           payInfo = AliPayInfo(response.alipayOrderString!);
@@ -107,42 +112,62 @@ class StoreController extends BaseController {
             response.appAccountToken);
       }
       AgilePay.startPay(payInfo, success: (String? result) {
-        ToastUtil.showToast('支付成功');
-        LoadingDialog.hide();
+        LoadingDialog.show(StringName.storeQueryPayState.tr);
+        checkPaymentStatus(outTradeNo, paymentWay, storeItem);
+        Future.delayed(const Duration(seconds: 30), () {
+          LoadingDialog.hide();
+        });
       }, payError: (int error, String? errorMessage) {
-        ToastUtil.showToast(StringName.storePayError.tr);
+        errorPayToast(error);
         LoadingDialog.hide();
       }, error: (int errno, String? error) {
-        if (errno == AgilePayCode.payCodeNotSupport) {
-          ToastUtil.showToast(StringName.storePayNotSupport.tr);
-        } else if (errno == AgilePayCode.payCodeCancelError) {
-          ToastUtil.showToast(StringName.storePayUserCancel.tr);
-        } else if (errno == AgilePayCode.payCodeWxEnvError) {
-          ToastUtil.showToast(StringName.storePayWxEvnError.tr);
-        } else if (errno == AgilePayCode.payCodeNotConnectStore) {
-          ToastUtil.showToast(StringName.storePayNotConnectstore.tr);
-        } else {
-          ToastUtil.showToast(StringName.storePayError.tr);
-        }
+        errorPayToast(errno);
         LoadingDialog.hide();
       });
     } catch (error) {
+      LoadingDialog.hide();
       if (error is ServerErrorException &&
           error.code == ErrorCode.errorCodeNoLogin) {
         ToastUtil.showToast(StringName.errorCodeNoLogin.tr);
         Get.toNamed(RoutePath.login);
       } else {
-        ErrorHandler.toastError(error);
+        ToastUtil.showToast(StringName.storePayError.tr);
       }
     }
-    // finally {
-    //   LoadingDialog.hide();
-    // }
+  }
+
+  void errorPayToast(int errno) {
+    if (errno == AgilePayCode.payCodeNotSupport) {
+      ToastUtil.showToast(StringName.storePayNotSupport.tr);
+    } else if (errno == AgilePayCode.payCodeCancelError) {
+      ToastUtil.showToast(StringName.storePayUserCancel.tr);
+    } else if (errno == AgilePayCode.payCodeWxEnvError) {
+      ToastUtil.showToast(StringName.storePayWxEvnError.tr);
+    } else if (errno == AgilePayCode.payCodeNotConnectStore) {
+      ToastUtil.showToast(StringName.storePayNotConnectstore.tr);
+    } else {
+      ToastUtil.showToast(StringName.storePayError.tr);
+    }
+  }
+
+  void checkPaymentStatus(
+      String orderNo, PaymentWay paymentWay, StoreItem storeItemBean) {
+    paymentStatusManager.registerPaymentSuccessCallback(orderNo, this);
+    paymentStatusManager.checkPaymentStatus(orderNo, paymentWay, storeItemBean);
   }
 
   @override
   void onClose() {
     super.onClose();
     AgilePay.dispose();
+    paymentStatusManager.unregisterPaymentSuccessCallback(this);
+  }
+
+  @override
+  void onPaymentSuccess(
+      String orderNo, PaymentWay paymentWay, StoreItem storeItemBean) {
+    LoadingDialog.hide();
+    ToastUtil.showToast(StringName.storePaySuccess.tr);
+    Get.back();
   }
 }

+ 109 - 0
lib/module/store/payment_status_manager.dart

@@ -0,0 +1,109 @@
+import 'package:electronic_assistant/data/repositories/account_repository.dart';
+import 'package:electronic_assistant/data/repositories/store_repository.dart';
+import 'package:electronic_assistant/utils/async_util.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:synchronized/synchronized.dart';
+
+import '../../data/bean/payment_way.dart';
+import '../../data/bean/store_item.dart';
+import '../../utils/cancel_future.dart';
+
+class PaymentStatusManager {
+  PaymentStatusManager._();
+
+  //订单状态
+  //0-查询失败,继续轮询
+  //1-未支付,继续轮询
+  //2-支付成功
+  //3-支付关闭
+  //4-已退款
+  static const int payStatusFail = 0;
+  static const int payStatusUnpaid = 1;
+  static const int payStatusSuccess = 2;
+  static const int payStatusClose = 3;
+  static const int payStatusRefund = 4;
+
+  final Map<String, PaymentStatusCallback> callbackMap = {};
+  final Map<String, CancelableFuture> pollingSubscriptionMap = {};
+  final _lock = Lock();
+
+  void _startCheckPolling(
+      String orderNo, PaymentWay paymentWay, StoreItem storeItemBean) async {
+    await _lock.synchronized(() async {
+      pollingSubscriptionMap[orderNo]?.cancel();
+      debugPrint('开始轮询支付状态: orderNo = $orderNo');
+      CancelableFuture orderFuture = AsyncUtil.retryWithExponentialBackoff(
+          () {
+            return storeRepository.orderStatus(orderNo).then((status) {
+              if (status == payStatusSuccess) {
+                return true;
+              } else {
+                throw PaymentStatusException(status);
+              }
+            });
+          },
+          10,
+          (error) {
+            if (error is PaymentStatusException) {
+              return error.status == payStatusFail ||
+                  error.status == payStatusUnpaid;
+            }
+            return true;
+          });
+      orderFuture.then((data) async {
+        debugPrint('支付成功: orderNo = $orderNo');
+        accountRepository.refreshUserInfo();
+        await _lock.synchronized(() {
+          callbackMap[orderNo]
+              ?.onPaymentSuccess(orderNo, paymentWay, storeItemBean);
+          callbackMap.remove(orderNo);
+        });
+        reportPaySuccess(storeItemBean.amount, orderNo, storeItemBean.name,
+            paymentWay.payMethod);
+      }).catchError((error) {
+        debugPrint('支付失败: orderNo = $orderNo, error = $error');
+      });
+      pollingSubscriptionMap[orderNo] = orderFuture;
+    });
+  }
+
+  void reportPaySuccess(
+      int price, String orderId, String itemName, int paymentWay) {}
+
+  void checkPaymentStatus(
+      String orderNo, PaymentWay paymentWay, StoreItem storeItemBean) {
+    // recordKeyInfoToDisk(orderNo, paymentWay, storeItemBean);
+    _startCheckPolling(orderNo, paymentWay, storeItemBean);
+  }
+
+  void registerPaymentSuccessCallback(
+      String orderNo, PaymentStatusCallback callback) async {
+    await _lock.synchronized(() {
+      callbackMap[orderNo] = callback;
+    });
+  }
+
+  void unregisterPaymentSuccessCallback(PaymentStatusCallback callback) async {
+    await _lock.synchronized(() {
+      callbackMap.removeWhere((key, value) => value == callback);
+    });
+  }
+}
+
+class PaymentStatusException implements Exception {
+  final int status;
+
+  PaymentStatusException(this.status);
+
+  @override
+  String toString() {
+    return '支付状态异常: status = $status';
+  }
+}
+
+abstract class PaymentStatusCallback {
+  void onPaymentSuccess(
+      String orderNo, PaymentWay paymentWay, StoreItem storeItemBean);
+}
+
+final paymentStatusManager = PaymentStatusManager._();

+ 4 - 1
lib/module/store/view.dart

@@ -81,7 +81,10 @@ class StorePage extends BasePage<StoreController> {
             child: Text("电量",
                 style: TextStyle(color: Colors.white, fontSize: 12.w)),
           ),
-          Text("600", style: TextStyle(color: Colors.white, fontSize: 12.w)),
+          Obx(() {
+            return Text('${controller.userInfo?.memberInfo?.electric ?? '0'}',
+                style: TextStyle(color: Colors.white, fontSize: 12.w));
+          }),
         ],
       ),
     );

+ 3 - 3
pubspec.lock

@@ -1290,7 +1290,7 @@ packages:
     source: hosted
     version: "1.2.0"
   synchronized:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: synchronized
       sha256: "51b08572b9f091f8c3eb4d9d4be253f196ff0075d5ec9b10a884026d5b55d7bc"
@@ -1493,10 +1493,10 @@ packages:
     dependency: transitive
     description:
       name: vm_service
-      sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
+      sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
       url: "https://pub.dev"
     source: hosted
-    version: "14.2.5"
+    version: "14.2.4"
   wakelock_plus:
     dependency: transitive
     description:

+ 6 - 0
pubspec.yaml

@@ -99,6 +99,12 @@ dependencies:
   alipay_kit: ^6.0.0
   wechat_kit: ^6.0.1
   in_app_purchase: ^3.2.0
+
+  #并发
+  synchronized: ^3.3.0+2
+
+
+
 wechat_kit:
   app_id: "your_wechat_app_id"
   universal_link: "https://your_applinks_domain/universal_link/your_app/wechat/"