DEV Community

Build a Telegram Paid Access System with Crypto Payments

Core Idea

A Telegram paid access system lets a community owner charge users for access to a private Telegram channel, group, course, file drop, or VIP community. The system should handle the full lifecycle:

  • User starts the bot
  • User selects a plan
  • Backend creates a crypto invoice
  • User pays the invoice
  • OxaPay sends a webhook update
  • Backend validates the webhook
  • Subscription becomes active
  • Telegram access is granted
  • Renewal reminders are sent
  • Expired members are removed or restricted
  • Admins can search payment and membership status

The developer is not selling: "I can create a Telegram bot that sends a payment link." The developer is selling: "I can build a paid access system that sells membership, confirms crypto payments, grants Telegram access, manages expiry, reduces manual admin work, and gives you a searchable payment trail." That difference matters. A payment link is a feature. Access management is a product.

Why This Is a Real Developer Business

Most paid Telegram communities are run by creators, educators, operators, marketers, traders, coaches, or small digital businesses. They often have an audience, but they do not have a payment engineering team. Their problem is not only "how do I accept payment?" Their real problems are operational:

  • How do I know who paid?
  • How do I prevent unpaid users from joining?
  • How do I remove expired members?
  • How do I stop invite links from being shared?
  • How do I handle failed, expired, or underpaid payments?
  • How do I remind users before access expires?
  • How do I give admins a clean view of who is active?
  • How do I avoid manually checking wallets and screenshots?
  • How do I sell weekly, monthly, quarterly, and lifetime plans?
  • How do I run the business when payment volume grows?

That is exactly where a developer can productize the solution.

Telegram gives developers a mature Bot API. The Bot API lets bots create invite links, receive chat member updates, approve join requests, and manage members when the bot has the required administrator permissions. For example, Telegram documents createChatInviteLink, including parameters such as expire_date and member_limit, and states that the bot must be an administrator with the appropriate rights. Telegram also documents revokeChatInviteLink, approveChatJoinRequest, and chat member update objects. See the official Telegram Bot API reference.

OxaPay gives developers the payment side. Its Generate Invoice endpoint creates a payment URL and supports fields such as amount, currency, lifetime, callback_url, return_url, order_id, description, and sandbox mode. Its Webhook documentation explains how to receive payment callbacks and validate HMAC signatures. Its Payment Information and Payment History endpoints help you build status pages, admin tools, and reconciliation screens. OxaPay also documents no-code and low-code Telegram automation examples through n8n Telegram Bot and Make Telegram Bot workflows. Those are useful signals: this workflow is not theoretical.

The business is not "accept crypto in Telegram." The business is "run paid Telegram access without manual payment operations."

Who Would Pay for This?

The best buyers are communities where access itself is the product. Good targets include:

Buyer What they sell Why they need this
Trading communities VIP channel, signals, market commentary They need fast activation, renewal tracking, and expired member removal
Course creators Private class group, study cohort, premium lessons They need paid onboarding and access control
Newsletter operators Private Telegram delivery channel They need subscription plans and renewals
Coaches and consultants Paid group calls, private Q&A, weekly reports They need simple checkout and member visibility
Software sellers License keys, bot access, private support group They need payment-to-access automation
Digital product sellers Templates, files, PDFs, source code, research drops They need automatic delivery after payment
Creator communities Premium chat, private updates, early access They need recurring community monetization
Agencies Paid client communities or customer support channels They need a reusable launch package for multiple clients

The key qualification is this: Does the merchant lose time or revenue because payment and access are handled manually? If yes, a paid access system can be sold as a practical business tool.

What You Can Sell

A developer can package this product in several ways:

Offer What the merchant gets Revenue model
Telegram paid access setup Bot, invoice flow, webhook handling, and invite link automation One-time setup fee
Managed community billing Ongoing payment monitoring, expiry checks, renewal reminders, and admin support Monthly retainer
Hosted paid access SaaS A reusable dashboard for multiple Telegram communities Monthly subscription
White-label agency kit A reusable bot/dashboard package agencies can sell to clients License + support fee
Custom automation package Payment connected to CRM, Sheets, email, Notion, Discord, or internal tools Setup + maintenance
Premium support dashboard Payment search, user search, membership timeline, issue categories Per-community or per-admin pricing

The simplest path is not to build a full SaaS on day one. The practical path is: build one custom implementation for one community, identify repeated requirements, turn the repeated parts into reusable modules, sell the same system to similar communities, add a dashboard only when operations become repetitive enough. A common mistake is building a generic bot first. A better approach is selling a productized service first.

The Technical Architecture

A production-grade Telegram paid access system has five layers:

Telegram user
  |
  | /start, select plan, request access
  v
Telegram bot
  |
  | creates checkout session
  v
Paid access backend
  |
  | creates OxaPay invoice with callback_url + order_id
  v
OxaPay invoice
  |
  | user pays crypto invoice
  v
OxaPay webhook
  |
  | validate HMAC, update payment state
  v
Membership engine
  |
  | activate subscription, create invite link, send access
  v
Telegram private group / channel

The critical design principle is that payment state and membership state must be separate. A payment can be created, paid, failed, expired, or disputed internally. A membership can be pending, active, grace, expired, revoked, banned, or manually extended. Do not collapse those into one boolean field like is_paid. That shortcut breaks quickly.

The OxaPay Primitives Used

The minimal OxaPay setup uses hosted invoices and webhooks:

