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 the Telegram bot step by step.
01Two places, one rule
| Where | What goes there | A change needs |
|---|---|---|
.env | Address, port, database path, operator claim, proxy trust, the console key; optionally the Stripe and Telegram bot secrets | A restart (sudo systemctl restart receipts) |
Admin console (/admin) | Fees, payment methods, branding, publishing limits, measurement, delivery, pages; Stripe and Telegram bot secrets under Integrations | Save. Stripe and Telegram bot 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
| Variable | Default if empty | Purpose |
|---|---|---|
PUBLIC_URL Required | http://localhost:8787 | The exact public address, with https://. Used in receipts, Telegram bot messages, Stripe return links and the provider webhook address. With https:// session cookies are marked Secure. |
PORT | 8787 | Local port the app listens on. Your proxy forwards to it. |
DATABASE_PATH | data/receipts.db | The SQLite database. The folder is created if missing. The console keeps admin.sqlite in the same folder. |
ADMIN_SETUP_TOKEN | (setup code) | The one-time secret (24 characters or more, openssl rand -hex 24) that claims the operator account at /signup and creates the console owner at /admin on a fresh install. Empty = a one-time setup code is printed in the log at start. Remove it once both accounts exist. Shorter than 24 characters stops the app at start. |
ADMIN_EMAIL | (any) | Optional. When set, the operator account must also use this email. It never replaces the setup secret. |
ADMIN_SECRET_KEY Recommended | generated into data/.admin-secret-key | 32 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_PROXY | 0 | 1 reads the visitor address from the right-most X-Forwarded-For entry for the sign-in limits of the site and of the Admin console. Only behind one proxy of your own (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 bot setup. |
OTS_CALENDARS | public calendars | Comma-separated OpenTimestamps calendar URLs. Empty uses a.pool.opentimestamps.org, b.pool.opentimestamps.org and a.pool.eternitywall.com. |
DEMO_MODE | 0 | 1 = a labelled demo checkout (no money moves), one-click demo logins and labelled strategy bots on live prices. Never in production. |
DEMO_PUBLIC | 0 | With 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. |
FRAME_ANCESTORS | (none) | Comma-separated https:// origins allowed to show the public pages in a frame, for example a preview page that embeds your site. Empty: no site may frame them (X-Frame-Options: DENY). The Admin console never allows frames. |
WORKERS | 1 | 0 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_…).
Stripe is your own account: you accept Stripe's terms, and Stripe deducts its processing fees from each payment. None of that is included in the item price. The app grants access only after a Stripe event signed with your webhook secret arrives on the server; the success page a member returns to grants nothing by itself.
- Secret keyStripe Dashboard → Developers → API keys → into
STRIPE_SECRET_KEY(or Admin → Integrations). - Webhook endpointDevelopers → Webhooks → add
https://signals.example.com/webhooks/stripe. - EventsSelect
invoice.paid,invoice.payment_failed,customer.subscription.deleted,checkout.session.completed,charge.refunded,charge.dispute.created,charge.dispute.closed. - Signing secretCopy the endpoint's
whsec_…intoSTRIPE_WEBHOOK_SECRET, then restart. The start-up line saysStripe on. - TestSubscribe from a second account with card
4242 4242 4242 4242. Afterinvoice.paidthe subscription shows in Account and in Admin → Subscriptions. - Go liveWith the live key and a live endpoint; its signing secret is different.
The Stripe CLI runs the same way on macOS, Linux and Windows (Windows Terminal, PowerShell or WSL); install it from docs.stripe.com/stripe-cli.
# 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.
The automated tests cover webhook signatures (valid, wrong secret, expired), refunds and payment recording with signed test events. Your Stripe account, keys and webhook endpoint are yours to configure: make one payment in Stripe test mode before you switch to live keys.
04Telegram bot, step by step
The bot is your own, made with @BotFather in Telegram. Telegram makes no charge for its Bot API at the time of release; its terms apply. Nothing about it is included in the item price.
- Create the botIn Telegram open @BotFather, send
/newbot, choose a name and a username ending inbot. - Add the token
TELEGRAM_BOT_TOKEN=123456789:your-tokenin.env(or Admin → Integrations), then restart. The start-up line saysTelegram on. The bot uses long polling: no webhook or open port needed. - Members link accountsSubscriptions and payments (
/account) → Connect Telegram shows a one-time link to your bot with a start code (valid 15 minutes). Pressing Start answers "Linked." One chat account per site account. - Providers connect a VIP groupThey add the bot as an administrator with Invite users via link and Ban users, then send
/connectin 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.
The automated tests cover the bot's signal commands (/long, /short and their parser) and the access rules that decide when a subscription starts and ends a member's access. The bot, its token and its groups are yours: after adding the token, link your own account and send yourself one test signal before you announce it.