πŸ”‘

Telegram bot for selling VPN subscriptions

for a private VPN service: invite code, trial period, admin-approved payment receipt

A Telegram bot for selling VPN subscriptions on Marzban: invite-code registration, a 2-hour trial, 1, 6 and 12 month plans, and bank-transfer payment with the receipt approved by an admin. Python, aiogram 3, MariaDB.

Client testingNDA
2 hours
trial access is issued automatically after registering with an invite code
3 plans
1, 6 and 12 months; +10 days for 6 months and +30 days for 12 months
3 days and 1 day
the bot reminds the user to renew this long before the subscription ends

The task

For a closed circle of customers, VPN access had to be issued and renewed without manual work in the Marzban panel. Every step of such access needs an admin's attention: working out who has arrived, creating the user, sending the key, accepting payment, extending the term, reminding about renewal in time and explaining how to set up the app on a particular device.

What was needed was a Telegram bot in which the customer goes from invite-code registration to payment and receives a key on their own, while the admin only approves the payment and sees the overall picture. Payment by bank transfer with a human checking the receipt: no card acquiring to set up, and full control over whose payment is accepted.

The solution

A Telegram bot for selling VPN subscriptions on top of Marzban, built for a private VPN service. Implemented in version 2.0.0:

For the user

  • Invite-code registration. Without a code the bot gives no access; with a valid code it automatically issues 2 hours of trial access.
  • Connection profile. Subscription status, remaining time and the VLESS key from Marzban.
  • Purchase. 1, 6 and 12 month plans (prices are set by a setting). The bot shows the bank-transfer details and a link to the bank, and the user sends a screenshot of the receipt.
  • Bonuses for long plans. +10 days for 6 months, +30 days for 12 months.
  • Instructions. For iOS, Android, Windows, macOS, Linux, routers and Smart TV; they open as a WebApp, and for routers there is a button to contact a setup specialist.
  • Reminders. 3 days and 1 day before the subscription ends, checked daily at 10:00.
  • A tidy chat. The bot's service messages are deleted after an hour; there are an FAQ, support and the /myid command.

For the admin

  • Payment approval. Buttons under the receipt with protection against double processing by several admins; on approval the subscription is extended in Marzban.
  • The Root Access panel. Statistics, search by ID, user lists (all, active, expired), deletion with confirmation, gifting hours or days, a broadcast with a delivery report, logs and a manual backup.
  • Notifications. About new registrations, bot start and stop, and backup results.
  • A hidden /version command. It shows the version of the running instance; non-admins are ignored.

How it works

Invite-code registration and a 2-hour trial

An invite code is a shared password for entering a private service: without it the bot will not register a stranger. A valid code starts the automatic issue of 2 hours of trial access: the bot creates the user in Marzban itself, and the customer gets a key and can try the service before paying. An admin registers without a code, and their Marzban access lasts 24 hours.

Marzban API: creating a user with the xtls-rprx-vision flow

Marzban is a control panel for a VPN server: it stores users and issues keys. The bot calls its API (a set of addresses through which programs send commands), obtains a token and creates a user named user_ plus the Telegram ID. The user gets the xtls-rprx-vision flow, a limit of one online device and no traffic limit. Subscriptions are built on VLESS and Reality: a protocol and a disguise method the client app uses to connect to the server. On renewal the new term is counted from the current end date, not from the payment day.

Bank-transfer payment: the admin approves the receipt

The customer picks a plan, the bot shows the details and a link to the bank, the customer pays and sends a screenshot of the receipt. Every admin receives the receipt with Approve and Deny buttons under the message. After approval the subscription is extended in Marzban (with bonus days for 6 and 12 months) and the user is notified. This is a manual check: there is no automatic payment-system integration in the project.

Protection against double approval by several admins

The receipt goes to several admins at once, so two of them could press Approve and extend the subscription twice. The version history records a separate fix: payment handling is protected against repeated confirmation. The README does not describe how exactly the locking works.

Reminders and maintenance on a schedule

