> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hexclave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Payments

> Sell subscriptions and one-time products, and manage entitlements like credits and seats - billing without the plumbing

export const TransactionsSkeleton = () => {
  const Frame = ({label, children}) => <div className="overflow-hidden rounded-2xl border border-zinc-950/10 bg-white dark:border-white/10 dark:bg-zinc-900">
      <div className="flex items-center gap-2 border-b border-zinc-950/10 bg-zinc-950/[0.03] px-3 py-2 dark:border-white/10 dark:bg-white/[0.03]">
        <div className="flex gap-1.5">
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
        </div>
        <span className="ml-1 text-[11px] font-medium text-zinc-400 dark:text-zinc-500">{label}</span>
      </div>
      <div className="p-4">{children}</div>
    </div>;
  const row = (amount, status, color) => <div className="flex items-center justify-between border-b border-zinc-950/[0.06] py-2.5 last:border-b-0 dark:border-white/[0.06]">
      <div className="flex items-center gap-2.5">
        <div className="h-6 w-6 rounded-md bg-zinc-100 dark:bg-zinc-800" />
        <div className="flex flex-col gap-1.5">
          <div className="h-2 w-24 rounded-full bg-zinc-300 dark:bg-zinc-600" />
          <div className="h-1.5 w-16 rounded-full bg-zinc-200 dark:bg-zinc-700" />
        </div>
      </div>
      <div className="flex items-center gap-3">
        <span className="text-[12px] font-semibold text-zinc-700 dark:text-zinc-200">{amount}</span>
        <span className="rounded-full px-2 py-0.5 text-[10px] font-medium" style={{
    backgroundColor: color + "22",
    color
  }}>
          {status}
        </span>
      </div>
    </div>;
  return <div className="not-prose my-6">
      <Frame label="Transactions">
        <div className="flex flex-col">
          {row("$20.00", "Paid", "#10b981")}
          {row("$99.00", "Renewed", "#10b981")}
          {row("$20.00", "Refunded", "#f59e0b")}
          {row("$20.00", "Failed", "#ef4444")}
        </div>
      </Frame>
    </div>;
};

export const EntitlementsSkeleton = () => {
  const ACCENT = "#10b981";
  const Frame = ({label, children}) => <div className="overflow-hidden rounded-2xl border border-zinc-950/10 bg-white dark:border-white/10 dark:bg-zinc-900">
      <div className="flex items-center gap-2 border-b border-zinc-950/10 bg-zinc-950/[0.03] px-3 py-2 dark:border-white/10 dark:bg-white/[0.03]">
        <div className="flex gap-1.5">
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
        </div>
        <span className="ml-1 text-[11px] font-medium text-zinc-400 dark:text-zinc-500">{label}</span>
      </div>
      <div className="p-4">{children}</div>
    </div>;
  const meter = (label, value, pct) => <div className="flex flex-col gap-2 rounded-xl border border-zinc-950/[0.06] p-3 dark:border-white/[0.06]">
      <div className="flex items-center justify-between">
        <span className="text-[11px] font-medium text-zinc-500 dark:text-zinc-400">{label}</span>
        <span className="text-[13px] font-semibold text-zinc-800 dark:text-zinc-100">{value}</span>
      </div>
      <div className="h-1.5 w-full overflow-hidden rounded-full bg-zinc-200 dark:bg-zinc-700">
        <div className="h-full rounded-full" style={{
    width: pct,
    backgroundColor: ACCENT
  }} />
      </div>
    </div>;
  return <div className="not-prose my-6">
      <Frame label="user.useItem(…)">
        <div className="grid grid-cols-3 gap-2.5">
          {meter("Credits", "820", "82%")}
          {meter("Seats", "4 / 5", "80%")}
          {meter("API quota", "12k", "47%")}
        </div>
      </Frame>
    </div>;
};

