Stripe for AI: Embed AI + Credit Purchases in Your App
Our new Embeddable SDK lets your end-users buy credits inside your app and chat with any model — billed through LLM Gateway, with your markup as margin. Here's how it works and how to ship it in ~40 lines.

If you're building an AI feature into your product, you hit the same two problems fast: how do your users pay for the AI they use, and how do you not rebuild billing, wallets, and model plumbing from scratch?
Today we're shipping the Embeddable SDK — think Stripe + Stripe Elements, but for AI. Your end-users get their own wallet, buy credits inside your app, and chat with any model the gateway supports. LLM Gateway is the merchant of record, you set a markup, and the margin is yours.
The model: platform wallets
Most "add AI to your app" stories assume you eat the model cost and reconcile it later. We wanted the opposite: each of your users holds their own balance, tops it up themselves, and is billed per request — while you earn a margin on top.
So the SDK is built around three ideas:
- Per-end-user wallets. Every one of your users has a wallet with a real USD balance, separate from your org credits.
- Markup at top-up time. You set a markup percent. When a user buys $10 of credits, their wallet is credited the net spend power and your margin accrues to your organization for payout. The per-request path stays simple — it just debits raw cost.
- Browser-safe sessions. Your secret key never touches the browser. Your backend mints a short-lived, scoped session token (
es_…) bound to one wallet; the browser uses only that.
1Your backend ──(sk_)──▶ POST /v1/sessions ──▶ es_ token (~15 min, scoped to one wallet)2 │ │3 └──────── returns es_ to your frontend ◀───────┘4 │5 Browser (es_) ──▶ chat / images / embeddings ──▶ debits that user's wallet6 └──▶ buy credits (Stripe Elements) ─▶ net credited, your margin accrues1Your backend ──(sk_)──▶ POST /v1/sessions ──▶ es_ token (~15 min, scoped to one wallet)2 │ │3 └──────── returns es_ to your frontend ◀───────┘4 │5 Browser (es_) ──▶ chat / images / embeddings ──▶ debits that user's wallet6 └──▶ buy credits (Stripe Elements) ─▶ net credited, your margin accruesThree packages
@llmgateway/server— your backend, holds the secret key. Mints sessions, manages wallets/customers, verifies webhooks, triggers payouts.@llmgateway/client— a headless, browser-safe client (chat/stream/image/embeddings + balance/top-up) with automatic session refresh.@llmgateway/elements— React drop-ins:<Chat/>,<BuyCredits/>,<CreditBalance/>, plususeBalance/useChat.
Shipping it in ~40 lines
Backend — mint a session with your secret key:
1// app/api/llmgateway/session/route.ts2import { LLMGateway } from "@llmgateway/server";3
4const lg = new LLMGateway({ secretKey: process.env.LLMGATEWAY_SECRET_KEY! });5
6export async function POST() {7 const session = await lg.sessions.create({8 customer: { externalId: "user_123" }, // your signed-in user9 scope: { models: ["openai/gpt-4o-mini"] }, // lock down what they can call10 });11 return Response.json(session); // { sessionToken, walletId, expiresAt }12}1// app/api/llmgateway/session/route.ts2import { LLMGateway } from "@llmgateway/server";3
4const lg = new LLMGateway({ secretKey: process.env.LLMGATEWAY_SECRET_KEY! });5
6export async function POST() {7 const session = await lg.sessions.create({8 customer: { externalId: "user_123" }, // your signed-in user9 scope: { models: ["openai/gpt-4o-mini"] }, // lock down what they can call10 });11 return Response.json(session); // { sessionToken, walletId, expiresAt }12}Frontend — drop in the widgets:
1"use client";2import {3 LLMGatewayProvider,4 Chat,5 CreditBalance,6 BuyCredits,7} from "@llmgateway/elements";8
9const fetchSession = () =>10 fetch("/api/llmgateway/session", { method: "POST" }).then((r) => r.json());11
12export default function App({ session }) {13 return (14 <LLMGatewayProvider15 session={session}16 fetchSession={fetchSession}17 test={process.env.NODE_ENV !== "production"}18 >19 <CreditBalance /> {/* live wallet balance */}20 <BuyCredits amount={10} />{" "}21 {/* Stripe checkout → credits land in the wallet */}22 <Chat model="openai/gpt-4o-mini" />{" "}23 {/* streams, debits the wallet per request */}24 </LLMGatewayProvider>25 );26}1"use client";2import {3 LLMGatewayProvider,4 Chat,5 CreditBalance,6 BuyCredits,7} from "@llmgateway/elements";8
9const fetchSession = () =>10 fetch("/api/llmgateway/session", { method: "POST" }).then((r) => r.json());11
12export default function App({ session }) {13 return (14 <LLMGatewayProvider15 session={session}16 fetchSession={fetchSession}17 test={process.env.NODE_ENV !== "production"}18 >19 <CreditBalance /> {/* live wallet balance */}20 <BuyCredits amount={10} />{" "}21 {/* Stripe checkout → credits land in the wallet */}22 <Chat model="openai/gpt-4o-mini" />{" "}23 {/* streams, debits the wallet per request */}24 </LLMGatewayProvider>25 );26}That's the whole integration. The session token auto-refreshes before it expires, <BuyCredits> loads LLM Gateway's bundled Stripe publishable key, confirms the payment, and the balance updates once the webhook credits the wallet. Pass test while developing to use Stripe test mode; you don't need to ship a Stripe publishable key of your own for LLM Gateway payments.
A few engineering details we cared about
- The hot path stays low-cardinality. Browser sessions live in a dedicated session table, while logs and API-key aggregates use one hidden project-level embedded-session key so end-user traffic does not create one API key per user.
- Idempotent top-ups. Wallet credits are written in a single transaction guarded by a unique index on the payment intent, so a re-delivered Stripe webhook can never double-credit.
- Safe by default. Tokens are short-lived and revocable, scoped to an allow-list of models, bounded by per-session spend caps, and constrained by a per-project origin allowlist. Webhook URLs are validated against SSRF (no private/internal targets), and developer margin payouts reserve funds before transferring so they can't overpay under concurrency.
Try it
There's a complete, runnable Next.js example — backend session route, provider, chat, and buy-credits — in the templates repo:
➡️ theopenco/llmgateway-templates → templates/embeddable-credits
Full reference is in the Embeddable SDK docs. Enable end-user sessions on your project, create a platform secret key, and you can be live in an afternoon.