140 lines
4.3 KiB
Dart
140 lines
4.3 KiB
Dart
import 'dart:async';
|
||
|
||
import 'package:fluwx/fluwx.dart';
|
||
import 'package:tobias/tobias.dart';
|
||
|
||
import '../core/config/app_config.dart';
|
||
import 'payment_service.dart';
|
||
|
||
/// 支付宝/微信原生 SDK 调起封装(《03-接口文档》8.2 pay_params)
|
||
///
|
||
/// 服务端配置了商户密钥时 pay_params 为真实调起参数,此处调起原生 SDK;
|
||
/// 未配置(dev)时服务端返回 dev_mock 占位,调用方应以轮询兜底代替调起。
|
||
enum PayLaunchResult {
|
||
/// 渠道回调成功(仍需以服务端查单为准,避免客户端伪造)
|
||
success,
|
||
|
||
/// 用户主动取消
|
||
canceled,
|
||
|
||
/// 调起失败(未安装 App / 参数缺失 / SDK 报错)
|
||
failed,
|
||
|
||
/// pay_params 无有效调起参数(服务端未配置商户密钥)
|
||
unsupported,
|
||
}
|
||
|
||
class PayLauncher {
|
||
PayLauncher._();
|
||
|
||
static final _fluwx = Fluwx();
|
||
|
||
/// 微信调起等待回调的最长时间
|
||
static const _wechatTimeout = Duration(minutes: 5);
|
||
|
||
/// 支付宝调起:order_string 直接交给 SDK
|
||
static Future<PayLaunchResult> launchAlipay(RechargeOrderInfo order) async {
|
||
final orderString = order.payParams['order_string'] as String?;
|
||
if (orderString == null || orderString.isEmpty) {
|
||
return PayLaunchResult.unsupported;
|
||
}
|
||
try {
|
||
final result = await Tobias().pay(orderString);
|
||
return _mapAlipayStatus(result['resultStatus'] as String?);
|
||
} catch (_) {
|
||
return PayLaunchResult.failed;
|
||
}
|
||
}
|
||
|
||
/// 微信调起:registerApi → pay → 等待 SDK 回调
|
||
static Future<PayLaunchResult> launchWechat(RechargeOrderInfo order) async {
|
||
final params = order.payParams;
|
||
final appId = params['appid'] as String?;
|
||
final partnerId = params['partnerid'] as String?;
|
||
final prepayId = params['prepayid'] as String?;
|
||
final packageValue = params['package'] as String?;
|
||
final nonceStr = params['noncestr'] as String?;
|
||
final sign = params['sign'] as String?;
|
||
final timestamp = int.tryParse('${params['timestamp']}');
|
||
if (appId == null ||
|
||
appId.isEmpty ||
|
||
partnerId == null ||
|
||
partnerId.isEmpty ||
|
||
prepayId == null ||
|
||
prepayId.isEmpty ||
|
||
packageValue == null ||
|
||
packageValue.isEmpty ||
|
||
nonceStr == null ||
|
||
nonceStr.isEmpty ||
|
||
sign == null ||
|
||
sign.isEmpty ||
|
||
timestamp == null) {
|
||
return PayLaunchResult.unsupported;
|
||
}
|
||
|
||
final completer = Completer<PayLaunchResult>();
|
||
final cancelable = _fluwx.addSubscriber((response) {
|
||
if (response is WeChatPaymentResponse && !completer.isCompleted) {
|
||
completer.complete(_mapWechatErrCode(response.errCode));
|
||
}
|
||
});
|
||
|
||
try {
|
||
await _fluwx.registerApi(
|
||
appId: appId,
|
||
universalLink: AppConfig.wechatUniversalLink.isEmpty
|
||
? null
|
||
: AppConfig.wechatUniversalLink,
|
||
);
|
||
final sent = await _fluwx.pay(
|
||
which: Payment(
|
||
appId: appId,
|
||
partnerId: partnerId,
|
||
prepayId: prepayId,
|
||
packageValue: packageValue,
|
||
nonceStr: nonceStr,
|
||
timestamp: timestamp,
|
||
sign: sign,
|
||
),
|
||
);
|
||
if (!sent) return PayLaunchResult.failed;
|
||
return await completer.future.timeout(
|
||
_wechatTimeout,
|
||
onTimeout: () => PayLaunchResult.failed,
|
||
);
|
||
} catch (_) {
|
||
return PayLaunchResult.failed;
|
||
} finally {
|
||
cancelable.cancel();
|
||
}
|
||
}
|
||
|
||
/// 按支付方式分发(1支付宝 2微信)
|
||
static Future<PayLaunchResult> launch(RechargeOrderInfo order) {
|
||
return switch (order.payMethod) {
|
||
1 => launchAlipay(order),
|
||
2 => launchWechat(order),
|
||
_ => Future.value(PayLaunchResult.unsupported),
|
||
};
|
||
}
|
||
|
||
/// 支付宝 resultStatus:9000成功 6001用户取消 8000/8001处理中 其余失败
|
||
static PayLaunchResult _mapAlipayStatus(String? status) {
|
||
return switch (status) {
|
||
'9000' => PayLaunchResult.success,
|
||
'6001' => PayLaunchResult.canceled,
|
||
'8000' || '8001' => PayLaunchResult.success, // 处理中,交由服务端查单确认
|
||
_ => PayLaunchResult.failed,
|
||
};
|
||
}
|
||
|
||
/// 微信 errCode:0成功 -2用户取消 其余失败
|
||
static PayLaunchResult _mapWechatErrCode(int? errCode) {
|
||
return switch (errCode) {
|
||
0 => PayLaunchResult.success,
|
||
-2 => PayLaunchResult.canceled,
|
||
_ => PayLaunchResult.failed,
|
||
};
|
||
}
|
||
}
|