export const PricingSkeleton = () => {
  const ACCENT = "#10b981";
  const Frame = ({label, children}) => <div className="overflow-hidden rounded-2xl border border-zinc-950/10 bg-white dark:border-white/10 dark:bg-zinc-900">
      <div className="flex items-center gap-2 border-b border-zinc-950/10 bg-zinc-950/[0.03] px-3 py-2 dark:border-white/10 dark:bg-white/[0.03]">
        <div className="flex gap-1.5">
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
        </div>
        <span className="ml-1 text-[11px] font-medium text-zinc-400 dark:text-zinc-500">{label}</span>
      </div>
      <div className="p-4">{children}</div>
    </div>;
  const tier = (name, price, highlighted) => <div className={"flex flex-col gap-3 rounded-xl border p-3 " + (highlighted ? "border-transparent ring-2" : "border-zinc-950/[0.08] dark:border-white/[0.08]")} style={highlighted ? {
    boxShadow: `0 0 0 2px ${ACCENT}`
  } : undefined}>
      <div className="flex items-center justify-between">
        <span className="text-[12px] font-semibold text-zinc-700 dark:text-zinc-200">{name}</span>
        {highlighted && <span className="rounded-full px-2 py-0.5 text-[9px] font-semibold text-white" style={{
    backgroundColor: ACCENT
  }}>
            Popular
          </span>}
      </div>
      <div className="flex items-baseline gap-1">
        <span className="text-[18px] font-bold text-zinc-800 dark:text-zinc-100">{price}</span>
        <span className="text-[10px] text-zinc-400 dark:text-zinc-500">/mo</span>
      </div>
      <div className="flex flex-col gap-1.5 pt-1">
        {["80%", "65%", "72%"].map((w, i) => <div key={i} className="flex items-center gap-1.5">
            <div className="h-1.5 w-1.5 rounded-full" style={{
    backgroundColor: ACCENT,
    opacity: 0.6
  }} />
            <div className="h-2 rounded-full bg-zinc-200/90 dark:bg-zinc-700/80" style={{
    width: w
  }} />
          </div>)}
      </div>
    </div>;
  return <div className="not-prose my-6">
      <Frame label="Product line: &quot;Plan&quot;">
        <div className="grid grid-cols-3 gap-2.5">
          {tier("Free", "$0", false)}
          {tier("Pro", "$20", true)}
          {tier("Enterprise", "$99", false)}
        </div>
      </Frame>
    </div>;
};

<Note>
  **For agents/LLMs:** This is a high-level *marketing* overview of the Payments app, not an implementation reference. To actually build with payments, start at [Payments setup](./setup), then use the concept pages: [Products & Pricing](./products-and-pricing), [Items & Entitlements](./items-and-entitlements), [Checkout & Purchases](./checkout), [Subscriptions](./subscriptions), [Billing & Invoices](./billing-and-invoices), [Refunds](./refunds), [Granting Products](./granting-products), and [Customer Types](./customers).
</Note>

Charging the card is the easy part. Everything *after* that - turning a payment into access, keeping it in sync, handling upgrades, refunds, and renewals - is the part you'd normally build and maintain yourself. The Payments app owns that layer. You define products in the dashboard; Hexclave runs checkout, grants and revokes entitlements, tracks subscriptions, and keeps your billing state correct. Below are the questions developers actually ask, and the honest answers.

## Can I sell a subscription or one-time product?

Yes. Define products and prices in the dashboard, then generate a checkout URL and redirect. Hexclave handles the checkout session, the webhook, and granting access on success - you never write a webhook handler.

```typescript theme={null}
const checkoutUrl = await user.createCheckoutUrl({
  productId: "pro_monthly",
  returnUrl: window.location.href,
});
window.location.href = checkoutUrl;
```

<PricingSkeleton />

