瀏覽代碼

1、 调整StripeService 的 Provider 方式。
2、Form表单的提交真实的订单生成与支付流程

liukai 2 周之前
父節點
當前提交
6eef05d706

+ 26 - 20
packages/cpt_form/lib/modules/apply/vm/apply_view_model.dart

@@ -6,6 +6,7 @@ import 'package:domain/entity/form_list_entity.dart';
 import 'package:domain/repository/form_repository.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
+import 'package:plugin_platform/engine/loading/loading_engine.dart';
 import 'package:plugin_platform/engine/toast/toast_engine.dart';
 import 'package:plugin_platform/engine/notify/notify_engine.dart';
 import 'package:plugin_platform/http/dio/dio_cancelable_mixin.dart';
@@ -156,26 +157,31 @@ class ApplyViewModel extends _$ApplyViewModel with DioCancelableMixin {
 
   /// 提交Form表单
   void submitForm() async {
-    //   final result = await _formRepository.submitForm(
-    //     estateFormId: state.estateFormId,
-    //     typeId: state.formType,
-    //     content: state.formContentDetail,
-    //     cancelToken: cancelToken,
-    //   );
-    //
-    //   if (result.isSuccess) {
-    //     NotifyEngine.showSuccess(S.current.successful);
-    //     gotoNextPage();
-    //   } else {
-    //     ToastEngine.show(result.errorMsg ?? "UnKnow Error");
-    //   }
-    // }
-
-    //TODO 测试Stripe支付
-    // 测试卡号
-    // 4242 4242 4242 4242 – Visa (无需 3D)
-    // 4000 0025 0000 3155 – 需要 3D Secure 验证
-    ComponentServiceManager().paymentService.executePayment(orderId: "123456");
+    //支付流程 Loading 开始
+    LoadingEngine.show();
+
+    //调用接口生成订单
+    final result = await _formRepository.submitForm(
+      estateFormId: state.estateFormId,
+      typeId: state.formType,
+      content: state.formContentDetail,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      String orderId = result.data?.order?.id ?? "";
+      Log.d("当前Form生成的订单ID:$orderId");
+
+      //调用支付服务
+      bool success = await ComponentServiceManager().paymentService.executePayment(orderId: orderId);
 
+      //支付流程 Loading 结束
+      LoadingEngine.dismiss();
+      if (success){
+        gotoNextPage();
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "UnKnow Error");
+    }
   }
 }

+ 36 - 15
packages/cpt_payment/lib/provider/stripe_service.dart

@@ -1,24 +1,30 @@
+import 'package:domain/repository/payment_repository.dart';
 import 'package:flutter/material.dart';
 import 'package:plugin_platform/engine/notify/notify_engine.dart';
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
 import 'package:plugin_platform/platform_export.dart';
 import 'package:shared/utils/log_utils.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:shared/utils/util.dart';
 
-//定义一个top-level(全局)变量,页面引入该文件后可以直接使用
-var stripeService = StripeService();
+part 'stripe_service.g.dart';
 
