An open-source live tip jar for performers. Cash is gone — the applause isn't.
Musicians, buskers, and street artists put one QR code on stage. Fans scan it, pick an amount, leave a name and a message. With Stripe — the recommended default — tips land directly in the artist's own Stripe account, with no platform, no middleman, and no extra cut. With no account (the default) no live.tips server sits in between at all; signing in moves the artist's Stripe key to a server so tips can reach a second device (see Accounts). Either way live.tips never touches the money. The artist's tablet or phone shows every tip live, with a goal progress bar, the latest message, and confetti.
Artists can optionally also accept Revolut and MobilePay Box tips. Those two
have no API to confirm a payment, so they route through a tiny open-source relay
(firebase/, tip.live.tips) that forwards tip notifications to the
artist's device and keeps no tip history and never touches money. Tips from these
methods are shown as unverified because live.tips cannot confirm they were actually
paid. A Stripe-only setup with no account still talks to no live.tips server at all —
see Connected mode below.
An account is optional too. The default is still a device-local profile, and it still keeps everything on the device. Signing in buys one thing: the same bands, keys, settings and history on a second device, and a live session several devices can watch at once — see Accounts below.
- The artist creates a restricted API key in their own Stripe dashboard
(2 minutes, guided) and pastes it into
the app. With no account the key is stored in the device keychain and only ever
talks to
api.stripe.com; once the artist signs in it moves to the server, encrypted (see Accounts). - The app creates a Product + pay-what-you-want Price + Payment Link in the artist's account. The Payment Link URL becomes their QR code — print it, tape it to the guitar case, put it on the merch table.
- During a live session, tips show in real time against tonight's goal, which can be
edited mid-set. With no account, the app polls Stripe's
/v1/eventsendpoint directly from the device — a tablet on a stage has no public HTTPS endpoint to receive a webhook, so this polls a documented endpoint instead. Once the artist signs in, the key lives on the server, so Stripe delivers tips to a server webhook instead and the app stops polling. See architecture notes. - Stage lock blocks the screen from casual tampering while the device sits on stage; unlocking uses Face ID / Touch ID / device passcode, with an in-app PIN as fallback.
- Refunds, payouts, and disputes stay where they belong: in the artist's own Stripe dashboard.
These are tips for a performance, not charitable donations — Stripe treats those as two different businesses, and only one of them is you. Artists should describe their Stripe account as live performance, not fundraising: see tips, not donations.
Stripe is the default and needs no server. Artists who also want to accept Revolut
or MobilePay Box tips can opt in during onboarding. Because those services offer no
way to confirm a payment, this mode uses a minimal relay (firebase/:
Cloud Functions + Firestore, with the fan page at tip.live.tips):
- The QR code points to a small hosted page (
tip.live.tips/t/<id>) that offers every method the artist enabled. Card / Apple Pay / Google Pay still go straight to the artist's Stripe link; Revolut and MobilePay open a short form (amount, name, message). - Submitting the form queues the tip for the artist's device and redirects the fan to the Revolut/MobilePay deep link. The artist's app listens for it and deletes it as it shows it — that delete is the delivery receipt. The relay keeps no tip history, no analytics, and the artist's profile (name, message, payment handles, all plain text) self-deletes after 90 days of inactivity.
- A device that is away — phone locked, artist checking their MobilePay app, walked out of signal — would otherwise miss the tip entirely, since the fan has already paid by then. So an undelivered tip waits in its jar for up to one hour, is handed over the moment the artist's screen comes back, and is deleted unseen if it never does. That is the relay's only storage of fan-written text, and the only exception to "no tip history".
- Revolut/MobilePay tips are shown as unverified: they appear the moment a fan submits the form, whether or not the payment completes. The artist reconciles against their own Revolut/MobilePay app.
- If the relay is unreachable, the app falls back to Stripe-only automatically. All tip history stays on the device regardless of mode — which is one more reason Stripe (the only method with a real payment record) is recommended.
The app boots into a local profile — no sign-in, no uid, nothing off the device. An artist who wants a second device can sign in with Apple, Google, or as a guest (an anonymous account: it syncs, but nothing recovers it if the device is lost). An account owns the artist's bands — the per-gig profiles, each with its own Stripe key, tip jar and QR code.
- What syncs: bands, settings, and tip + session history. The Stripe restricted
key moves to the server on sign-in — envelope-encrypted (a per-secret AES-256 key
wrapped by Cloud KMS) in a
stripeConnections/*doc readable by no principal at all, the owner included — and Stripe delivers tips to a per-account webhook from then on. The relay jar secret is kept under the account (hashed) so any device can serve the tip page. The key never rides back down to a device. - One live session per account, enforced by a single Firestore doc rather than by policy. Every device joins from a "Live session running in " banner and follows the same tip feed — fed by the server webhook (or, for a no-account jar, by the polling device).
- Devices are listed in Settings → Security: revoke one, or sign out everywhere else. A new device is added by scanning a QR — and the signed-in device has to confirm the request before anything is issued.
- Push notifications are available to signed-in accounts, per device and off until turned on: a tip or song request that lands while no set is running is delivered via Firebase Cloud Messaging. Guests and no-account devices get none.
- The trade-off, stated plainly: a signed-in account's tips (names and messages included) are written by the server into Firestore under the artist's own uid, where no other account can read them, and kept as long as the band. The local profile stores them nowhere but the device. Syncing is opt-in for exactly that reason.
See architecture notes for how any of this holds together.
| Path | What it is |
|---|---|
app/ |
Flutter app (iPhone, iPad, Android phone/tablet, macOS, web; Windows/Linux scaffolded) |
firebase/ |
Cloud Functions + Firestore relay for optional Revolut/MobilePay tips, and the fan page (tip.live.tips) |
docs/ |
Onboarding guide, architecture notes |
docs/stripe-app-plan.md |
Plan for the phase-2 Stripe App Marketplace companion |
stripe-app/ |
The future Stripe Apps (marketplace) companion — not started |
cd app
flutter pub get
flutter run # pick a device; try "Try the demo" — no Stripe account needed
flutter test # unit + widget tests
flutter drive --driver=test_driver/integration_test.dart \
--target=integration_test/demo_flow_test.dart # e2e demo flow with screenshotsThe demo mode on the welcome screen simulates a live session with generated tips — useful for UI work and for trying the app before connecting Stripe.
- Bring your own key. With no account the restricted key never leaves the device
(keychain/keystore) and is only sent to
api.stripe.comover TLS. When the artist signs in it moves to the server, envelope-encrypted under Cloud KMS and readable by no one — not another account, not the owner — and Stripe reaches it only through the server, never a device. - Least privilege. The key needs only: Checkout Sessions Read, Events Read, Charges Read (to see in-person taps), Payment Links Write, Products Write, Prices Write. It cannot touch balances, payouts, refunds, or customer data beyond the payments behind its own tips.
- Live secret keys are refused. The app rejects
sk_live_…keys outright and explains how to create a restricted one instead. - Test mode is loud. Sandbox/test keys get a permanent orange banner.
- Stage lock is honest about its limits: it stops casual taps, not theft. For a sealed kiosk, combine it with iOS Guided Access or Android app pinning (see architecture notes).
MVP under active development in a private repo; will be open-sourced once verified end-to-end. Roadmap highlights:
- Stripe App Marketplace companion for one-click onboarding (plan)
- Create-key deep link with pre-selected permissions
- Windows build
- Optional sounds / external display mode for the live screen