
The problem
Accepting small payments — tips, donations, “buy me a coffee” — sounds trivial until you wire it up. Stripe integration, a secure checkout, and reliably confirming that a payment actually happened is real work.
So most creators rent a hosted service that takes a cut and can't be customised — when all they wanted was a button on their own site.
One script tag. Real payments.
Framework-agnostic, isolated, and safe by construction — built to drop onto any page.
One-tag embed
Vanilla JS in a Shadow DOM — no framework, no build step. Inline, floating, or modal placements, configured with data attributes, plus a small JS API.
Hosted Stripe Checkout
Sessions are created server-side and the browser is redirected to Stripe’s own page, so raw card data never touches the backend and PCI scope stays minimal.
Webhook as source of truth
Tips are recorded only from a signature-verified checkout.session.completed event — idempotent on the session id, and handling delayed async payments too.
Tested & observable
pytest + a jsdom widget suite on green CI, rate limiting on the public endpoint, and Sentry with release tracking and strict PII scrubbing.
How I built it
Payments run through Stripe's hosted Checkout, so card details never touch the server — no PCI burden and no card UI to build. The visitor is redirected to Stripe, pays, and returns.
The key decision: a tip is only confirmed by a Stripe webhook — the webhook is the source of truth, never the browser redirect (which can be dropped or faked). The Django backend verifies and records each event idempotently. Deployed on Railway on a custom domain with TLS, with tests and CI.
Drop it on a page and tip yourself a coffee
Open the live demo to see every placement and the JS API in action — all in Stripe test mode, so 4242 4242 4242 4242 is all you need.
Need Stripe or payments wired up properly?
Payments look simple and bite hard — webhooks, idempotency, edge cases. I build embeddable, correctly-integrated payment flows end to end.