Receipts docs
v1.1.0
Get help
● Set up · .env, Stripe, Telegram bot

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

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

VariableDefault if emptyPurpose
PUBLIC_URL Requiredhttp://localhost:8787The 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.
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_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 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 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_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.
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.
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_…).

Your account, your costs

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.

  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.

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.
Check it with your own account

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.

  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 link to your bot with a start code (valid 15 minutes). Pressing Start answers "Linked." One chat 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.

Your own bot

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.