OxaPay primitive Role in this product
Generate Invoice Create a payment link for the selected Telegram access plan
callback_url Tell OxaPay where to send payment status updates
order_id Store your internal subscription or checkout ID in the payment request
lifetime Control how long the payment link should remain valid
Webhook Receive payment updates and trigger membership activation
HMAC validation Verify that webhook callbacks are authentic
Payment Information Fetch a specific payment by track_id for support and recovery
Payment History Build admin dashboards, payment search, and reporting
n8n Telegram Bot integration Useful for prototypes or low-code workflows
Make Telegram Bot integration Useful for non-code automation and merchant demos

For the first version, hosted invoices are usually enough. White-label payment is useful later if you want to keep the full payment experience inside your own interface or mini app. OxaPay's Generate White Label endpoint returns payment details such as address, currency, amount, network, and expiration information, allowing you to manage the payment process inside your own UI. Static addresses are usually not the first choice for paid access subscriptions, because plan-based access normally needs a clear invoice per membership period. But static addresses can be useful for account balance top-ups or wallet-like community credits.

Telegram Access Control Model

There are two practical access models.

Model 1: One-time invite links

After payment is confirmed, your backend creates a Telegram invite link with:

  • short expiration time
  • member_limit = 1
  • clear internal link name mapping to the payment ID and Telegram user ID

Then the bot sends the invite link to the user. This is simple and works well for many private groups and channels.

Risks:

  • the user can forward the link before joining
  • you need to detect whether the expected user actually joined
  • invite link abuse must be monitored
  • expired links need cleanup

Telegram's createChatInviteLink supports expiration and member limits. The bot must be an administrator with the needed permissions. See Telegram Bot API: createChatInviteLink.

Model 2: Join request approval

Instead of sending a simple invite link, the user requests access and the bot approves the join request only if the subscription is active. This is stricter. The bot can receive chat_join_request updates, and Telegram documents methods such as approveChatJoinRequest and declineChatJoinRequest. This requires the bot to have the correct admin rights.

This model is better for higher-value communities because you can match:

  • Telegram user ID
  • subscription ID
  • payment ID
  • requested channel or group
  • plan validity

A developer product can support both. Start with one-time invite links for the MVP. Add join request approval for serious merchants.

Suggested Database Schema

Here is a simple relational schema for the MVP:

CREATE TABLE merchants (
    id UUID PRIMARY KEY,
    name TEXT NOT NULL,
    telegram_chat_id TEXT NOT NULL,
    oxapay_merchant_api_key_encrypted TEXT NOT NULL,
    oxapay_webhook_secret_encrypted TEXT,
    created_at TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE plans (
    id UUID PRIMARY KEY,
    merchant_id UUID REFERENCES merchants(id),
    name TEXT NOT NULL,
    price_amount NUMERIC(18, 2) NOT NULL,
    price_currency TEXT NOT NULL DEFAULT 'USD',
    duration_days INTEGER NOT NULL,
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE telegram_users (
    id UUID PRIMARY KEY,
    telegram_user_id BIGINT NOT NULL,
    username TEXT,
    first_name TEXT,
    last_name TEXT,
    created_at TIMESTAMPTZ DEFAULT now(),
    UNIQUE(telegram_user_id)
);

CREATE TABLE subscriptions (
    id UUID PRIMARY KEY,
    merchant_id UUID REFERENCES merchants(id),
    plan_id UUID REFERENCES plans(id),
    telegram_user_id UUID REFERENCES telegram_users(id),
    status TEXT NOT NULL, -- pending, active, grace, expired, revoked
    starts_at TIMESTAMPTZ,
    expires_at TIMESTAMPTZ,
    last_payment_id UUID,
    created_at TIMESTAMPTZ DEFAULT now(),
    updated_at TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE payments (
    id UUID PRIMARY KEY,
    merchant_id UUID REFERENCES merchants(id),
    subscription_id UUID REFERENCES subscriptions(id),
    oxapay_track_id TEXT UNIQUE,
    order_id TEXT UNIQUE NOT NULL,
    amount NUMERIC(18, 2) NOT NULL,
    currency TEXT NOT NULL,
    status TEXT NOT NULL, -- created, paying, paid, expired, failed, ignored
    invoice_url TEXT,
    raw_provider_payload JSONB,
    created_at TIMESTAMPTZ DEFAULT now(),
    paid_at TIMESTAMPTZ,
    updated_at TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE invite_links (
    id UUID PRIMARY KEY,
    subscription_id UUID REFERENCES subscriptions(id),
    payment_id UUID REFERENCES payments(id),
    telegram_invite_link TEXT NOT NULL,
    expected_telegram_user_id BIGINT NOT NULL,
    status TEXT NOT NULL, -- created, sent, used, expired, revoked
    expires_at TIMESTAMPTZ NOT NULL,
    created_at TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE audit_events (
    id UUID PRIMARY KEY,
    merchant_id UUID REFERENCES merchants(id),
    entity_type TEXT NOT NULL,
    entity_id UUID NOT NULL,
    event_type TEXT NOT NULL,
    payload JSONB,
    created_at TIMESTAMPTZ DEFAULT now()
);

This schema is intentionally operational. It gives you enough structure to answer support questions later: Which plan did the user buy? Which invoice did they pay? Which Telegram account received the invite? When does access expire? Was the invite used? Was the user removed manually or by the expiry job? What webhook payload changed the state? Those questions are the difference between a demo bot and a paid product.

Payment and Membership State Machine

Use explicit state transitions:

Checkout created -> invoice created -> waiting for payment -> payment callback received -> payment verified -> subscription activated -> invite link generated -> invite sent -> user joined -> renewal reminder sent -> grace period -> expired -> access revoked

Comments

No comments yet. Start the discussion.