Skip to content

AI app builders

No backend? Still a real business.

Apps from Lovable, v0, Bolt, or Replit are mostly frontend, so the usual move is a payment link from a payments API. That collects money and records an amount, and the amount is all you hold. A Flint link records the sale itself: what sold, tax computed, refunds per item, a receipt, a buyer portal. Your app has no database, so that record is your entire back office.

A checkout your app can hold
{
  "data": {
    "payment_link_id": "plink_1kmn0aExample",
    "status": "active",
    "payment_link_type": "standard",
    "url": "https://pay.withflintpay.com/plink_1kmn0aExample"
  }
}

This URL is the whole integration for a client-only app. The amounts behind it were fixed server-side when the link was created, so it is safe in a button, a bio, or a QR code.

zero keys in the app · zero card fields in your code · every payment lands as a real order

01The rule

The key never ships to the browser.

This is the one thing to hold the line on, because the builder's agent will not. Prompted for payments, it pastes a key into the page and computes the total there too. Everyone who opens the app can read both.

What a prompt writes
// what a prompt happily writes into the page
const API_KEY = "sk-live-8f2a...";        // now in every browser

async function pay(total) {              // and totals from the client
  await fetch("https://api.some-processor.com/charges", {
    method: "POST",
    headers: { Authorization: "Bearer " + API_KEY },
    body: JSON.stringify({ amount: total * 100 }),
  });
}

Frontend code is public code. A key here can be lifted by anyone who opens devtools, and a client-computed total can be edited before it is charged.

Every Flint key is a server credential. There is no browser variant to reach for, so the mistake has no valid form.

server-only keys

What the app exposes instead is a checkout URL. Amounts were fixed when it was created, so exposure costs nothing.

pay.withflintpay.com

Card fields render on Flint's hosted page, never in your generated components, so card data never touches your app.

hosted checkout

Tell the agent the rule and it follows it. The prompt at the bottom of this page includes it verbatim.

no keys in app code

02Path one

A payment link, before the app has a server.

Any payments API can hand you a link; the difference is what the link leaves behind. A payment models money arriving. An order models the sale: what sold, what tax, what is refundable, and to whom. One call makes this one, or the dashboard does with no code at all.

Create the link
curl -X POST https://api.withflintpay.com/v1/payment-links \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: payment-link-spring-tee-001" \
  -d '{
    "name": "Limited spring tee",
    "line_items": [
      { "name": "Spring tee", "quantity": 1,
        "unit_price_money": { "amount": 3500, "currency": "USD" } }
    ]
  }'

Make it in the dashboard instead and the result is identical. Links can also take donations with suggested amounts, sell event tickets, or start subscriptions.

Every payment through the link creates a real order with server-computed totals and tax, not a bare charge.

origin: payment_link

Buyers get an emailed receipt and a hosted page; you get the order in the dashboard with refunds one click away.

order.paid

Links deactivate cleanly when the price or product changes, and the orders they created stay.

POST /v1/payment-links/{payment_link_id}/deactivate

Link types and depth, donations, events, capacity, custom fields, live on the payment links page.

03Path two

One tiny function, when the builder gives you a server.

Most builders do have a server side, it is just not where the agent defaults to putting things. One function that creates the order and returns the checkout URL is the entire backend a store needs.

The whole backend
// one server function, any runtime that has fetch.
// shown as a Supabase edge function; the body is identical
// in a Node server or a route handler.
Deno.serve(async () => {
  const key = Deno.env.get("FLINT_API_KEY"); // a secret, not app code

  const order = await fetch("https://api.withflintpay.com/v1/orders", {
    method: "POST",
    headers: { Authorization: `Bearer ${key}` },
    body: JSON.stringify({
      line_items: [
        { name: "Riso print", quantity: 1,
          unit_price_money: { amount: 1800, currency: "USD" } },
      ],
    }),
  }).then((r) => r.json());

  const session = await fetch("https://api.withflintpay.com/v1/checkout-sessions", {
    method: "POST",
    headers: { Authorization: `Bearer ${key}` },
    body: JSON.stringify({
      order_id: order.data.order_id,
      redirects: { success_redirect_url: "https://example.com/thanks" },
    }),
  }).then((r) => r.json());

  // the app opens this URL; the key never left the function
  return Response.json({ url: session.data.url });
});

