Crypto payments for Marzban VPN
A small self-hosted service that sells Marzban VPN subscriptions for crypto through SnapEX Pay. Your shop or Telegram bot asks it for a checkout link, the customer pays on the SnapEX Pay page (USDT on TRC20, ERC20 or BEP20, plus other methods SnapEX offers for that amount), and once the invoice is paid the service creates or extends the user in your Marzban panel and returns the subscription link.
Free. Version 1.0.0.
What it does
- No wallet or blockchain node to run.
POST /v1/checkoutcreates a SnapEX Pay invoice for a plan and returnspay_url;GET /v1/orders/{invoice_id}returns the status and the subscription URL, so a bot can poll it.- Signed webhook (HMAC-SHA256, 5-minute timestamp window). The webhook body is never trusted: the invoice is re-read from the SnapEX Pay API and the user is provisioned only when it is paid.
- Idempotent: repeated or concurrent webhook deliveries provision an invoice only once.
- New users get every protocol that has an inbound in your panel; existing users are extended from their current expiry, and traffic plans add a fresh quota on top of their usage.
- Plans are set with one environment variable; orders are kept in SQLite. Test mode moves no real funds.
Install
- Download and unpack the archive, then in its folder run
cp .env.example .env. - In the SnapEX Pay merchant cabinet create an API key with the
invoices.readandinvoices.writescopes and put it intoSNAPEX_API_KEY. - Set
API_TOKEN(for exampleopenssl rand -hex 32),MARZBAN_URLand the Marzban admin credentials (MARZBAN_USERNAMEandMARZBAN_PASSWORD), and your plans inPLANS(id:days:usd:traffic_bytes,0bytes = unlimited). - Start it:
docker compose up -d, then checkcurl http://127.0.0.1:8080/health. Put the service behind an HTTPS reverse proxy. - In the cabinet add a webhook endpoint
https://<your service>/webhooksubscribed at least toinvoice.paid, copy its signing secret intoSNAPEX_WEBHOOK_SECRETand restart the service. - From your shop or bot call
POST /v1/checkoutwithAuthorization: Bearer <API_TOKEN>, sendpay_urlto the customer and pollGET /v1/orders/{invoice_id}for the subscription link. For a test without real funds setSNAPEX_ENVIRONMENT=test.
Settings
| SNAPEX_API_KEY | Merchant API key (smk_…) with invoices.read and invoices.write. |
|---|---|
| SNAPEX_WEBHOOK_SECRET | Signing secret (whsec_…) of your webhook endpoint. |
| SNAPEX_ENVIRONMENT | live or test. Test invoices move no real funds. |
| SNAPEX_FEE_ON | Who pays the SnapEX service fee: merchant or customer. |
| API_TOKEN | Bearer token your bot or shop sends to /v1/checkout and /v1/orders/*. |
| MARZBAN_URL, MARZBAN_USERNAME, MARZBAN_PASSWORD | Panel URL and admin credentials (or a ready MARZBAN_TOKEN; tokens expire, so username and password are better for a long-running service). |
| PLANS | Comma-separated id:days:usd:traffic_bytes, default month:30:5.00:0,quarter:90:12.00:0. |
Questions
Can I use it from a Telegram bot?
Yes. The bot calls POST /v1/checkout, sends pay_url to the user and polls GET /v1/orders/{invoice_id} for the subscription link. There is also a complete Telegram bot for Marzban, 3x-ui and Remnawave.
What happens to an existing user who pays again?
The subscription is extended from the current expiry (or from now if it has already expired); traffic plans add a fresh quota on top of the usage.
Can a webhook provision a user twice?
No. Repeated or concurrent deliveries provision an invoice only once, and the service re-reads the invoice from the SnapEX Pay API before doing anything.
What if the Marzban panel is down when the payment arrives?
The webhook handler returns 502, and SnapEX Pay retries the delivery later.
Why do I need HTTPS?
SnapEX Pay delivers webhooks only to https:// URLs, so put the service behind nginx, Caddy or Traefik.
Other integrations
Ready to accept crypto?
Create a merchant account, get an API key and connect the module. 1% per payment, no monthly fee.