fix(client/payment): switchMethod 补 catch-all 兜网络/超时错(与 start/resume 对齐)防卡 awaitingPayment
switchMethod 的首个 retry() 调用只 catch AuthApiException,网络/超时/未知异常会穿出到 无 try/catch 的 _pickAndSwitch,UI 卡在 awaitingPayment 无任何提示。补一个 catch-all, 与 start()/resume() 同一处理形状落 failed 相位(自动经 build() 切到 _failed() 呈现); payment_page.dart 的 _pickAndSwitch 再加一层防御性 try/catch + toast,防任何逃逸异常 变成无提示的 unhandled rejection。补单测覆盖非 Auth 异常路径。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P9G7E3wmAYL9KeYCVZVsqu
This commit is contained in:
@@ -245,9 +245,16 @@ class _PaymentScreenState extends ConsumerState<PaymentScreen> {
|
||||
]);
|
||||
}
|
||||
|
||||
// switchMethod 内部已把网络/超时/未知异常兜到 failed 相位(与 start/resume 对齐,
|
||||
// 会经 build() 的 phase 分支自动切到 _failed() 呈现)——这里的 try/catch 是最后一道
|
||||
// 防线,防止任何逃逸异常变成无提示的 unhandled rejection(照抄 _openRedirectUrl 的写法)。
|
||||
Future<void> _pickAndSwitch(BuildContext context, WidgetRef ref, PaymentFlowState s) async {
|
||||
final other = s.method == 'crypto' ? 'nezha' : 'crypto';
|
||||
await ref.read(paymentFlowProvider.notifier).switchMethod(other);
|
||||
try {
|
||||
await ref.read(paymentFlowProvider.notifier).switchMethod(other);
|
||||
} catch (_) {
|
||||
if (context.mounted) showPangolinToast(context, t.orderCreateFailed);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _succeeded(BuildContext context, PaymentFlowState s) {
|
||||
|
||||
@@ -169,6 +169,15 @@ class PaymentFlowController extends StateNotifier<PaymentFlowState> {
|
||||
return;
|
||||
}
|
||||
state = state.copyWith(phase: PaymentPhase.failed, errorZh: e.messageZh, errorEn: e.messageEn);
|
||||
} catch (_) {
|
||||
// 网络/超时等非 Auth 异常也要落 failed —— 与 start()/resume() 同一形状,否则
|
||||
// 穿出到无 try/catch 的 _pickAndSwitch,UI 卡在 awaitingPayment 无提示。
|
||||
if (!mounted) return;
|
||||
state = state.copyWith(
|
||||
phase: PaymentPhase.failed,
|
||||
errorZh: '下单失败,请检查网络后重试',
|
||||
errorEn: 'Failed to create order, please check your network and retry',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -105,6 +105,20 @@ void main() {
|
||||
expect(c.read(paymentFlowProvider).order?.orderNo, 'pay2');
|
||||
});
|
||||
|
||||
test('switchMethod 遇非 Auth 异常(网络/超时)→ 落 failed 兜底,不卡 awaitingPayment', () async {
|
||||
final api = _FakePaymentApi();
|
||||
final c = _container(api);
|
||||
final ctl = c.read(paymentFlowProvider.notifier);
|
||||
await ctl.start(item, 'crypto');
|
||||
api.retryError = Exception('network timeout');
|
||||
await ctl.switchMethod('alipay');
|
||||
expect(c.read(paymentFlowProvider).phase, PaymentPhase.failed);
|
||||
expect(c.read(paymentFlowProvider).errorZh, isNotNull);
|
||||
expect(c.read(paymentFlowProvider).errorZh, isNotEmpty);
|
||||
expect(c.read(paymentFlowProvider).errorEn, isNotNull);
|
||||
expect(c.read(paymentFlowProvider).errorEn, isNotEmpty);
|
||||
});
|
||||
|
||||
test('resume() 成功 → retry 恢复原单会话(不新下单),phase=awaitingPayment', () async {
|
||||
final api = _FakePaymentApi();
|
||||
final c = _container(api);
|
||||
|
||||
Reference in New Issue
Block a user