Implement billing functionality and enhance role management
- Introduced billing capabilities, allowing users to request payments for subscription periods (3/6/12 months) with admin approval via Telegram. - Updated role management to include a `BillingEnabled` property, preventing billing for admin roles. - Enhanced the `CreateRoleCommand` and `UpdateRoleCommand` to accept billing parameters, ensuring proper handling during role creation and updates. - Added new endpoints for billing management and integrated billing checks into VPN config creation to enforce payment requirements. - Updated related services, models, and tests to support the new billing features, ensuring comprehensive coverage and functionality. - Enhanced documentation to reflect the new billing processes and role management changes.
This commit is contained in:
@@ -3360,6 +3360,7 @@ export interface components {
|
||||
/** Format: int32 */
|
||||
maxIpLimit: number | string;
|
||||
isSystem: boolean;
|
||||
billingEnabled: boolean;
|
||||
};
|
||||
StatsDto: {
|
||||
/** Format: int32 */
|
||||
|
||||
@@ -173,6 +173,7 @@ export type RoleDto = {
|
||||
maxConfigs: number
|
||||
maxIpLimit: number
|
||||
isSystem: boolean
|
||||
billingEnabled: boolean
|
||||
}
|
||||
|
||||
/** Глобальная справочная цена за конфиг (видна только админу) — одна на весь сервис, не per-роль.
|
||||
@@ -183,6 +184,48 @@ export type PricingSettingsDto = {
|
||||
pricePerConfigPerYear: number | null
|
||||
}
|
||||
|
||||
export type PaymentPeriod = 'Quarter' | 'HalfYear' | 'Year'
|
||||
export type PaymentRequestStatus =
|
||||
| 'AwaitingPayment'
|
||||
| 'AwaitingConfirmation'
|
||||
| 'Confirmed'
|
||||
| 'Rejected'
|
||||
| 'Cancelled'
|
||||
|
||||
export type PaymentRequestDto = {
|
||||
id: string
|
||||
period: PaymentPeriod
|
||||
amountSnapshot: number
|
||||
status: PaymentRequestStatus
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export type BillingStatusDto = {
|
||||
billingEnabled: boolean
|
||||
paidUntil: string | null
|
||||
suspended: boolean
|
||||
requisitesText: string
|
||||
activeRequest: PaymentRequestDto | null
|
||||
}
|
||||
|
||||
/** Реквизиты + грейс-период для новых billing-пользователей (см. BillingSettings.DefaultGraceDays). */
|
||||
export type BillingSettingsDto = {
|
||||
requisitesText: string
|
||||
graceDays: number
|
||||
/** Начальное состояние чекбокса "Включить биллинг" в диалоге создания новой роли. */
|
||||
defaultBillingEnabledForNewRoles: boolean
|
||||
}
|
||||
|
||||
export type AdminPaymentRequestDto = {
|
||||
id: string
|
||||
userId: string
|
||||
userName: string
|
||||
period: PaymentPeriod
|
||||
amountSnapshot: number
|
||||
status: PaymentRequestStatus
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export type ActivationRequestAdminDto = {
|
||||
id: string
|
||||
userId: string
|
||||
|
||||
@@ -50,6 +50,7 @@ const resources = {
|
||||
instructions: 'Инструкции',
|
||||
news: 'Новости',
|
||||
support: 'Поддержка',
|
||||
billing: 'Оплата',
|
||||
settings: 'Настройки',
|
||||
admin: 'Админка',
|
||||
logout: 'Выйти',
|
||||
@@ -100,6 +101,40 @@ const resources = {
|
||||
},
|
||||
},
|
||||
|
||||
billing: {
|
||||
title: 'Оплата подписки',
|
||||
notApplicable: 'Биллинг не применяется к вашей роли.',
|
||||
status: 'Статус оплаты',
|
||||
suspended: 'Приостановлено',
|
||||
paidUntil: 'Оплачено до {{date}}',
|
||||
neverPaid: 'Оплата ещё не производилась.',
|
||||
newRequest: 'Оформить оплату',
|
||||
period: 'Период',
|
||||
periods: {
|
||||
Quarter: '3 месяца',
|
||||
HalfYear: 'Полгода',
|
||||
Year: 'Год',
|
||||
},
|
||||
createRequest: 'Создать заявку',
|
||||
requestCreated: 'Заявка создана.',
|
||||
activeRequest: 'Заявка на оплату',
|
||||
requestStatus: {
|
||||
AwaitingPayment: 'Ожидает оплаты',
|
||||
AwaitingConfirmation: 'На проверке у администратора',
|
||||
},
|
||||
requisites: 'Реквизиты для оплаты',
|
||||
requisitesMissing: 'Реквизиты ещё не настроены администратором.',
|
||||
sendToTelegram: 'Отправить в Telegram',
|
||||
requisitesSent: 'Реквизиты отправлены в Telegram.',
|
||||
telegramNotLinked: 'Сначала привяжите Telegram в настройках.',
|
||||
iPaid: 'Я оплатил',
|
||||
markedPaid: 'Отмечено — администратор проверит оплату.',
|
||||
cancelRequest: 'Отменить',
|
||||
confirmCancel: 'Отменить заявку на оплату?',
|
||||
requestCancelled: 'Заявка отменена.',
|
||||
awaitingAdminHint: 'Администратор уведомлён и проверит оплату. Конфиги не отключатся, пока заявка не решена.',
|
||||
},
|
||||
|
||||
instructions: {
|
||||
title: 'Инструкции по подключению',
|
||||
appsTitle: 'Приложения',
|
||||
@@ -197,6 +232,7 @@ const resources = {
|
||||
configs: 'Все конфиги',
|
||||
roles: 'Роли',
|
||||
pricing: 'Цены',
|
||||
billing: 'Оплата',
|
||||
nodes: 'Ноды',
|
||||
apps: 'Приложения',
|
||||
instructions: 'Инструкции',
|
||||
@@ -271,6 +307,8 @@ const resources = {
|
||||
created: 'Роль создана.',
|
||||
updated: 'Квота обновлена.',
|
||||
deleted: 'Роль удалена.',
|
||||
billingEnabledLabel: 'Включить биллинг для этой роли',
|
||||
billingBadge: 'биллинг',
|
||||
},
|
||||
pricing: {
|
||||
title: 'Справочная цена конфига',
|
||||
@@ -285,6 +323,35 @@ const resources = {
|
||||
yearCheaperThanQuarter: 'Цена за год (в пересчёте на 12 месяцев) не может быть меньше цены за 3 месяца.',
|
||||
updated: 'Цена обновлена.',
|
||||
},
|
||||
billing: {
|
||||
settingsTitle: 'Настройки биллинга',
|
||||
requisitesText: 'Реквизиты для оплаты',
|
||||
requisitesTextHint: 'Показываются пользователю вместе с заявкой на оплату.',
|
||||
graceDays: 'Грейс-период (дней)',
|
||||
graceDaysHint: 'Сколько дней доступ активен после назначения billing-роли, пока не поступит первая оплата.',
|
||||
defaultBillingEnabledForNewRolesLabel: 'Новые роли по умолчанию с включённым биллингом',
|
||||
settingsUpdated: 'Настройки биллинга обновлены.',
|
||||
requestsTitle: 'Заявки на оплату',
|
||||
allStatuses: 'Все статусы',
|
||||
user: 'Пользователь',
|
||||
period: 'Период',
|
||||
amount: 'Сумма',
|
||||
statusLabel: 'Статус',
|
||||
created: 'Создана',
|
||||
empty: 'Заявок не найдено.',
|
||||
confirm: 'Подтвердить',
|
||||
reject: 'Отклонить',
|
||||
confirmed: 'Оплата подтверждена.',
|
||||
rejected: 'Заявка отклонена.',
|
||||
rejectReasonPrompt: 'Причина отклонения (необязательно):',
|
||||
status: {
|
||||
AwaitingPayment: 'Ожидает оплаты',
|
||||
AwaitingConfirmation: 'На проверке',
|
||||
Confirmed: 'Подтверждена',
|
||||
Rejected: 'Отклонена',
|
||||
Cancelled: 'Отменена',
|
||||
},
|
||||
},
|
||||
nodes: {
|
||||
create: 'Добавить ноду',
|
||||
name: 'Название',
|
||||
@@ -492,6 +559,7 @@ const resources = {
|
||||
instructions: 'Instructions',
|
||||
news: 'News',
|
||||
support: 'Support',
|
||||
billing: 'Billing',
|
||||
settings: 'Settings',
|
||||
admin: 'Admin',
|
||||
logout: 'Log out',
|
||||
@@ -542,6 +610,40 @@ const resources = {
|
||||
},
|
||||
},
|
||||
|
||||
billing: {
|
||||
title: 'Subscription billing',
|
||||
notApplicable: 'Billing does not apply to your role.',
|
||||
status: 'Payment status',
|
||||
suspended: 'Suspended',
|
||||
paidUntil: 'Paid until {{date}}',
|
||||
neverPaid: 'No payment has been made yet.',
|
||||
newRequest: 'Set up payment',
|
||||
period: 'Period',
|
||||
periods: {
|
||||
Quarter: '3 months',
|
||||
HalfYear: 'Half a year',
|
||||
Year: 'A year',
|
||||
},
|
||||
createRequest: 'Create request',
|
||||
requestCreated: 'Request created.',
|
||||
activeRequest: 'Payment request',
|
||||
requestStatus: {
|
||||
AwaitingPayment: 'Awaiting payment',
|
||||
AwaitingConfirmation: 'Under admin review',
|
||||
},
|
||||
requisites: 'Payment details',
|
||||
requisitesMissing: 'The administrator has not set up payment details yet.',
|
||||
sendToTelegram: 'Send to Telegram',
|
||||
requisitesSent: 'Payment details sent to Telegram.',
|
||||
telegramNotLinked: 'Link Telegram in settings first.',
|
||||
iPaid: 'I paid',
|
||||
markedPaid: 'Marked — the administrator will verify the payment.',
|
||||
cancelRequest: 'Cancel',
|
||||
confirmCancel: 'Cancel this payment request?',
|
||||
requestCancelled: 'Request cancelled.',
|
||||
awaitingAdminHint: 'The administrator has been notified and will verify the payment. Configs stay active until the request is decided.',
|
||||
},
|
||||
|
||||
instructions: {
|
||||
title: 'Connection instructions',
|
||||
appsTitle: 'Apps',
|
||||
@@ -639,6 +741,7 @@ const resources = {
|
||||
configs: 'All configs',
|
||||
roles: 'Roles',
|
||||
pricing: 'Pricing',
|
||||
billing: 'Billing',
|
||||
nodes: 'Nodes',
|
||||
apps: 'Apps',
|
||||
instructions: 'Instructions',
|
||||
@@ -713,6 +816,8 @@ const resources = {
|
||||
created: 'Role created.',
|
||||
updated: 'Quota updated.',
|
||||
deleted: 'Role deleted.',
|
||||
billingEnabledLabel: 'Enable billing for this role',
|
||||
billingBadge: 'billing',
|
||||
},
|
||||
pricing: {
|
||||
title: 'Reference config price',
|
||||
@@ -727,6 +832,35 @@ const resources = {
|
||||
yearCheaperThanQuarter: 'The annual price (over 12 months) cannot be lower than the 3-month price.',
|
||||
updated: 'Pricing updated.',
|
||||
},
|
||||
billing: {
|
||||
settingsTitle: 'Billing settings',
|
||||
requisitesText: 'Payment details',
|
||||
requisitesTextHint: 'Shown to the user alongside their payment request.',
|
||||
graceDays: 'Grace period (days)',
|
||||
graceDaysHint: 'How many days access stays active after a billing role is assigned, before the first payment is due.',
|
||||
defaultBillingEnabledForNewRolesLabel: 'New roles default to billing enabled',
|
||||
settingsUpdated: 'Billing settings updated.',
|
||||
requestsTitle: 'Payment requests',
|
||||
allStatuses: 'All statuses',
|
||||
user: 'User',
|
||||
period: 'Period',
|
||||
amount: 'Amount',
|
||||
statusLabel: 'Status',
|
||||
created: 'Created',
|
||||
empty: 'No requests found.',
|
||||
confirm: 'Confirm',
|
||||
reject: 'Reject',
|
||||
confirmed: 'Payment confirmed.',
|
||||
rejected: 'Request rejected.',
|
||||
rejectReasonPrompt: 'Rejection reason (optional):',
|
||||
status: {
|
||||
AwaitingPayment: 'Awaiting payment',
|
||||
AwaitingConfirmation: 'Under review',
|
||||
Confirmed: 'Confirmed',
|
||||
Rejected: 'Rejected',
|
||||
Cancelled: 'Cancelled',
|
||||
},
|
||||
},
|
||||
nodes: {
|
||||
create: 'Add node',
|
||||
name: 'Name',
|
||||
|
||||
Reference in New Issue
Block a user