Group products into a **product line** to make them mutually exclusive - Free, Pro, and Enterprise where a customer can only hold one at a time. Upgrades, downgrades, and proration on plan switches are handled for you. Recurring prices can include a **free trial** — the card is collected at checkout and charged when the trial ends. See [Products & Pricing](./products-and-pricing#free-trials).

## Can I sell credits, seats, or API quota?

Yes - and this is the real point. Products include **items**: quantifiable entitlements like credits, seats, or API calls, with their own quantity, refresh schedule, and expiration. When a customer buys, the items are granted. When their plan ends, they're revoked. You don't track any of it in your own database.

<EntitlementsSkeleton />

Read a balance anywhere with a hook that stays in sync, and check access without a billing round-trip:

```typescript theme={null}
// Client - re-renders when the balance changes
const credits = user.useItem("credits");
return <span>{credits.nonNegativeQuantity} credits left</span>;
```

## Can I consume credits safely under load?

Yes. Consume on the server with `tryDecreaseQuantity` - a single transactional operation that returns `false` instead of letting a balance go negative. No read-then-write race, no double-spend when two requests land at once.

```typescript theme={null}
const credits = await user.getItem("credits");
const ok = await credits.tryDecreaseQuantity(1);
if (!ok) throw new Error("Out of credits");
// ...do the work
```

Grant more with `increaseQuantity`, or adjust manually from the dashboard. This is the consumption layer you'd otherwise build by hand with a ledger table and a pile of webhooks.

## What exactly does Hexclave own for me?

With a raw payment processor you build and maintain the glue: webhook handlers, granting and revoking access on payment, proration on upgrades, refunds, subscription endings, and keeping your own database in sync with all of it. Hexclave owns that layer:

* **Webhooks** - received and reconciled for you; there's nothing to host
* **Grants & revokes** - products and items are applied on payment and removed when a subscription ends or a refund is issued
* **Subscription lifecycle** - status, renewals, cancellations, and period ends tracked automatically
* **Proration** - handled automatically on plan switches, triggered through one SDK call
* **Refunds** - issued from the dashboard (partial or full), with optional entitlement revocation. See [Refunds](./refunds).
* **Sync** - entitlement and subscription state stays consistent without a database to babysit

<TransactionsSkeleton />

## Can I bill teams and external customers, not just users?

Yes. Every customer is a **user**, a **team**, or a **custom customer** (any external entity you key by ID). The billing surface - `createCheckoutUrl`, `useProducts`, `useItem`, `switchSubscription` - lives on user and team objects; custom customers are managed from the server SDK for items, products, and grants.

## Can I manage subscriptions in code?

Yes. Switch a customer between plans in the same product line, or cancel, with a single call - proration and timing handled for you.

```typescript theme={null}
await user.switchSubscription({
  fromProductId: "pro_monthly",
  toProductId: "enterprise_monthly",
});

await hexclaveServerApp.cancelSubscription({ productId: "pro_monthly" });
```

You can also `grantProduct` directly - for trials, comps, or admin grants - with a pre-defined product or an inline one-off definition.

## Can I build and test before going live?

Yes. Flip on **test mode** and purchases are granted instantly for free - no real charge, no live round-trip - so you can wire up entitlements end to end before connecting a real account.

## What it costs, and what it doesn't do

Straight answers so there are no surprises:

* **Platform fee** - Hexclave takes a **0.9%** fee on charges, on top of standard payment processing fees.
* **Currency** - payments are processed in **USD** today.
* **Not a marketplace** - Payments run through a single connected payment account per project. It isn't built for marketplace-style payouts to many sellers.
* **Not locked in** - this is a convenience layer, not a cage. Use it because it saves you the billing plumbing, not because you have to.

## Start here

1. [Set up Hexclave](/guides/getting-started/setup), then enable **Payments** in the dashboard.
2. Connect your payment account under **Payments → Settings**, and turn on **test mode** while you build.
3. Define a product (with items, if you want entitlements), then call `user.createCheckoutUrl(...)`.

Ready for the details - products, items, subscriptions, billing, invoices, refunds, and grants? Start with [Payments setup](./setup).
