Billing & Plans¶
Plans¶
Hierarchy: SIMPLE (0) → VIP (1) → PRO (2) → PERS (3).
There is also an administrative role GLAVA outside the plan ladder (platform staff, not sold).
| Feature | SIMPLE (free) | VIP (~$3/30 days) | PRO (~$5/30 days) | PERS (~$9/30 days) |
|---|---|---|---|---|
| HTTP/HTTPS tunnel, profile card | ✅ | ✅ | ✅ | ✅ |
| Public catalog listing | ✅ | ✅ | ✅ | ✅ |
| SSH tunnel (TCP 22) + SFTP | ❌ | ✅ | ✅ | ✅ |
| Security section — attack logs + email alerts | ❌ | ✅ | ✅ | ✅ |
| RDP, VNC tunnel | ❌ | ❌ | ✅ | ✅ |
| Traffic analytics (visits, UTM, geo, Excel export) | ❌ | ❌ | ✅ | ✅ |
| PostgreSQL, MySQL, Redis, MongoDB tunnel | ❌ | ❌ | ❌ | ✅ |
Sources of truth for this table (update both code and this table when a plan changes):
- TCP ports —
TcpPortController.SYSTEM_PORTS(minRoleper port) +dixu_proxy/README.md. - Security/incidents —
IncidentsController.MIN_ROLE,InternalController.ATTACK_NOTIFY_MIN_ROLE(both"VIP"). - Analytics —
AnalyticsController.MIN_ROLE("PRO"). - Prices/descriptions —
BillingController.TARIFFS.
Smart Billing¶
Instead of a fixed expiry date, each plan has a remaining-days counter (user_tariffs.remaining_days).
A user can hold multiple plans simultaneously: one active, the rest frozen; switching is instant.
- Deduction: 1 day per day for whichever plan was active that day — regardless of how
many times plans were switched within the day (lazy-deduction via
last_deducted_date). - Switching (
POST /api/billing/switch): unlimited per day; downgrading to a lower-tier plan is blocked while a higher-tier plan is active. - Auto-switch on expiry (
SmartBillingScheduler, daily at 00:05): whenremaining_days = 0on the active plan, looks for the next-highest frozen plan (PERS(3) > PRO(2) > VIP(1)), activates it, sends email; if none remain — falls back to SIMPLE. - Tunnel freeze: while
tunnel_frozen_by != 'none', all plan counters stop decrementing.
Days stack, not reset: adding 30 days to a plan with 5 remaining gives you 35 days.
Payment providers¶
Lemon Squeezy (international — card payments)¶
For users outside Russia. Checkout URL is built client-side via POST /api/billing/init-ls;
webhook handler at POST /api/billing/notify-ls credits 30 days on subscription_created /
subscription_payment_success.
Current status: code is fully deployed and ready. The
dixsu.lemonsqueezy.comstore is not yet activated — Stripe Connect onboarding requires a non-Russian bank account (IBAN/ routing number). Once the store is activated through Stripe Connect, card payments will work without any code changes.Setup guide: guides/lemon-squeezy.md
Tinkoff / T-Bank (Russia only)¶
For Russian users. Balance top-up + deduction. Endpoints: /api/billing/pay, /topup,
/notify, /payment-status/{id}, /history.
Private mode¶
A separate axis of the account (combined with the plan ladder, not a parallel ladder):
on first login at /auth/account-type-setup the user makes a permanent, irreversible
choice between public and private account. Stored in users.private_mode.
- Public — profile card, catalog, authored short links, weighted reviews.
- Private — none of the above; tunnel accessible only to the owner via their dix.su
session. On the free SIMPLE plan — 14-day trial (
users.private_trial_expires_at), after which the tunnel is frozen until a paid plan (VIP+) is activated.
Verification before granting private mode — a one-time 1 ₽ payment through Tinkoff
(POST /api/billing/verify-private) plus a self-entered full name. Payment confirmation
in /notify sets private_mode=true and private_trial_expires_at=NOW()+14 days.
Invited users (PRO and above) — a private account owner on PRO or higher can invite
other registered dix.su users by their slug; invitees pass the access gate the same way
as the owner. Limits by owner's plan (PrivateInviteController.MAX_INVITES): PRO — up to 10,
PERS — up to 100.
End-to-end encryption (Phase 2, deployed) — a separate entry point
e2e-<token>.{BASE_DOMAIN} without TLS termination on the server
(POST /api/private/e2e/mint, PrivateE2eController). The private key never
leaves the device. Verified on production in Chrome and Firefox. Details — Security.
Database schema¶
user_tariffs (migration V28) — role, remaining_days, status (active/frozen/expired),
last_deducted_date. Fields role/role_expires_at in users — cached current active plan
for fast access, updated on every switch.
Where to find the code¶
modulauth/.../billing/BillingController.java— all HTTP endpoints.modulauth/.../billing/TariffService.java— lazy-deduction, syncusers.role.modulauth/.../billing/SmartBillingScheduler.java— daily auto-switcher (@Scheduled).modulauth/.../billing/TinkoffService.java— Tinkoff integration (RU).modulauth/.../billing/LemonSqueezyService.java— Lemon Squeezy integration (international).