Back to blog

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.

A digital wallet streaming credit tokens into an AI chat interface

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:

  1. Per-end-user wallets. Every one of your users has a wallet with a real USD balance, separate from your org credits.
  2. 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.
  3. 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 ◀───────┘45   Browser (es_) ──▶ chat / images / embeddings ──▶ debits that user's wallet6                 └──▶ buy credits (Stripe Elements) ─▶ net credited, your margin accrues

Three 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/>, plus useBalance/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}

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}

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.