Docs
Payments
Your app takes payments into your own Stripe account. Tofu connects the two through its Stripe app — you install it once, on Stripe’s own page — creates your payment links and passes each payment’s event to your app, so it knows who paid. The money goes straight to you, and nobody copies a key.
Agent tools
appPaymentsStatusappPaymentsConnectappPaymentsSetupappPaymentsLinkCreateappPaymentsDisconnect
How it works
Tofu is not part of the sale. Your buyers pay on Stripe’s own payment page, into your Stripe account: Tofu never sees a card, holds no money and takes no fee on your sales. What Tofu does is the wiring — the connection to your account, the payment links your app sells through, the secret your app checks each payment’s event with, and the events themselves, passed on to your app.
Connect Stripe from your app’s Payments page in the dashboard. A payment link of your own also takes payments in every app, a static site included.
Your app needs a server to hear about payments. Connecting and testing come with a paid plan, its free trial included, and real payments open once the plan’s first month is paid.
Connect Stripe
Once, from the app’s Payments page, approved on Stripe’s own page.
Press Connect Stripe
On your app’s Payments page. Your coding agent can hand you the same thing: Tofu’s own link, for you to open once within 30 minutes, signed in to Tofu. An install that didn’t start at Tofu’s link connects no app.
Install Tofu’s app on Stripe’s page
Choose the Stripe account, and a sandbox first: test payments move no money. Stripe shows what Tofu’s app may do there — read the account’s name, create products, prices and payment links, and read the account’s payment events, checkouts, subscriptions and invoices, buyer details included, of which Tofu passes on only those of your apps. Installing takes Apps administrator rights in that account.
Back in Tofu
Stripe sends you back to the app’s Payments page, which names the account the money goes to, its ID, and whether it takes real or test payments.
Set up payment events
Your app hears about each payment at a webhook route in its own code. The route checks each event’s signature with Stripe’s own library and a secret Tofu saves for it. It needs no Stripe key, because it never calls Stripe: everything it needs — who paid, how much, whether it was paid — is in the event.
// app/api/stripe/webhook/route.js — checks each payment event; needs no Stripe key.
import Stripe from "stripe";
export async function POST(request) {
const body = await request.text();
let event;
try {
event = Stripe.webhooks.constructEvent(body, request.headers.get("stripe-signature"), process.env.STRIPE_WEBHOOK_SECRET);
} catch {
return new Response("Not a verified event", { status: 400 });
}
if (event.type === "checkout.session.completed" && event.data.object.payment_status === "paid") {
// Unlock what was bought for event.data.object.client_reference_id — once per event.id — and keep event.livemode beside it.
}
return new Response(null, { status: 200 });
}On the Payments page, Set up events takes the route’s path and the name your code reads the secret from — STRIPE_WEBHOOK_SECRET unless you choose another. Tofu saves the secret as a hidden value in your app’s settings, never over a value of your own, and sends the route one request with no signature, which it must refuse:
| The page says | What it means |
|---|---|
| Checks signatures | The route refused the unsigned request. This is the one that is right. |
| Accepts anything | Anyone could fake a payment to it: have the route check each event as above. |
| Not found | Nothing answers at that path: check the route’s path. |
| Crashed | Often a Stripe client created without a key when the route loads. Checking an event needs no key. |
The check is one request, at one time: the page shows it as the last check, with its time. If your route fails Tofu’s deliveries afterwards, the page marks it as failing now beside that check, and setting up events again checks it once more.
The saved secret reaches your app with its next deploy, so press Put it online now. Until Stripe lets Tofu’s app create endpoints in your account, Tofu passes each event on itself, from Tofu’s own servers and signed the way Stripe signs them, so the code above accepts it as it is: don’t limit the route to Stripe’s IP addresses. An event can arrive more than once, so handle each event ID once. A delivery your app doesn’t take is tried again on Stripe’s own schedule — for up to 3 days for a real payment, a few hours for a test one. While your app keeps failing, Tofu holds its events and sends them once its route answers again, but for no more than 3 days from when each arrived, so fix a failing route before then: an event your app hasn’t taken by then isn’t delivered, though its payment stays recorded in your Stripe account.
Create a payment link
Create payment link takes a name your buyers see, an amount, a currency and — once your Stripe account allows it — how often it repeats, and shows you what it will make before it does. Tofu creates the product, its price and the link in your Stripe account, and the Payments page lists each link with how often it repeats and its newest payment — for a link that repeats, its newest sign-up, since each renewal is an invoice that your Stripe Dashboard lists and Deliveries shows reaching your app. Put the link on a button, and add the signed-in person’s ID as client_reference_id, so each payment’s event says who paid — an ID, never an email address.
<a href="https://buy.stripe.com/…?client_reference_id=USER_ID">Pay the deposit</a>Never unlock anything on the page buyers return to after paying: anyone can open that address without paying.
Subscriptions
A payment link that repeats: every day, week, month or year.
Choose how often a link repeats in Create payment link’s Repeats — your coding agent can also make one repeat every few days, weeks, months or years, at most three years apart — and the link makes a subscription in your Stripe account: Stripe charges each buyer again every period until the subscription is cancelled. Tofu passes your app the subscription’s events as well as its first payment, from Tofu’s own servers and signed the way Stripe signs them, so the webhook route above checks them as they are, and handles each event ID once.
| Event | What it means |
|---|---|
checkout.session.completed | The first payment. Its subscription and client_reference_id say which subscription it started, and for whom. |
customer.subscription.created | The subscription exists. |
invoice.paid | A period was paid, the first one and each renewal: keep the membership going. The invoice names its subscription in parent.subscription_details.subscription. |
invoice.payment_failed | A renewal failed: tell the member. Stripe tries again. |
customer.subscription.updated | It changed: set to end at the period’s end (cancel_at_period_end), or behind on payment (status past_due or unpaid). |
customer.subscription.deleted | It ended: end the membership. |
Set up events forwards these beside a checkout’s, a link that repeats adds them for an app set up before, and setting up again keeps them while the app has such a link, turned off or not. Only the first payment’s event carries client_reference_id: record which person its subscription is for, because a renewal’s invoice names the subscription alone. An invoice’s event carries the buyer’s email and address, and passes through Tofu like a checkout’s: held encrypted until your app takes it, for at most 3 days, and never logged.
Repeats appears once your Stripe account has approved the permissions that let Tofu pass a subscription’s events on. An account that installed Tofu’s app before it asked for them approves them in Stripe, which asks by email and on Tofu’s app in your Stripe Dashboard (Settings → Installed Apps); until then your links are one-time, and the Payments page says so. Right after you install, Stripe takes a minute or so to tell Tofu what the install allows, and the page checks again by itself meanwhile.
You cancel or change a subscription in your Stripe Dashboard, and your app hears it. A subscription keeps renewing in Stripe until it is cancelled there — also after you turn its link off or disconnect, when your app no longer hears about it.
Test with a test card
In a sandbox, pay with Stripe’s test card 4242 4242 4242 4242, any future date and any CVC: no money moves. The Payments page’s Deliveries lists each event Tofu passed on with the status code your app answered, and says Test payment received once your app took one. Then check that your app unlocked what was bought. Stripe sends no receipt for a test payment by itself.
Go live
Real payments open once your paid plan’s first month is paid, and need two things done with your test connection first: your webhook route passed Tofu’s check (it refused the unsigned request, so it checks signatures), and a test payment reached your app. Until both are done, the Payments page says which is left where Connect your live account will be, and your coding agent is refused the same step. Then Connect your live account installs Tofu’s app in your live Stripe account and replaces the test connection, taking out the values it saved. Set up events and create your links again there, clear what test payments unlocked — their events carried livemode: false — and put your app online.
Never test a live link with a real card: Stripe’s terms do not allow it.
With your coding agent
Every step on the Payments page is one of your coding agent’s tools as well, each with a command for the command line. The three that change something carry confirmed: true only after you agreed to that exact step. They run on the agent client installed on your computer; a chat app connected by address doesn’t offer them.
| Tool | What it does |
|---|---|
appPaymentsStatus | Reads the connection, the account, the route check, the links and the deliveries. |
appPaymentsConnect | Hands you Tofu’s link to open once, signed in. |
appPaymentsSetup | Saves the secret and checks the route. |
appPaymentsLinkCreate | Creates a product, its price and a payment link, one-time or repeating. |
appPaymentsDisconnect | Ends the connection, turning off the links you name. |
Disconnect
Disconnect Stripe on the Payments page ends the connection after you type the app’s name — its letter case as it is, a straight or curly apostrophe either way: Tofu stops passing on its events, removes what it saved in the app and forgets what it recorded but a dated line of what was done. A payment link left on keeps taking payments your app no longer hears about, so the dialog lists the links Tofu created and turns off the ones you tick — none until you do. Your Stripe account, its data and its money stay as they are.
Uninstalling Tofu’s app in Stripe (Settings → Installed Apps) ends the connection too, but then Tofu can no longer reach that account to turn links off there.
A payment link of your own
For any app on any plan, a static site included: four steps, with nothing set up in Tofu.
Make the link in your Stripe account
In the Stripe Dashboard, open Payment Links and choose New. Pick a product or add one with its price, or choose Customers choose what to pay for a tip or a donation, then Create link. Start in a sandbox, where no money moves.
Give your agent the link, and only the link
It is an address, not a secret. Adding a payment link takes no Stripe key, password or code, so your agent has no reason to ask for one.
Your agent adds the button and deploys
A plain link or button to that address, with
client_reference_idwhere people sign in.Test it, then go live
With the test card, then the same link made in live mode, which your agent swaps in.
Stripe records every payment in your account, but with a link of your own your app is not told who paid, so nothing in it unlocks by itself. It suits a deposit, a tip, a single item or a booking fee — Frank’s Kitchen taking a deposit for a table, say. An app with its own Stripe code — a checkout its server starts, a webhook it checks — needs your Stripe secret key and webhook signing secret: you type them in Keys & settings with Hide this value after saving on, never in a chat (Environment variables).
What stays in your Stripe account
Refunds, disputes and payouts.
You handle them in your Stripe account. No Tofu tool touches them.
Receipts.
Stripe emails buyers a receipt once you turn on Settings → Business → Customer emails → Successful payments. A sandbox payment sends none by itself.
Tax.
Yours to handle, unless you sell something digital through Stripe’s Managed Payments, which takes on sales tax, disputes and buyer support for eligible businesses and products. You turn it on in Stripe and choose it when you make a link; an existing link cannot take it on.
Your buyers’ details.
They stay in Stripe. Through Tofu, a payment’s event passes through Tofu on its way to your app — held encrypted until your app takes it, at most 3 days, and never logged.
What Tofu does not do with payments
Take part in the sale. Payments go from your buyer to your Stripe account. Tofu never sees a card, holds no money and takes no fee.
Ask for a key. Connecting needs none, and neither does a payment link. A key your own server code needs is a secret you type in Keys & settings.
Refund, dispute or pay out. Those stay in your Stripe account, and no Tofu tool does them.
Cancel a subscription. You cancel or change one in your Stripe account, and no Tofu tool does; Tofu passes the cancellation on to your app.
Tell a static site who paid. It has no server to hear about payments: a payment link of your own is its way, and the app is not told who paid.
Real payments during the free trial. During the trial you test in a sandbox. Real payments open once the first month is paid.
Questions? Email hello@trytofu.ai, we reply within 2 business days.
说说你想做什么 app,或者带上 agent 已经做好的 app。Tofu 帮你上线。
任何 coding agent 都能用
© 2026 Tofu
trytofu.ai
本页由英文翻译;如有内容冲突,请以英文原版为准。