|
|
|
@ -9,6 +9,8 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; |
|
|
|
import '../../../data/services/network/dio_manager.dart'; |
|
|
|
import '../../../data/services/network/api.dart'; |
|
|
|
import 'package:flutter/foundation.dart'; |
|
|
|
import 'dart:async'; |
|
|
|
import 'package:app_links/app_links.dart'; |
|
|
|
|
|
|
|
/// goods 模块基础控制器,封装通用的商品加载与支付流程 |
|
|
|
abstract class GoodsBaseController extends GetxController { |
|
|
|
@ -17,11 +19,22 @@ abstract class GoodsBaseController extends GetxController { |
|
|
|
final RxString error = ''.obs; |
|
|
|
|
|
|
|
late final PayService _payService = Get.find<PayService>(); |
|
|
|
StreamSubscription<Uri?>? _deeplinkSub; |
|
|
|
late final AppLinks _appLinks = AppLinks(); |
|
|
|
|
|
|
|
@override |
|
|
|
void onInit() { |
|
|
|
super.onInit(); |
|
|
|
_init(); |
|
|
|
// 处理冷启动场景:如果 App 通过深链直接启动 |
|
|
|
_handleInitialDeepLink(); |
|
|
|
} |
|
|
|
|
|
|
|
@override |
|
|
|
void onClose() { |
|
|
|
_deeplinkSub?.cancel(); |
|
|
|
_deeplinkSub = null; |
|
|
|
super.onClose(); |
|
|
|
} |
|
|
|
|
|
|
|
Future<void> _init() async { |
|
|
|
@ -559,8 +572,13 @@ abstract class GoodsBaseController extends GetxController { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Future<void> _confirmOrderAndShow(String? orderId, |
|
|
|
{required String channel, String? initialMessage}) async { |
|
|
|
Future<void> _confirmOrderAndShow( |
|
|
|
String? orderId, { |
|
|
|
required String channel, |
|
|
|
String? initialMessage, |
|
|
|
int retries = 5, |
|
|
|
int intervalMs = 1500, |
|
|
|
}) async { |
|
|
|
// 若无法获取订单号,直接弹成功提示但提醒稍后刷新 |
|
|
|
if (orderId == null || orderId.isEmpty) { |
|
|
|
_showPayResultDialog(true, '${channel}支付成功,订单处理中,请稍后查看。'); |
|
|
|
@ -571,7 +589,7 @@ abstract class GoodsBaseController extends GetxController { |
|
|
|
bool success = false; |
|
|
|
try { |
|
|
|
// 简单轮询回查,避免回调延迟 |
|
|
|
for (int i = 0; i < 5; i++) { |
|
|
|
for (int i = 0; i < retries; i++) { |
|
|
|
final resp = await Api.payaskorder({'order_id': orderId}); |
|
|
|
print("查询订单 resp: $resp"); |
|
|
|
final order = resp?['order'] as Map<String, dynamic>?; |
|
|
|
@ -584,7 +602,7 @@ abstract class GoodsBaseController extends GetxController { |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
await Future.delayed(const Duration(milliseconds: 1500)); |
|
|
|
await Future.delayed(Duration(milliseconds: intervalMs)); |
|
|
|
} |
|
|
|
|
|
|
|
// 若标记成功但未拿到用户信息,使用token登录接口回拉用户资料,确保额度刷新 |
|
|
|
@ -728,7 +746,88 @@ abstract class GoodsBaseController extends GetxController { |
|
|
|
onResult?.call(false, '创建 PayPal 订单失败'); |
|
|
|
return false; |
|
|
|
} |
|
|
|
return _payService.paypalPay(approvalUrl: approvalUrl, onResult: onResult); |
|
|
|
final orderId = _payService.lastOrderId; // 创建订单时已缓存 |
|
|
|
print('[PayPal] 创建完成,orderId: $orderId, approvalUrl: $approvalUrl'); |
|
|
|
|
|
|
|
// 启动深链监听:当浏览器重定向到 deepvoiceclient://paypal/return 或 /cancel 时处理 |
|
|
|
_deeplinkSub?.cancel(); |
|
|
|
_deeplinkSub = _appLinks.uriLinkStream.listen((uri) async { |
|
|
|
if (uri == null) return; |
|
|
|
print('[PayPal] 深链回跳: ${uri.toString()}'); |
|
|
|
final token = uri.queryParameters['token'] ?? ''; |
|
|
|
if (token.isNotEmpty) { |
|
|
|
print('[PayPal] 回跳 token(订单ID): $token'); |
|
|
|
} |
|
|
|
final scheme = uri.scheme; |
|
|
|
final host = uri.host; |
|
|
|
final path = uri.path; |
|
|
|
if (scheme == 'deepvoiceclient' && host == 'paypal') { |
|
|
|
if (path == '/return') { |
|
|
|
print('[PayPal] 检测到 /return,使用 orderId 回查: $orderId'); |
|
|
|
await _confirmOrderAndShow( |
|
|
|
orderId, |
|
|
|
channel: 'PayPal', |
|
|
|
initialMessage: '支付完成,正在确认', |
|
|
|
retries: 1, |
|
|
|
intervalMs: 0, |
|
|
|
); |
|
|
|
} else if (path == '/cancel') { |
|
|
|
_showPayResultDialog(false, '已取消支付'); |
|
|
|
} |
|
|
|
// 一次性监听,处理后即取消 |
|
|
|
_deeplinkSub?.cancel(); |
|
|
|
_deeplinkSub = null; |
|
|
|
} |
|
|
|
}, onError: (e) { |
|
|
|
// 忽略监听错误,保持现有轮询逻辑 |
|
|
|
}); |
|
|
|
return _payService.paypalPay( |
|
|
|
approvalUrl: approvalUrl, |
|
|
|
onResult: (success, msg) async { |
|
|
|
onResult?.call(success, msg); |
|
|
|
if (success) { |
|
|
|
// 仅提示已打开授权页面,不在此处回查与弹窗,等待深链回跳后再处理 |
|
|
|
print('[PayPal] 已打开授权页面,等待回跳后再确认。orderId: $orderId'); |
|
|
|
Get.snackbar('提示', msg ?? '已打开 PayPal,请在浏览器完成支付', |
|
|
|
snackPosition: SnackPosition.BOTTOM, |
|
|
|
margin: const EdgeInsets.all(12)); |
|
|
|
} else { |
|
|
|
// 无法打开授权页才提示失败 |
|
|
|
_showPayResultDialog(false, msg ?? '无法打开 PayPal 支付页面'); |
|
|
|
} |
|
|
|
}, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
Future<void> _handleInitialDeepLink() async { |
|
|
|
try { |
|
|
|
final uri = await _appLinks.getInitialLink(); |
|
|
|
if (uri == null) return; |
|
|
|
print('[PayPal] 冷启动深链: ${uri.toString()}'); |
|
|
|
final token = uri.queryParameters['token'] ?? ''; |
|
|
|
if (token.isNotEmpty) { |
|
|
|
print('[PayPal] 冷启动 token(订单ID): $token'); |
|
|
|
} |
|
|
|
if (uri.scheme == 'deepvoiceclient' && uri.host == 'paypal') { |
|
|
|
if (uri.path == '/return') { |
|
|
|
final orderId = _payService.lastOrderId; |
|
|
|
if (orderId != null) { |
|
|
|
print('[PayPal] 冷启动 /return,使用 orderId 回查: $orderId'); |
|
|
|
await _confirmOrderAndShow( |
|
|
|
orderId, |
|
|
|
channel: 'PayPal', |
|
|
|
initialMessage: '支付完成,正在确认', |
|
|
|
retries: 1, |
|
|
|
intervalMs: 0, |
|
|
|
); |
|
|
|
} |
|
|
|
} else if (uri.path == '/cancel') { |
|
|
|
_showPayResultDialog(false, '已取消支付'); |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (_) { |
|
|
|
// 忽略异常,维持原有逻辑 |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|