-class StripeService {
-
-  StripeService._internal();
-
-  //保存单例
-  static final StripeService _singleton = StripeService._internal();
+@Riverpod(keepAlive: true)
+StripeService stripeService(Ref ref) {
+  return StripeService(ref.read(paymentRepositoryProvider));
+}
 
-  //工厂构造函数
-  factory StripeService() => _singleton;
+class StripeService {
+  final PaymentRepository _paymentRepository;
 
+  StripeService(this._paymentRepository); // 依赖注入 Dio 实例
 
   bool _isInitialized = false;
 
+  // 4242 4242 4242 4242 – Visa (无需 3D)
+  // 4000 0025 0000 3155 – 需要 3D Secure 验证
+
   //初始化 Stripe
   Future<void> _ensureInitialized() async {
     if (_isInitialized) return;
@@ -35,19 +41,25 @@ class StripeService {
     required String orderId,
   }) async {
     try {
+      //尝试初始化 Stripe SDK
       await _ensureInitialized();
 
-      // 实际开发中应从服务端获取 clientSecret
+      //从服务端获取 clientSecret
       final clientSecret = await _fetchPaymentIntent(orderId);
 
       await _initializePaymentSheet(clientSecret);
       await Stripe.instance.presentPaymentSheet();
+
       Log.d("presentPaymentSheet 关闭,用户操作完成 !");
+
       return await _verifyPaymentResult(clientSecret);
+
     } on StripeException catch (e) {
+      // Stripe SDK 出错
       _handleStripeError(e);
       return false;
     } catch (e) {
+      // 其他的错误捕获
       _handleGenericError(e);
       return false;
     }
@@ -55,8 +67,17 @@ class StripeService {
 
   ///调用网络请求
   Future<String> _fetchPaymentIntent(String orderId) async {
-    // 实际开发中调用后端接口获取
-    return "pi_3ROERGRpg7SPAcNn0sPDWCQI_secret_EZPly9t0IbKqaKA2CQwkUNIjd";
+    final result = await _paymentRepository.obtainPaymentIntent(orderId: orderId);
+    if (result.isSuccess) {
+      if (Utils.isNotEmpty(result.data?.clientSecret)) {
+        return result.data!.clientSecret!;
+      } else {
+        throw Exception("Empty client secret");
+      }
+    } else {
+      ToastEngine.show(result.errorMsg ?? "UnKnow Error");
+      throw Exception("Failed to get payment intent");
+    }
   }
 
   Future<void> _initializePaymentSheet(String clientSecret) async {
@@ -104,6 +125,6 @@ class StripeService {
 
   void _handleGenericError(dynamic e) {
     Log.e('System Error: $e');
-    NotifyEngine.showError('支付系统异常');
+    NotifyEngine.showError('支付异常:$e');
   }
-}
+}

+ 27 - 0
packages/cpt_payment/lib/provider/stripe_service.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'stripe_service.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$stripeServiceHash() => r'248042042fd310ebd7ad3f00515c409ed82dc5ef';
+
+/// See also [stripeService].
+@ProviderFor(stripeService)
+final stripeServiceProvider = Provider<StripeService>.internal(
+  stripeService,
+  name: r'stripeServiceProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$stripeServiceHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+@Deprecated('Will be removed in 3.0. Use Ref instead')
+// ignore: unused_element
+typedef StripeServiceRef = ProviderRef<StripeService>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

+ 2 - 1
packages/cpt_payment/lib/router/component/payment_component_service.dart

@@ -4,6 +4,7 @@
 import 'package:cpt_payment/modules/add_card/add_card_page.dart';
 import 'package:cpt_payment/modules/choose_card/choose_card_page.dart';
 import 'package:cpt_payment/provider/stripe_service.dart';
+import 'package:plugin_basic/provider/global_provider_container.dart';
 import 'package:router/componentRouter/payment_service.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -35,6 +36,6 @@ class PaymentComponentService extends PaymentService {
 
   @override
   Future<bool> executePayment({required String orderId}) {
-    return stripeService.executePayment(orderId: orderId);
+    return globalContainer.read(stripeServiceProvider).executePayment(orderId: orderId);
   }
 }

+ 3 - 0
packages/cs_domain/lib/constants/api_constants.dart

@@ -148,6 +148,9 @@ class ApiConstants {
   //可支付列表
   static const apiPaymentList = "/api/v1/user/payment/bill/index";
 
+  //生成支付意图
+  static const apiPaymentIntent = "/api/v1/user/payment/create-payment-intent";
+
   // =========================== 其他 ↓=========================================
 
   //服务器时间

+ 51 - 0
packages/cs_domain/lib/entity/form_order_entity.dart

@@ -0,0 +1,51 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/form_order_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/form_order_entity.g.dart';
+
+@JsonSerializable()
+class FormOrderEntity {
+	String? id;
+	@JSONField(name: "user_id")
+	String? userId;
+	int status = 0;
+	FormOrderOrder? order;
+
+	FormOrderEntity();
+
+	factory FormOrderEntity.fromJson(Map<String, dynamic> json) => $FormOrderEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $FormOrderEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}
+
+
+@JsonSerializable()
+class FormOrderOrder {
+	String? id;
+	@JSONField(name: "total_amount")
+	String? totalAmount;
+	@JSONField(name: "order_amount")
+	String? orderAmount;
+	@JSONField(name: "deposit_amount")
+	String? depositAmount;
+	@JSONField(name: "payment_status")
+	String? paymentStatus;
+	@JSONField(name: "refund_status")
+	String? refundStatus;
+
+	FormOrderOrder();
+
+	factory FormOrderOrder.fromJson(Map<String, dynamic> json) => $FormOrderOrderFromJson(json);
+
+	Map<String, dynamic> toJson() => $FormOrderOrderToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

+ 21 - 0
packages/cs_domain/lib/entity/payment_intent_entity.dart

@@ -0,0 +1,21 @@
+import 'package:domain/generated/json/base/json_field.dart';
+import 'package:domain/generated/json/payment_intent_entity.g.dart';
+import 'dart:convert';
+export 'package:domain/generated/json/payment_intent_entity.g.dart';
+
+@JsonSerializable()
+class PaymentIntentEntity {
+	@JSONField(name: "client_secret")
+	String? clientSecret;
+
+	PaymentIntentEntity();
+
+	factory PaymentIntentEntity.fromJson(Map<String, dynamic> json) => $PaymentIntentEntityFromJson(json);
+
+	Map<String, dynamic> toJson() => $PaymentIntentEntityToJson(this);
+
+	@override
+	String toString() {
+		return jsonEncode(this);
+	}
+}

+ 14 - 0
packages/cs_domain/lib/generated/json/base/json_convert_content.dart

@@ -15,6 +15,7 @@ import 'package:domain/entity/form_content_entity.dart';
 import 'package:domain/entity/form_detail_entity.dart';
 import 'package:domain/entity/form_list_entity.dart';
 import 'package:domain/entity/form_option_entity.dart';
+import 'package:domain/entity/form_order_entity.dart';
 import 'package:domain/entity/form_submitted_entity.dart';
 import 'package:domain/entity/form_submitted_page_entity.dart';
 import 'package:domain/entity/garage_sale_history_entity.dart';
@@ -40,6 +41,7 @@ import 'package:domain/entity/notice_board_event_entity.dart';
 import 'package:domain/entity/paid_service_detail_entity.dart';
 import 'package:domain/entity/paid_service_entity.dart';
 import 'package:domain/entity/paid_service_pay_success_info_entity.dart';
+import 'package:domain/entity/payment_intent_entity.dart';
 import 'package:domain/entity/payment_page_entity.dart';
 import 'package:domain/entity/property_news_detail_entity.dart';
 import 'package:domain/entity/property_news_entity.dart';
@@ -267,6 +269,12 @@ class JsonConvert {
     if (<FormOptionEntity>[] is M) {
       return data.map<FormOptionEntity>((Map<String, dynamic> e) => FormOptionEntity.fromJson(e)).toList() as M;
     }
+    if (<FormOrderEntity>[] is M) {
+      return data.map<FormOrderEntity>((Map<String, dynamic> e) => FormOrderEntity.fromJson(e)).toList() as M;
+    }
+    if (<FormOrderOrder>[] is M) {
+      return data.map<FormOrderOrder>((Map<String, dynamic> e) => FormOrderOrder.fromJson(e)).toList() as M;
+    }
     if (<FormSubmittedEntity>[] is M) {
       return data.map<FormSubmittedEntity>((Map<String, dynamic> e) => FormSubmittedEntity.fromJson(e)).toList() as M;
     }
@@ -465,6 +473,9 @@ class JsonConvert {
     if (<PaidServicePaySuccessInfoEstateUnit>[] is M) {
       return data.map<PaidServicePaySuccessInfoEstateUnit>((Map<String, dynamic> e) => PaidServicePaySuccessInfoEstateUnit.fromJson(e)).toList() as M;
     }
+    if (<PaymentIntentEntity>[] is M) {
+      return data.map<PaymentIntentEntity>((Map<String, dynamic> e) => PaymentIntentEntity.fromJson(e)).toList() as M;
+    }
     if (<PaymentPageEntity>[] is M) {
       return data.map<PaymentPageEntity>((Map<String, dynamic> e) => PaymentPageEntity.fromJson(e)).toList() as M;
     }
@@ -729,6 +740,8 @@ class JsonConvertClassCollection {
     (FormDetailEntity).toString(): FormDetailEntity.fromJson,
     (FormListEntity).toString(): FormListEntity.fromJson,
     (FormOptionEntity).toString(): FormOptionEntity.fromJson,
+    (FormOrderEntity).toString(): FormOrderEntity.fromJson,
+    (FormOrderOrder).toString(): FormOrderOrder.fromJson,
     (FormSubmittedEntity).toString(): FormSubmittedEntity.fromJson,
     (FormSubmittedEstateOnlineForm).toString(): FormSubmittedEstateOnlineForm.fromJson,
     (FormSubmittedPageEntity).toString(): FormSubmittedPageEntity.fromJson,
@@ -795,6 +808,7 @@ class JsonConvertClassCollection {
     (PaidServicePaySuccessInfoOrderProducts).toString(): PaidServicePaySuccessInfoOrderProducts.fromJson,
     (PaidServicePaySuccessInfoEstate).toString(): PaidServicePaySuccessInfoEstate.fromJson,
     (PaidServicePaySuccessInfoEstateUnit).toString(): PaidServicePaySuccessInfoEstateUnit.fromJson,
+    (PaymentIntentEntity).toString(): PaymentIntentEntity.fromJson,
     (PaymentPageEntity).toString(): PaymentPageEntity.fromJson,
     (PaymentPageList).toString(): PaymentPageList.fromJson,
     (PropertyNewsDetailEntity).toString(): PropertyNewsDetailEntity.fromJson,

+ 106 - 0
packages/cs_domain/lib/generated/json/form_order_entity.g.dart

@@ -0,0 +1,106 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/form_order_entity.dart';
+
+FormOrderEntity $FormOrderEntityFromJson(Map<String, dynamic> json) {
+  final FormOrderEntity formOrderEntity = FormOrderEntity();
+  final String? id = jsonConvert.convert<String>(json['id']);
+  if (id != null) {
+    formOrderEntity.id = id;
+  }
+  final String? userId = jsonConvert.convert<String>(json['user_id']);
+  if (userId != null) {
+    formOrderEntity.userId = userId;
+  }
+  final int? status = jsonConvert.convert<int>(json['status']);
+  if (status != null) {
+    formOrderEntity.status = status;
+  }
+  final FormOrderOrder? order = jsonConvert.convert<FormOrderOrder>(json['order']);
+  if (order != null) {
+    formOrderEntity.order = order;
+  }
+  return formOrderEntity;
+}
+
+Map<String, dynamic> $FormOrderEntityToJson(FormOrderEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['id'] = entity.id;
+  data['user_id'] = entity.userId;
+  data['status'] = entity.status;
+  data['order'] = entity.order?.toJson();
+  return data;
+}
+
+extension FormOrderEntityExtension on FormOrderEntity {
+  FormOrderEntity copyWith({
+    String? id,
+    String? userId,
+    int? status,
+    FormOrderOrder? order,
+  }) {
+    return FormOrderEntity()
+      ..id = id ?? this.id
+      ..userId = userId ?? this.userId
+      ..status = status ?? this.status
+      ..order = order ?? this.order;
+  }
+}
+
+FormOrderOrder $FormOrderOrderFromJson(Map<String, dynamic> json) {
+  final FormOrderOrder formOrderOrder = FormOrderOrder();
+  final String? id = jsonConvert.convert<String>(json['id']);
+  if (id != null) {
+    formOrderOrder.id = id;
+  }
+  final String? totalAmount = jsonConvert.convert<String>(json['total_amount']);
+  if (totalAmount != null) {
+    formOrderOrder.totalAmount = totalAmount;
+  }
+  final String? orderAmount = jsonConvert.convert<String>(json['order_amount']);
+  if (orderAmount != null) {
+    formOrderOrder.orderAmount = orderAmount;
+  }
+  final String? depositAmount = jsonConvert.convert<String>(json['deposit_amount']);
+  if (depositAmount != null) {
+    formOrderOrder.depositAmount = depositAmount;
+  }
+  final String? paymentStatus = jsonConvert.convert<String>(json['payment_status']);
+  if (paymentStatus != null) {
+    formOrderOrder.paymentStatus = paymentStatus;
+  }
+  final String? refundStatus = jsonConvert.convert<String>(json['refund_status']);
+  if (refundStatus != null) {
+    formOrderOrder.refundStatus = refundStatus;
+  }
+  return formOrderOrder;
+}
+
+Map<String, dynamic> $FormOrderOrderToJson(FormOrderOrder entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['id'] = entity.id;
+  data['total_amount'] = entity.totalAmount;
+  data['order_amount'] = entity.orderAmount;
+  data['deposit_amount'] = entity.depositAmount;
+  data['payment_status'] = entity.paymentStatus;
+  data['refund_status'] = entity.refundStatus;
+  return data;
+}
+
+extension FormOrderOrderExtension on FormOrderOrder {
+  FormOrderOrder copyWith({
+    String? id,
+    String? totalAmount,
+    String? orderAmount,
+    String? depositAmount,
+    String? paymentStatus,
+    String? refundStatus,
+  }) {
+    return FormOrderOrder()
+      ..id = id ?? this.id
+      ..totalAmount = totalAmount ?? this.totalAmount
+      ..orderAmount = orderAmount ?? this.orderAmount
+      ..depositAmount = depositAmount ?? this.depositAmount
+      ..paymentStatus = paymentStatus ?? this.paymentStatus
+      ..refundStatus = refundStatus ?? this.refundStatus;
+  }
+}

+ 26 - 0
packages/cs_domain/lib/generated/json/payment_intent_entity.g.dart

@@ -0,0 +1,26 @@
+import 'package:domain/generated/json/base/json_convert_content.dart';
+import 'package:domain/entity/payment_intent_entity.dart';
+
+PaymentIntentEntity $PaymentIntentEntityFromJson(Map<String, dynamic> json) {
+  final PaymentIntentEntity paymentIntentEntity = PaymentIntentEntity();
+  final String? clientSecret = jsonConvert.convert<String>(json['client_secret']);
+  if (clientSecret != null) {
+    paymentIntentEntity.clientSecret = clientSecret;
+  }
+  return paymentIntentEntity;
+}
+
+Map<String, dynamic> $PaymentIntentEntityToJson(PaymentIntentEntity entity) {
+  final Map<String, dynamic> data = <String, dynamic>{};
+  data['client_secret'] = entity.clientSecret;
+  return data;
+}
+
+extension PaymentIntentEntityExtension on PaymentIntentEntity {
+  PaymentIntentEntity copyWith({
+    String? clientSecret,
+  }) {
+    return PaymentIntentEntity()
+      ..clientSecret = clientSecret ?? this.clientSecret;
+  }
+}

+ 5 - 3
packages/cs_domain/lib/repository/form_repository.dart

@@ -15,6 +15,7 @@ import 'package:plugin_basic/provider/http_provider/http_provider.dart';
 import '../entity/form_content_entity.dart';
 import '../entity/form_detail_entity.dart';
 import '../entity/form_list_entity.dart';
+import '../entity/form_order_entity.dart';
 import '../entity/form_submitted_page_entity.dart';
 import '../entity/id_name_entity.dart';
 
@@ -163,7 +164,7 @@ class FormRepository {
   }
 
   /// 上传表单
-  Future<HttpResult> submitForm({
+  Future<HttpResult<FormOrderEntity>> submitForm({
     required String? estateFormId,
     required String? typeId,
     required FormContentEntity content,
@@ -285,13 +286,14 @@ class FormRepository {
       paths: paths,
       pathStreams: streams,
       method: HttpMethod.POST,
-      isShowLoadingDialog: true,
       networkDebounce: true,
       cancelToken: cancelToken,
     );
 
     if (result.isSuccess) {
-      return result.convert();
+      final json = result.getDataJson();
+      var data = FormOrderEntity.fromJson(json!);
+      return result.convert<FormOrderEntity>(data: data);
     }
     return result.convert();
   }

+ 24 - 0
packages/cs_domain/lib/repository/payment_repository.dart

@@ -1,5 +1,6 @@
 import 'dart:typed_data';
 
+import 'package:domain/entity/payment_intent_entity.dart';
 import 'package:domain/entity/payment_page_entity.dart';
 import 'package:plugin_platform/platform_export.dart';
 import 'package:plugin_platform/http/dio_engine.dart';
@@ -51,4 +52,27 @@ class PaymentRepository {
     }
     return result.convert();
   }
+
+  /// 根据 OrderId 生成对应的 PaymentIntent
+  Future<HttpResult<PaymentIntentEntity>> obtainPaymentIntent({
+    required String orderId,
+    CancelToken? cancelToken,
+  }) async {
+    Map<String, String> params = {};
+    params['order_id'] = orderId;
+
+    final result = await dioEngine.requestNetResult(
+      ApiConstants.apiPaymentIntent,
+      params: params,
+      method: HttpMethod.POST,
+      cancelToken: cancelToken,
+    );
+
+    if (result.isSuccess) {
+      final json = result.getDataJson();
+      var data = PaymentIntentEntity.fromJson(json!);
+      return result.convert<PaymentIntentEntity>(data: data);
+    }
+    return result.convert();
+  }
 }