Connect Stripe
Connect your own Stripe with a restricted key, and what your AI sets in payments.json.
Connect Stripe
Connect Stripe once per environment with a restricted key. Gemmein then makes your products, prices and Payment Links in your Stripe account and registers the webhook that tells it who paid.
- Open Stripe's API keys page: Developers (bottom left) → API keys, or directly: test mode for Development, live mode for Live.
- Choose Create restricted key. When Stripe asks how you will use the key, choose Powering an integration you built, then Choose your own below the templates. Don't pick a template: each one turns on 15 to 42 permissions, and Gemmein refuses the key. The third-party option does the same, ticking many permissions for you.
- Turn on only these rows: the first five always, and the last two if your app sells
plans. The filter box at the top of the key editor finds a row by name.
Accounts is under Connect, not Accounts v2. It is Read only, so Gemmein knows which Stripe account the key belongs to. Customer portal and Subscriptions let your members manage their plan in Stripe's portal, and let Gemmein end a deleted member's subscription. An app that sells only products or credit packs needs the five. Then choose Create.
Section Row Set to Core Products Write Billing Prices Write Payment Links Payment Links Write Webhook Endpoints Webhook Endpoints Write Connect Accounts Read Billing Customer portal Write, if you sell plans Billing Subscriptions Write, if you sell plans - Optional, on Development's key only: two more rows let your AI test payments with the
test kit. Charges and Refunds (under Core) set to Write refunds its own test payments, and
Checkout Sessions (under Checkout) set to Read checks a test payment while Stripe's webhook
is on its way. Gemmein accepts these rows on a sandbox (
rk_test_) key only; a live key that holds them is refused. - Already made a key with more? Edit it in Stripe and choose None on every section heading: each heading sets every row under it. Then turn on only the rows above.
- Paste the key on that environment's Payments page. It starts
rk_test_in Development andrk_live_in Live. Don't share your keys anywhere else: paste the restricted key only there. - Give each paid plan a price (an amount, a currency, and how often it bills: day, week,
month or year), and each product or credit pack a one-time price: your AI writes them in
gemmein/payments.jsonandnpx gemmein syncsets them in Development, or you set them on the Payments page. Gemmein makes the Payment Link.
What it is. A Stripe restricted key, one per environment: test mode for Development, live mode for Live. It carries 5 permissions, plus 2 if you sell plans: Write on Products, Prices, Payment Links and Webhook Endpoints, and Read on Accounts; and, when your app sells plans through Stripe, Write on Customer portal and Subscriptions. None of them can pay out, refund, or read cards.
Does. Checks the permissions your app needs (five, plus two if it sells plans)
with calls that change nothing, and names each one missing. Gemmein refuses a key that can
do more than these seven: it tries
harmless reads of your money and customer data (balance, charges and refunds, customers,
payouts, payment intents, payment methods, invoices, checkout sessions,
transfers, coupons, disputes and more), and a key that can make any of them is refused.
The Payments page then shows what the key can read and the rows to set; the API
answers stripe_key_too_broad with what it can read in
readable. A Stripe account belongs to one Gemmein
account: another Gemmein account connecting it is refused, while your own apps may share
it. Registers one webhook endpoint in your
Stripe with the eleven events Gemmein needs, plus the event of each custom rule you add.
Checks the webhook every 6 hours and the key every day: a key that was revoked, rolled or given more
permissions stops being used, and a webhook that was deleted, disabled, moved or stripped of
events is flagged on Overview and by email. A key problem shows Connect a new key, and a
webhook problem shows Repair webhook. Replace key, on the key row, swaps in a new
key from the same Stripe account and keeps the webhook and your Payment Links. Your Payment Links keep
selling throughout. Creates a Stripe product, price and Payment Link for each plan, product
and credit pack you price. A new price makes a new Stripe price and Payment Link and
turns the old ones off, because a Stripe price never changes. A rename renames the Stripe
product, and removing one archives it in Stripe. In Live, the live-mode key creates
Live's own products, prices and Payment Links from the prices going live copied. Sets up
the customer portal from your plans, where members switch plan, cancel at the end of the
period and update their card.
Does not. Pay out, refund, or read cards, customers, charges or payments: the
key carries no permission for any of them, and none to move money out of the account. It
can change subscriptions (Subscriptions: Write); Gemmein uses that only to end a deleted
member's subscription, and members switch or cancel their own in the portal. Show the key again: once pasted, only its last
four characters are shown. Accept a Stripe secret key (sk_…) or publishable
key (pk_…), a live-mode key in Development or a test-mode key in Live. Take a
Payment Link or a webhook signing secret made in Stripe: selling through Stripe needs the
key, and Gemmein hands out only the Payment Links it made.
Needs something else when. You sell through a provider other than Stripe: use a relay.
Example. A restricted key made in Stripe test mode with the five permissions
and the two plan rows (the app sells a plan), pasted on Development's Payments page; "pro" priced at 15 GBP a month in
gemmein/payments.json and set by npx gemmein sync.
g.subscriptions.checkout("pro") opens the Payment Link Gemmein made in your
Stripe.
Changing a price, or renaming something Gemmein made in Stripe, needs the key connected. Disconnecting forgets the key: the webhook, its signing secret and every Payment Link keep working, and only making or changing things in Stripe stops. If two people save at once, the later save is refused with a reload message rather than overwriting the first.
Turning payments off stops new sales: nothing new can be bought and Gemmein turns off the Payment Links it made. Existing customers carry on as before. A renewal keeps its access, and a cancellation, a full refund of a purchase, or a renewal Stripe stops retrying takes it away. The webhook stays; a new sale that still arrives is kept on the Errors page for you to refund or grant, and is never granted on its own. Removing the webhook is a separate action, Disconnect and delete, which waits until no Gemmein link is on, no subscription can still charge, and 72 hours have passed since the last link went off. A key from a different Stripe account is refused. In Development, Start over lets you connect another Stripe account once nothing can still pay.
A payment names what was bought by the Payment Link it was made on. A payment on a link Gemmein did not make with this environment's key is kept on the Errors page for you to refund or grant, and is never granted on its own.
Protect your accounts
- Use a different email address for Stripe and for Gemmein. If one inbox is compromised, the other account stays safe.
- Make them addresses only you use, not your public name or contact address: for example, a new address kept only for this. Attackers target the addresses they can find on your site, on LinkedIn or in git commits.
- Turn on two-step sign-in for both inboxes and for Stripe. You sign in to Gemmein with a code sent to your email, so your inbox's security is your Gemmein account's security.
- Never share your keys or sign-in codes, including in screenshots. Gemmein asks for a key only in the Connect Stripe field on the Payments page.
Going live
Going live copies your plans and products into Live with their names, prices, credit
allowances and packs, and what they unlock. Development's Stripe products, Payment Links and
signing secret stay in Development. Connect Stripe in Live with a live-mode key and Gemmein
creates them in Stripe live mode. Until then customers can't pay in Live, and Live's
checkout answers payments_not_configured; you can still go live first.
Once live, Promote on the Go live page copies what Development has into Live:
- Plans, products and credit packs Live doesn't have are copied whole.
- Plan and pack definitions (credits, rollover, the welcome allowance, a pack's credits) are brought to Development's values.
- A different price is shown and never applied. Change Live's price on Live's Payments page.
- An AI tool or collection unlocked by a plan Live doesn't have is re-linked to Live's plan of the same name. If no name matches, the promote stops before anything moves and says which one.
- An AI provider key is copied only when you tick Use the same key as Development.
Each priced plan and product has a Tax category, such as Online service, Software subscription, Ebook or Audio. Gemmein sets the matching tax code on the product it makes in Stripe, which Stripe Tax and Managed Payments read. A product starts on Online service and a plan on Software subscription.
Set by your AI
In Development your AI sets how each plan and product is sold, in one file the project
keeps: gemmein/payments.json. Per product: its price, the file inside the
project (or the link) a buyer gets, the credits it adds and its tax category. Per plan: its
price and how often it bills, the credits each period grants and what rolls over, and its
tax category.
{ "plans": [
{ "name": "Free", "default": true },
{ "name": "Pro", "paid": true,
"price": { "amountMinor": 900, "currency": "gbp", "interval": "month" },
"credits": { "perPeriod": 500, "rollover": "none" } } ],
"products": [
{ "name": "Field Guide", "price": { "amountMinor": 1500, "currency": "gbp" },
"deliveryFile": "assets/field-guide.pdf", "taxCategory": "ebook" } ] }
npx gemmein syncshows what it will change, uploads the file, and sets it in Development. With Stripe connected there, each priced item sells at once through Stripe test mode; without, it waits as not sold yet and is made the moment the key connects.- Those rows read set by your AI on the Payments page. You can edit any of them;
the next sync shows your edit and leaves it as it is, unless you run
npx gemmein sync --overwrite. - An item missing from the file stays.
"remove": trueon it takes it down: its Payment Link goes off, and a file stays for whoever bought it. - A product's file is a pdf, zip, epub, audio file or image up to 100 MB, or a
video (mp4, webm, mov) up to 500 MB (50 MB in Development). The buyer's
g.files.link(file)plays an audio or video product in the page; other files download. npx gemmein payments --cloudreads back what Development sells: each price, how it is sold, its Payment Link, its file and tax category, and whether Stripe is connected.- Live is never set by sync. Going live and promote copy Development's plans and products into Live, where Live's own key makes them.
Thanks for telling us.
Thanks for telling us.
Last updated