Enhance billing request handling to support immediate config suspension and protection
CI / Backend (build + test) (push) Successful in 1m21s
CI / Frontend (lint + typecheck + build) (push) Successful in 33s

- Updated the `RejectPaymentRequestCommandHandler` to immediately suspend user configs if a payment request is rejected and no other pending subscription requests exist.
- Introduced `SuspendIfStillUnpaidAsync` method to handle the logic for suspending configs based on the user's billing status.
- Enhanced the `MarkPaymentSentCommandHandler` to protect configs during the payment confirmation process, ensuring users remain active while awaiting admin approval.
- Refactored `BillingConfigResumer` to include methods for protecting and suspending configs, improving the overall billing management flow.
- Updated tests to cover new behaviors and ensure proper functionality in various scenarios related to payment requests and config management.
This commit is contained in:
Leonid Pershin
2026-07-19 19:25:33 +03:00
parent 979eddf72e
commit b32756d5bc
8 changed files with 617 additions and 200 deletions
@@ -193,8 +193,9 @@ public class ConfirmPaymentRequestCommandHandlerTests
Assert.NotNull(expiredConfig.ExpiresAt);
Assert.NotNull(activeConfig.ExpiresAt);
Assert.Equal(expiredConfig.ExpiresAt, activeConfig.ExpiresAt);
// enable: null — возврат из приостановки теперь идёт через expiresAt (новый newPaidUntil),
// а не через переключение enable (см. IXuiPanelGateway.UpdateClientAsync).
// enable:true И expiresAt=новый newPaidUntil вместе (см. IXuiPanelGateway.UpdateClientAsync) —
// и для ранее Expired конфига (ext-1), и для уже Active (ext-2): пользователь мог доплатить
// заранее, панель должна узнать новый срок сразу, а не только когда конфиг реально просрочится.
await _gateway
.Received(1)
.UpdateClientAsync(
@@ -203,10 +204,22 @@ public class ConfirmPaymentRequestCommandHandlerTests
"ext-1",
VpnProtocol.Vless,
Arg.Any<string>(),
null,
true,
Arg.Is<DateTimeOffset?>(d => d == expiredConfig.ExpiresAt),
Arg.Any<CancellationToken>()
);
await _gateway
.Received(1)
.UpdateClientAsync(
Arg.Any<Node>(),
inbound.RemoteInboundId,
"ext-2",
VpnProtocol.Vless,
Arg.Any<string>(),
true,
Arg.Is<DateTimeOffset?>(d => d == activeConfig.ExpiresAt),
Arg.Any<CancellationToken>()
);
}
[Fact]