Business

Invoices & payments

All purchases in the Telegram storefront are proof-of-payment based: a user picks a plan, uploads a payment screenshot, and an admin approves it in Payments. Approved orders generate a permanent record in Invoices. This page covers both queues end to end.
  1. 1
    Configure payment instructions and a QR code in Bot → Content (paymentInstructions, paymentQrUrl) so buyers know where/how to pay outside the bot — bank transfer, crypto address, etc.
  2. 2
    A user picks a plan in the bot's Buy flow and the bot shows the configured payment instructions.
  3. 3
    The user pays out-of-band, then uploads a screenshot/proof of payment as a photo/document in the chat.
  4. 4
    The bot creates an order that lands in Payments → Pending with status: "pending".
  5. 5
    An admin reviews the proof image against the plan and amount, then approves, rejects, or holds the order.
  6. 6
    Approving auto-provisions the VPN account(s) for the plan and DMs the config back to the user via the bot.

No automated payment gateway

There is no card/gateway integration in this flow — every purchase is manually verified by an admin against a screenshot. This keeps the storefront usable with local bank transfers and crypto wallets that have no programmatic webhook.

§3 — the moderation surface

Payments queue#

Route: /_authed/payments. This is the working queue admins act on; Invoices (below) is the read-only historical ledger generated once a payment is approved.

Payment data model

FieldMeaning
idOrder id.
telegramId / telegramNameThe buyer.
planName / amountCentsWhat was bought and for how much, in cents.
proofUrlThe uploaded payment screenshot/proof image.
statuspending | on_hold | approved | rejected.
noteOptional admin note, e.g. reason for hold/reject.
createdAtOrder timestamp.

UI and moderation actions

  • Tabs: Pending / On hold / Approved / Rejected / All.
  • Each pending/on-hold card shows the proof image, buyer name + Telegram id, plan + amount, timestamp, and any admin note.
  • Approve — provisions the account(s) immediately and notifies the buyer.
  • Reject (with a reason — preset reasons: unclear screenshot, amount mismatch, transaction not found, duplicate payment).
  • Hold (with a reason — preset reasons: verifying with bank, need TX ID, awaiting crypto confirmations).
  • Contact — send a free-form Telegram message to the buyer without changing status.
  • On approved/rejected orders: Message user is still available; approved orders additionally get Revoke — a destructive rollback that removes the provisioned account(s) and reopens the order as pending, with an optional reason shown to the user.
  • Every reject/hold/message/revoke action notifies the buyer inside the Telegram bot automatically.

Endpoints (api.payments)

text
list(status?)                              filter by tab: pending | on_hold | approved | rejected | all
decide(id, "approved"|"rejected"|"on_hold", reason?)   the core moderation action
revoke(id, reason?, removeAccount: true)   undo an approval; reopens as pending, deletes account
message(id, text)                          send a free-form Telegram message, no status change

Taking a payment, step by step

  1. 1
    Configure payment instructions/QR in Bot → Content so users know where and how to pay.
  2. 2
    User buys a plan, uploads a screenshot as proof, order appears in Payments → Pending.
  3. 3
    Admin reviews the proof photo and amount.
  4. 4
    Click Approve to provision the account and notify the user, or Reject/Hold with a reason.
  5. 5
    If a wrong approval happened, use Revoke to roll it back — this deletes the account and returns the order to Pending.

§2 — the historical ledger

Invoices#

Route: /_authed/invoices. A read-only ledger of generated business invoices, one per approved payment/order, with export and Telegram re-send.

Invoice fields

id / number
Row id and the invoice number (e.g. shown in mono font).
issuedAt
ISO issue date.
username / telegramId
The buyer.
planId / planName
Links back to the plan (/plans?open=<planId> for quick cross-reference).
method
Payment method label (bank/crypto/points/topup — shown uppercase).
amountCents
Invoice amount in cents.

Filtering and export

  • Client-side period filter: Monthly / Yearly / All-time, computed via periodRange().
  • Free-text search across invoice number, username, Telegram id, and plan name.
  • send(id, "telegram") — re-sends the invoice PDF to the buyer's Telegram (the bot delivers it).
  • pdfUrl(id) — direct download link for a single invoice PDF.
  • exportUrl("xlsx" | "pdf", { start, end }) — bulk export of all invoices in the selected period.
  • txns() — the full wallet transaction ledger tagged with telegram_id, used elsewhere (e.g. Users page) to reconstruct financial history per user.

Send requires a linked Telegram id

Clicking Send on an invoice requires telegramId to be present — invoices without a bot user attached cannot be re-delivered automatically.
  • There is no live payment-gateway integration; "payment methods" are whatever instructions/QR you configure in Bot → Content — reconciliation is manual, driven by comparing the uploaded proof to your own bank/crypto records.
  • Every approved payment eventually shows up in both the Payments → Approved tab and as a row in Invoices — use the invoice's plan link to jump back to the plan, or the Users page's transaction history (api.invoices.txns()) to see a buyer's full financial trail across all wallet tracks.
  • For crypto payments specifically, ongoing balance/confirmation tracking after the initial proof lives in Wallet → Wallet Tracks (see the Wallet & tracks page) rather than in Payments itself.
  • Use Invoices → Export for period-end reconciliation against your own accounting; XLSX and combined PDF exports are both scoped to a start/end date range.
  • Rejecting or holding an order does not delete it — it stays visible under its tab and can still be approved later.
  • Revoke is destructive: it deletes the provisioned account(s), not just the order status; only use it to correct a genuine mistake.
  • Invoices are generated from approved payments — there is no invoice for a rejected or still-pending order.
  • The invoice "method" field is a label only; it does not enforce or validate against a real payment processor.