The APScheduler scheduler runs in the Europe/Moscow time zone. The check for expiring subscriptions starts at 10:00 and reminds 3 days and 1 day ahead. Old backups are cleaned at 00:30, and weekly database maintenance (removing old log records) runs on Mondays at 03:00.

Backups: local and to Google Drive from the bot

The bot makes a JSON dump of users, admins and the last 100 log records and compiles a text report. The copy is saved to a local folder and uploaded to Google Drive through a service account (a system account meant for programs). The backup hour is configurable, by default 14:00 and 14:05. A final copy is made when the bot stops, and a manual backup is available from a button in the admin panel.

Polling or webhook in aiogram

By default the bot asks Telegram for updates itself (polling). For a server with a public address there is a webhook mode: Telegram delivers updates to a built-in web server. The mode is switched by a setting.

Results

  • A customer goes from registration to a key without any admin involvement: invite code, a 2-hour trial, purchase, key.
  • The admin does not need to open Marzban: payment approval, gifting time, and searching and deleting users are done in Telegram.
  • The subscription is extended in Marzban right after a payment is approved, and bonus days for longer plans are added automatically.
  • The customer gets a reminder before the subscription ends, and the admin gets notifications about registrations, start, stop and backups.
  • The bot's state is kept in MariaDB, and backups go to Google Drive.

Technologies and why

  • Python 3.10+ and aiogram 3 β€” the bot: dialogs on finite state machines, buttons under messages, a WebApp with instructions.
  • MariaDB (aiomysql) β€” users, admins, log and payments; tables are created at startup, connections come from a pool.
  • Marzban API (aiohttp) β€” creating users, extending subscriptions, fetching keys.
  • APScheduler β€” subscription reminders, backups, cleanup and database maintenance on a schedule.
  • Google Drive API (PyDrive2) β€” uploading backups through a service account.
  • aiohttp β€” the built-in web server for webhook mode.

Status

A test version. The current version, 2.0.0 of 2 February 2026, brought bank-transfer payment with receipt approval by an admin, updated plans and WebApp instructions per platform. After that the code gained a version constant, the hidden /version command and version output to the log at startup.

Not done yet: moving all settings out of the code into .env and adding .env.example, Docker, automated tests and rate limiting; the rate-limit and trial-duration settings are declared in the configuration but not applied yet. Payment remains manual, with no automatic payment-system integration.

Questions about this project

How can I sell VPN subscriptions through a Telegram bot without card acquiring?
The bot shows the bank-transfer details and a link to the bank, and the user sends a screenshot of the receipt. The receipt goes to all admins with Approve and Deny buttons. After approval the Marzban subscription is extended automatically. There is no automatic payment-system integration in the project: a person checks each payment.
How do the invite code and the trial period work?
A new user enters the invite code and gets trial access for 2 hours: the bot creates the user in Marzban itself and sends a VLESS key. A wrong code is rejected. An admin registers without a code and gets 24 hours of access.
How does the bot create a user in the Marzban panel?
Through the Marzban API it creates a user named user_ plus the Telegram ID, with the xtls-rprx-vision flow, a limit of one online device and no traffic limit. A renewal is counted from the current end date. The key for the profile screen is fetched from Marzban.
What can an admin do directly from Telegram?
In the Root Access panel: statistics (total, active, expired, expiring today), search by ID, user lists, deletion with confirmation, gifting hours or days with a comment, a broadcast to everyone with a delivery report, the latest logs and a manual backup.
Where do the bot's backups go?
The bot makes a JSON dump of users, admins and the last 100 log records, plus a text report. The copy is kept in a local folder and uploaded to Google Drive through a service account. A final copy is made when the bot stops, and old copies are cleaned up on a schedule.
How is the bot run, and what does it need?
It runs as an ordinary Python program (3.10 or newer) alongside a MariaDB or MySQL database and a Marzban panel. It creates its tables at startup. The default mode is polling, and webhook mode is available. The repository has no Docker or docker-compose files yet.

Need something similar?

Tell us about the task β€” we'll show how we solved it and estimate the scope.