Plain fetch, so it runs anywhere: a Supabase Edge Function, a Node server, a route handler. The key comes from the runtime's secret store and never appears in app code.

Lovable

Server work runs in Supabase Edge Functions. Put the key in a Supabase secret and paste the function above nearly verbatim. No Supabase connected? Use path one.

v0

v0 generates Next.js, so you have server actions and route handlers with env vars. The Next.js commerce page walks the exact integration, webhooks included.

Bolt

Bolt builds full-stack Node apps, so the function is a plain server route. Set the key as an env var when you deploy, not in the generated source.

Replit

A real server plus a Secrets pane, which is exactly the shape this needs. Ask the agent to read the key from secrets and keep it out of the repo.

Building in v0 or exporting to Next.js? The Next.js commerce page is the deeper version of this section.

04Underneath

Rebuild the app; keep the store.

Builder apps get regenerated, forked, and abandoned, sometimes in the same week, and they have no database for a store to live in. The record outside the app is not a nice-to-have. It is the business.

Orders, buyers, payment history, and receipts live on Flint. Regenerating the frontend does not touch a single sale.

GET /v1/orders

The dashboard is the back office you did not have to build: today's orders, who bought what, one-click refunds, payouts. A feed of amounts answers almost none of that.

app.withflintpay.com

When you outgrow the builder, the integration deepens instead of restarting: same records, same key, more calls.

link → checkout → your own frontend

The general argument for why the record matters more than the app is on the sell online with AI page.

05Start

One prompt, one rule.

Paste into your builder
Add a Buy button for my eighteen dollar riso print.
Open my Flint payment link when it is clicked. Do
not put any API key or card form in the app code.

Create the link first in the dashboard, then give the agent its URL. The rule in the last sentence is the one from section 01, stated where the agent will obey it.

Ready for path two? Swap the last sentence for: create the order and checkout session in a server function with the key from my secrets, and open the returned URL.

FAQ

Questions worth asking first.

Why not just use a Stripe payment link?

For collecting money once, either works, and Stripe processes Flint's card payments anyway. The difference is the record each link leaves. A payments API models the payment: an amount that arrived. A Flint link creates an order: line items, computed tax, per-item refunds, an emailed receipt, and a buyer portal, accumulating into history you can run a business on. In an app with no backend that matters double, because these records are the only back office you have. And when the app grows into hosted checkout or subscriptions, it grows onto the same order records instead of starting over.

How do I add payments to a Lovable app?

Lovable keeps server work in Supabase Edge Functions, which is exactly where Flint belongs. Store your Flint key as a Supabase secret, create the order and checkout session inside a function, and have the app open the returned URL. If you have not connected Supabase, skip the server entirely: create a payment link and point your Buy button at it.

How do I add payments to a v0 app?

v0 generates Next.js, so the integration is a server action or route handler with the key in a server-only env var, two calls, order then checkout session, and a redirect to the returned URL. The Next.js commerce page walks that exact code, including the webhook route that confirms payment.

How do I add payments to a Bolt or Replit app?

Both give you a real server, so use it. Keep the Flint key in the environment, Bolt sets env vars at deploy and Replit has a Secrets pane, create the order and session server-side, and send the browser the URL. The prompt to give either agent is on this page.

Can I accept payments with no backend at all?

Yes. A payment link is a full Flint checkout behind a URL: create it in the dashboard or with one API call, then use it as a plain link anywhere, a button, a bio, a DM, a QR code. Every payment through it creates a real order with computed totals and tax, and refunds and receipts work from the dashboard.

Why not just paste my API key into the generated code?

Anything in a builder app's frontend ships to every visitor, so a pasted key is public the moment the app loads, and a Flint key can read orders and move money. Flint keys are server credentials on purpose. The browser only ever needs the checkout URL, which is safe to expose because the amounts behind it were fixed server-side when it was created.

What happens when I outgrow the builder?

You keep the store. Orders, buyers, payment history, and receipts live on Flint, not in the generated app, so rebuilding the frontend, in the same builder or in Next.js with your own code, does not touch them. The integration deepens from a link to hosted checkout to your own frontend against the same order records. Nothing gets migrated because nothing was stored in the app.