| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- /// 后端 API 服务:dio 封装 + 各支付渠道创建/查询/收银台接口
- library;
- import 'package:dio/dio.dart';
- import '../constants.dart';
- import '../models/checkout.dart';
- import '../models/payment.dart';
- /// 统一 API 客户端(单例)
- class APIServer {
- APIServer._() : dio = Dio() {
- dio.options
- ..baseUrl = kApiBaseUrl
- ..connectTimeout = const Duration(seconds: 15)
- ..receiveTimeout = const Duration(seconds: 15)
- ..responseType = ResponseType.json;
- }
- static final APIServer _instance = APIServer._();
- /// 获取单例
- factory APIServer() => _instance;
- final Dio dio;
- /// 后端返回的是否 mock 模式(判断依据:stripe publishable_key 是否为 mock)
- static bool isMockPublishableKey(String key) =>
- key.startsWith('pk_test_mock');
- /// 发起 POST 请求并解析
- Future<T> post<T>(
- String path,
- T Function(Map<String, dynamic> json) parse, {
- Map<String, dynamic>? body,
- }) async {
- final resp = await dio.post<Map<String, dynamic>>(path, data: body);
- return parse(resp.data!);
- }
- /// 发起 GET 请求并解析
- Future<T> get<T>(
- String path,
- T Function(Map<String, dynamic> json) parse,
- ) async {
- final resp = await dio.get<Map<String, dynamic>>(path);
- return parse(resp.data!);
- }
- // ===== 支付创建 =====
- /// 创建微信支付(source: app=App支付, web=Native扫码)
- Future<WechatPaymentCreatedResponse> createWechatPayment({
- required String productId,
- String source = 'app',
- }) {
- return post<WechatPaymentCreatedResponse>(
- '/v1/payment/wechatpay/',
- WechatPaymentCreatedResponse.fromJson,
- body: {'product_id': productId, 'source': source},
- );
- }
- /// 创建支付宝支付(source: app=App支付, web=手机网站支付)
- Future<OtherPayCreatedReponse> createAlipayPayment({
- required String productId,
- String source = 'app',
- }) {
- return post<OtherPayCreatedReponse>(
- '/v1/payment/alipay/',
- OtherPayCreatedReponse.fromJson,
- body: {'product_id': productId, 'source': source},
- );
- }
- /// 创建云闪付支付(source: app=App支付返回 tn 拉起云闪付, web=手机网站跳转)
- Future<UnionPayCreatedResponse> createUnionPayPayment({
- required String productId,
- String source = 'app',
- }) {
- return post<UnionPayCreatedResponse>(
- '/v1/payment/unionpay/',
- UnionPayCreatedResponse.fromJson,
- body: {'product_id': productId, 'source': source},
- );
- }
- /// 创建 Stripe 支付(PaymentIntent)
- Future<StripePaymentCreatedResponse> createStripePayment({
- required String productId,
- String source = 'app',
- String currency = 'cny',
- }) {
- return post<StripePaymentCreatedResponse>(
- '/v1/payment/stripe/',
- StripePaymentCreatedResponse.fromJson,
- body: {
- 'product_id': productId,
- 'source': source,
- 'currency': currency,
- },
- );
- }
- /// 创建 Apple Pay 支付(PSP 模式,响应与 Stripe 一致)
- Future<StripePaymentCreatedResponse> createApplePayment({
- required String productId,
- String source = 'app',
- String currency = 'usd',
- }) {
- return post<StripePaymentCreatedResponse>(
- '/v1/payment/apple/',
- StripePaymentCreatedResponse.fromJson,
- body: {
- 'product_id': productId,
- 'source': source,
- 'currency': currency,
- },
- );
- }
- /// 创建 Google Pay 支付(PSP 模式,响应与 Stripe 一致)
- Future<StripePaymentCreatedResponse> createGooglePayment({
- required String productId,
- String source = 'app',
- String currency = 'usd',
- }) {
- return post<StripePaymentCreatedResponse>(
- '/v1/payment/google/',
- StripePaymentCreatedResponse.fromJson,
- body: {
- 'product_id': productId,
- 'source': source,
- 'currency': currency,
- },
- );
- }
- // ===== 状态查询 =====
- /// 查询支付状态(最终确认 / 轮询)
- Future<PaymentStatus> queryPaymentStatus(String paymentId) {
- return get<PaymentStatus>(
- '/v1/payment/$paymentId',
- PaymentStatus.fromJson,
- );
- }
- // ===== 收银台 =====
- /// 创建网页收银台会话
- Future<CheckoutSessionResponse> createCheckoutSession({
- required String channel,
- required String productId,
- String? returnUrl,
- }) {
- return post<CheckoutSessionResponse>(
- '/v1/checkout/session',
- CheckoutSessionResponse.fromJson,
- body: {
- 'channel': channel,
- 'product_id': productId,
- 'return_url': returnUrl,
- },
- );
- }
- /// 查询收银台会话状态
- Future<CheckoutSessionStatus> queryCheckoutStatus(String paymentId) {
- return get<CheckoutSessionStatus>(
- '/v1/checkout/session/$paymentId',
- CheckoutSessionStatus.fromJson,
- );
- }
- // ===== 商品 =====
- /// 拉取商品列表
- Future<PaymentProducts> fetchProducts() {
- return get<PaymentProducts>(
- '/v1/products',
- PaymentProducts.fromJson,
- );
- }
- // ===== Mock 模拟成功(仅后端 MOCK_MODE) =====
- /// 模拟支付成功(走与真实回调相同的幂等流程)
- Future<void> simulatePaymentSuccess(String paymentId) async {
- await dio.post<Map<String, dynamic>>(
- '/v1/payment/simulate/success',
- data: {'payment_id': paymentId},
- );
- }
- // ===== 错误解析 =====
- /// 将异常统一解析为友好中文提示
- String resolveError(Object error) {
- if (error is DioException) {
- final data = error.response?.data;
- if (data is Map) {
- final err = data['error'];
- if (err is Map && err['message'] != null) {
- return '服务端错误:${err['message']}';
- }
- }
- switch (error.type) {
- case DioExceptionType.connectionTimeout:
- case DioExceptionType.receiveTimeout:
- case DioExceptionType.sendTimeout:
- return '网络超时,请稍后重试';
- case DioExceptionType.connectionError:
- return '无法连接服务器,请检查网络或后端是否启动';
- default:
- return '请求失败:${error.message}';
- }
- }
- return error.toString();
- }
- }
|