fix(client/orders): 继续支付改为 retry 恢复原单会话(原误进购买页致重复下单);409 已付自纠刷新
ci-pangolin / Lint — shellcheck (pull_request) Successful in 12s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (pull_request) Successful in 25s
ci-pangolin / Cleartext Scan — Android 禁明文 (pull_request) Successful in 20s
ci-pangolin / OpenAPI Sync Check (pull_request) Successful in 32s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (pull_request) Successful in 21s
ci-pangolin / Flutter — analyze + test (pull_request) Successful in 38s
ci-pangolin / Codegen Drift — token 生成物未漂移 (pull_request) Successful in 2s
ci-pangolin / DS-flow — 原型/跨端同源/代码色单源闸 (pull_request) Successful in 5s
ci-pangolin / Go — build + test (pull_request) Failing after 13s
ci-pangolin / E2E Smoke — L4 进程级端到端 (pull_request) Failing after 10s
ci-pangolin / Go — integration (mysql/redis testcontainers) (pull_request) Failing after 4m50s
ci-pangolin / Golden — 视觉回归 (全量:components/auth/desktop/tablet) (pull_request) Failing after 20s

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P9G7E3wmAYL9KeYCVZVsqu
This commit is contained in:
wangjia
2026-07-13 11:29:25 +08:00
parent 07c339c18e
commit 92943258ab
3 changed files with 111 additions and 4 deletions
+36 -1
View File
@@ -7,10 +7,11 @@ import 'package:pangolin_vpn/state/payment_provider.dart';
class _FakePaymentApi implements PaymentApi {
_FakePaymentApi();
int createCalls = 0, cancelCalls = 0, statusCalls = 0;
int createCalls = 0, cancelCalls = 0, statusCalls = 0, retryCalls = 0;
bool activated = false;
Exception? retryError;
String lastMethod = '';
String lastOrderNo = '';
@override
Future<List<PayCatalogItem>> catalog() async => const [];
@@ -35,6 +36,9 @@ class _FakePaymentApi implements PaymentApi {
@override
Future<PayOrder> retry(String orderNo, {required String method, Map<String, String>? metadata}) async {
retryCalls++;
lastMethod = method;
lastOrderNo = orderNo;
if (retryError != null) throw retryError!;
return PayOrder(orderNo: orderNo, session: PaySession(renderType: 'redirect', payload: const {'url': 'https://y'}, expiresAt: null));
}
@@ -100,4 +104,35 @@ void main() {
expect(api.lastMethod, 'alipay');
expect(c.read(paymentFlowProvider).order?.orderNo, 'pay2');
});
test('resume() 成功 → retry 恢复原单会话(不新下单),phase=awaitingPayment', () async {
final api = _FakePaymentApi();
final c = _container(api);
final ctl = c.read(paymentFlowProvider.notifier);
await ctl.resume('o-month', 'alipay');
expect(api.retryCalls, 1);
expect(api.createCalls, 0, reason: '继续支付不应新下单');
expect(api.lastOrderNo, 'o-month');
expect(api.lastMethod, 'alipay');
expect(c.read(paymentFlowProvider).phase, PaymentPhase.awaitingPayment);
expect(c.read(paymentFlowProvider).order?.orderNo, 'o-month');
});
test('resume() 遇 409 ORDER_NOT_PENDING(订单其实已变化)→ failed 自纠,不 crash', () async {
final api = _FakePaymentApi();
final c = _container(api);
final ctl = c.read(paymentFlowProvider.notifier);
api.retryError = const AuthApiException(
statusCode: 409, messageZh: 'x', messageEn: 'x', code: 'ORDER_NOT_PENDING');
await ctl.resume('o-month', '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);
});
}