Receipts docs
v1.0.0
Live demo Get help
● Set up · .env, Stripe, Telegram

Configuration
keys and connections.

Start-up settings live in .env; everything else is set in the Admin console. This page lists every variable, then connects Stripe and Telegram step by step.

01Two places, one rule

WhereWhat goes thereA change needs
.envAddress, port, database path, operator claim, proxy trust, the console key; optionally the Stripe and Telegram secretsA restart (sudo systemctl restart receipts)
Admin console (/admin)Fees, payment methods, branding, publishing limits, measurement, delivery, pages; Stripe and Telegram secrets under IntegrationsSave. Stripe and Telegram secrets need a restart.

A secret set in .env wins and locks the field in the console. Otherwise the value saved in the console (encrypted with ADMIN_SECRET_KEY) applies. Nothing has a working default key: you bring every credential.

02Every variable

VariableDefault if emptyPurpose
PUBLIC_URL Requiredhttp://localhost:8787The exact public address, with https://. Used in receipts, Telegram messages, Stripe return links and the provider webhook address. With https:// session cookies are marked Secure.
PORT8787Local port the app listens on. Your proxy forwards to it.
DATABASE_PATHdata/receipts.dbThe SQLite database. The folder is created if missing. The console keeps admin.sqlite in the same folder.
ADMIN_EMAIL(setup code)The only email that can create the operator account on a fresh install. Empty = the one-time setup code from the log.
ADMIN_SECRET_KEY Recommendedgenerated into data/.admin-secret-key32 random bytes (openssl rand -hex 32) that encrypt the secrets saved in the console. Keep it out of git and back it up with data/admin.sqlite.
TRUST_PROXY01 reads the visitor address from X-Forwarded-For for sign-in limits. Only behind your own proxy (Caddy, nginx, Cloudflare).
STRIPE_SECRET_KEY(off)Card payments. Or set it in the console → Integrations. Stripe setup.
STRIPE_WEBHOOK_SECRET(off)Signing secret of your webhook endpoint. Without it /webhooks/stripe answers 503.
TELEGRAM_BOT_TOKEN(off)Your own bot. Or set it in the console → Integrations. Telegram setup.
OTS_CALENDARSpublic calendarsComma-separated OpenTimestamps calendar URLs. Empty uses a.pool.opentimestamps.org, b.pool.opentimestamps.org and a.pool.eternitywall.com.
DEMO_MODE01 = a labelled demo checkout (no money moves), one-click demo logins and labelled strategy bots on live prices. Never in production.
DEMO_PUBLIC0With DEMO_MODE=1: a demo reachable from the internet. The console is read-only for visitors and free-text fields that would be public or sealed forever are locked. Not listed in .env.example.
WORKERS10 turns off background jobs: pricing (every minute), anchoring, Bitcoin confirmation checks (every 15 minutes), access expiry (every 5 minutes), performance billing (hourly). Keep 1.

Source: .env.example, src/config.ts, src/admin/kit.ts.

03Stripe, step by step

Receipts talks to Stripe over its REST API with your own keys. Subscriptions, Provider Pro and verification fees are paid through Stripe Checkout into your Stripe account, and the app records each payment and splits it. Do the whole setup in test mode first (sk_test_…).

  1. Secret keyStripe Dashboard → Developers → API keys → into STRIPE_SECRET_KEY (or Admin → Integrations).
  2. Webhook endpointDevelopers → Webhooks → add https://signals.example.com/webhooks/stripe.
  3. EventsSelect invoice.paid, invoice.payment_failed, customer.subscription.deleted, checkout.session.completed, charge.refunded, charge.dispute.created, charge.dispute.closed.
  4. Signing secretCopy the endpoint's whsec_… into STRIPE_WEBHOOK_SECRET, then restart. The start-up line says Stripe on.
  5. TestSubscribe from a second account with card 4242 4242 4242 4242. After invoice.paid the subscription shows in Account and in Admin → Subscriptions.
  6. Go liveWith the live key and a live endpoint; its signing secret is different.
# testing on your own computer
stripe login
stripe listen --forward-to localhost:8787/webhooks/stripe
# put the printed whsec_... in STRIPE_WEBHOOK_SECRET and restart
  • Cancelling keeps access until the end of the paid period. A live renewal gets a 48-hour grace after the period ends.
  • A provider's price change applies to new checkouts; existing subscribers keep their price.
  • Refunds and lost disputes take the provider's share back out of their balance in the original split; a won dispute puts it back.
  • Coupons work with bank and crypto payments, not with card payments.
Tested with synthetic events

Webhook signatures, refunds and payment recording are covered by automated tests with synthetic events. A real Stripe account was not used.

04Telegram, step by step

  1. Create the botIn Telegram open @BotFather, send /newbot, choose a name and a username ending in bot.
  2. Add the tokenTELEGRAM_BOT_TOKEN=123456789:your-token in .env (or Admin → Integrations), then restart. The start-up line says Telegram on. The bot uses long polling: no webhook or open port needed.
  3. Members link accountsSubscriptions and payments (/account) → Connect Telegram shows a one-time t.me/<bot>?start=<code> link (15 minutes). Pressing Start answers "Linked." One Telegram account per site account.
  4. Providers connect a VIP groupThey add the bot as an administrator with Invite users via link and Ban users, then send /connect in the group (needs Provider Pro when you charge for it).

When a subscription becomes active the bot DMs the subscriber a personal join-request link (valid 7 days) and approves it only from that member's linked account. When access ends it removes them. Admin → Delivery switches DMs and group posts on or off and sets a footer.

Not run against a real bot

The command parser and handlers are covered by tests; a live bot was not used while writing these docs.