Blog
    Integration7 min read

    Add Credits and Payments to a Lovable, Bolt or v0 App

    Copy-paste prompts that add a credit balance, per-feature charging and Stripe checkout to an AI-built app, with the API key kept in a server or edge function secret.

    By cred.diy Team

    Add Credits and Payments to a Lovable, Bolt or v0 App

    You'll add a credit balance, per-feature charging and a "Buy credits" button to an app built with Lovable, Bolt or v0 by pasting three prompts that call the Cred REST API from server-side code. The prompts are written so the AI builder puts your Cred API key in a server or edge function secret and never in browser code.

    That last part needs watching. AI builders like to "simplify" by moving a fetch call into a React component. The prompts below forbid it, and the checklist in step 6 shows how to confirm the builder listened.

    Prerequisites

    • An app with sign-in, so the server knows each user's ID and email.
    • A server side the builder can write to:
    • A cred.diy project with:
      • a credit type (Dashboard → Credit Types, copy the Credit Type ID)
      • a pricing tier (Dashboard → Pricing Tiers, "Copy ID")
      • Stripe connected on the Payments page
      • an API key (Dashboard → API Keys, starts with sk_live_)

    Where the API key goes

    BuilderServer codeWhere the secret lives
    LovableSupabase Edge FunctionThe project's Supabase secrets, read with Deno.env.get
    BoltSupabase Edge Function or server routeSupabase secrets, or the host's environment variables
    v0Next.js route handlerVercel environment variable, without a NEXT_PUBLIC_ prefix

    Never paste the key into the chat itself, because chat history is stored with the project. Add it only through the platform's secrets or environment-variable settings, and give it to the builder as a name: CRED_API_KEY.

    Step 1: Give the builder the real API docs

    AI builders guess API shapes when they don't have the docs. Open /docs, click Copy docs for AI, and paste the result into the chat before the first prompt. The same Markdown is published at https://cred.diy/docs.md if your builder can fetch URLs.

    Step 2: Paste the server function prompt

    Create a server-side function named "cred". In Lovable or Bolt make it a Supabase
    Edge Function; in v0 make it a Next.js route handler at app/api/cred/route.ts.
    
    Rules:
    - Read secrets CRED_API_KEY, CRED_CREDIT_TYPE_ID and CRED_PRICING_TIER_ID from the
      server environment. Never put them in frontend code, and never in a variable
      prefixed VITE_ or NEXT_PUBLIC_.
    - Identify the user from the verified session on the server (the Supabase JWT, or
      the app's auth). Never trust a user id, email or amount sent by the frontend.
    - Lowercase the user's email before sending it.
    - Cred base URL: https://eutdgemlrpvnxfkkpvlu.supabase.co/functions/v1/credits-api
    - Send the key in an "x-api-key" header and "Content-Type: application/json".
    
    Actions (the frontend posts { action, feature?, requestId? }):
    - "welcome": POST /issue with body { user_id, user_email, credit_type_id, amount: 20,
      reason: "Welcome credits" } and header Idempotency-Key: "welcome:<user id>".
    - "balance": GET /balance?email=<email>&credit_type_id=<CRED_CREDIT_TYPE_ID>.
      Return balances[0].balance, or 0 if balances is empty.
    - "spend": look up the cost of the named feature in a server-side table
      { generate: 5 }. POST /deduct with body { user_id, user_email, credit_type_id,
      amount, reason: feature, reference_id: requestId } and header
      Idempotency-Key: "spend:<user id>:<requestId>".
      If the response code is INSUFFICIENT_CREDITS or USER_NOT_FOUND, return HTTP 402.
      If idempotent_replay is true, return HTTP 409 and do not run the feature again.
      Otherwise return { balance: new_balance, transaction_id }.
    

    Step 3: Add the secret

    Add CRED_API_KEY, CRED_CREDIT_TYPE_ID and CRED_PRICING_TIER_ID where the table above says. In Lovable and Bolt with Supabase, that's the project's Edge Function secrets. In v0, it's the Vercel project's environment variables, which v0 also uses for external API keys. Redeploy if the platform asks you to.

    Step 4: Paste the "charge for a feature" prompt

    Before the Generate button runs its feature, call the "cred" function with
    { action: "spend", feature: "generate", requestId } where requestId is
    crypto.randomUUID() created once per click (reuse it only when retrying that same
    click). Only run the feature if the call succeeds.
    If it returns 402, show "You're out of credits" with a "Buy credits" button.
    Show the user's balance in the header using { action: "balance" } on load, and
    update it from the balance returned by "spend". Call { action: "welcome" } once
    after every sign-in; it is safe to repeat.
    

    Behind this, Cred deducts atomically, so the balance can't go below zero even if two clicks land at once. The idempotency key means a network retry of the same click isn't charged twice.

    Step 5: Paste the "buy credits" prompt

    Add an action "checkout" to the "cred" function. It POSTs to
    https://eutdgemlrpvnxfkkpvlu.supabase.co/functions/v1/hosted-checkout with the
    same x-api-key header and body { user_id, user_email,
    pricing_tier_id: CRED_PRICING_TIER_ID, success_url: "<app url>/credits?status=success",
    cancel_url: "<app url>/credits" }. Return the "url" field from the response.
    The "Buy credits" button calls this action and sets window.location.href to url.
    Do NOT add credits on the success page; Cred adds them after Stripe confirms payment.
    

    The hosted checkout returns { url, session_id, purchase_id }, where url is a Stripe Checkout page. After payment, Cred's Stripe webhook adds the tier's credits to that user. Redirect URLs must be https:// (http://localhost is allowed while developing). Otherwise Cred falls back to its own default pages.

    Step 6: Check what the builder wrote

    Spend two minutes on this before you ship:

    1. Search the frontend code for sk_live_, CRED_API_KEY and credits-api. None of them should appear outside the server function.
    2. Open the browser's network tab and click Generate. You should see a request to your function and none to credits-api or hosted-checkout.
    3. Read the function. Confirm the cost comes from a server-side table, the user comes from the session, and the Idempotency-Key header is set on /issue and /deduct.
    4. Test running out. Spend until the balance hits zero and confirm you get the 402 path, not an error page.
    5. Test buying. Complete a checkout and confirm the balance rises without any code on the success page adding credits.

    Common mistakes

    • The builder moves the Cred call into a component. If you see fetch(".../credits-api/...") in a .tsx file, ask for it to go back into the server function.
    • The key ends up as VITE_CRED_API_KEY or NEXT_PUBLIC_CRED_API_KEY. Those prefixes exist to put values into the browser bundle (see Next.js and Vite). Rename the variable, rotate the key in Dashboard → API Keys, and update the secret.
    • The key is hard-coded in the function file. It's then in your repository's history. Rotate it and read it from the environment.
    • Pasting the key into the chat. Treat a pasted key as leaked and rotate it.
    • Crediting on the success page. Anyone can visit /credits?status=success. Cred credits purchases from the payment webhook.
    • The frontend sends the amount. A user can edit any request their browser makes. Prices belong in server code.
    • The function generates its own idempotency key on every attempt. Then a retry looks like a new charge. The key must come from the click, via requestId.

    FAQ

    Can I tell the builder to install a Cred SDK? Not yet. The npm SDK isn't published, so have it call the REST API with fetch, as these prompts do.

    Does this work if my app doesn't use Supabase? Yes. Any server code that can keep a secret works: a Next.js route, an Express server, a Cloudflare Worker. The Cred calls stay the same.

    Can I sell monthly subscriptions instead of packs? Yes. Add billing: "recurring" to the checkout body with a tier whose billing type allows it. Without that field, checkout is a one-time purchase. See Subscriptions.

    Will users keep their credits if I build a second app? Within the same Cred project, yes: the same email is the same wallet. See End users.

    What does Cred cost me? Each API call uses 1 CRD platform credit. The pricing page has the packages.

    Sources

    Try it on a real project.