18Docs

Payments

A Bounded app can charge its end users three ways: beta Bounded Pay for card checkout and fiat payout through Stripe Connect, payments.acceptCrypto for direct USDC on Solana, or a direct provider integration in a Function. Bounded Pay costs 1% in addition to Stripe’s fees; the direct USDC rail has no Bounded fee.

Choose a payment path

PathBuyer pays / seller receivesCost and control
Bounded PayCard payment / fiat payout through Stripe ConnectBounded’s 1% platform fee plus Stripe processing fees; managed seller onboarding and checkout
payments.acceptCryptoUSDC on Solana / USDC sent directly to the seller wallet0 Bounded fee on the direct-transfer rail; non-custodial, with Bounded verifying rather than holding funds or keys
Direct providerWhatever the provider supportsNo Bounded-managed payment fee; provider fees apply, and you own the account, keys, webhooks, lifecycle, and reconciliation

The paths are independent. An app can offer both card and USDC checkout; enabling one does not move money through or configure the other.

Bounded Pay beta: card checkout to fiat

Bounded Pay is the beta managed Stripe Connect path. The seller signs in with Bounded, completes Stripe Standard connected-account onboarding, and waits until /connect/status reports chargesEnabled. A buyer then starts checkout through /connect/checkout with their Bounded JWT.

Buyer checkout, persist before redirect
const response = await fetch("https://host.bounded.sh/connect/checkout", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${buyerJwt}`,
    "Content-Type": "application/json",
    "Idempotency-Key": logicalCheckoutId,
  },
  body: JSON.stringify({
    merchant: sellerBoundedUserId,
    amount: 2500,
    currency: "usd",
    productName: "Team plan",
    successUrl: `${location.origin}/paid?sessionId={CHECKOUT_SESSION_ID}`,
    cancelUrl: `${location.origin}/checkout/canceled`,
  }),
});

const { url, sessionId } = await response.json();
// Persist sessionId as a pending order before redirecting.
location.href = url;
  1. Create one durable checkout

    Send one stable Idempotency-Key per logical purchase and reuse it for retries, double clicks, and lost responses. Store the returned sessionId as pending reconciliation state before redirecting to Stripe.

  2. Verify payment server-side

    On return, capture and remove sessionId from the URL before analytics starts. A trusted Function calls /connect/session and verifies paid status, buyer, merchant, amount, and currency. Never grant value from client claims alone.

  3. Settle through policy

    Write one claim or settlement keyed by the Checkout session id, then grant the entitlement, credits, ownership, or balance through normal policy rules and invariants. A successful Stripe payment does not update app state by itself.

payments.acceptCrypto: direct USDC

Declare a seller-owned Solana address and the app can accept USDC directly into that wallet. Bounded creates an intent, verifies the finalized on-chain transfer, and records settlement; it never takes custody of the wallet, keys, or funds.

policy.json, direct USDC acceptance
{
  "payments": {
    "acceptCrypto": {
      "settleTo": "<seller-solana-address>",
      "token": "usdc",
      "environment": "production"
    }
  }
}
  • settleTo is a Solana address the seller controls. token is usdc; use the staging environment for devnet tests and production for mainnet.
  • The buyer pays the address from any compatible wallet. The app then submits the transaction signature for verification against the intended amount, token mint, environment, destination, finalization, and transaction success.
  • Each transaction signature can settle only one intent globally. Re-verifying a settled intent returns the same record; uncertainty or insufficient payment does not settle.
  • The direct-transfer rail has a 0 Bounded fee. Because funds move buyer-to-seller, Bounded has no post-settlement access to them.

Direct Stripe or another provider

Use a direct integration when you need the provider account in your name, provider-native webhooks, refunds and disputes, custom billing behavior, or no Bounded-managed payment fee. Store the API key in Bounded secrets and call the provider from a Function, never from the browser.

The settlement rule stays the same: verify the provider result in trusted code, write an idempotent claim, and grant app value through policy. You pay the provider directly and are responsible for its retries, webhook authentication, reconciliation, and compliance.

Subscription support and limits

The beta API can create simple destination subscriptions with recurring: { interval: "month" | "year" }. Registry split checkout remains one-off and cannot be combined with recurring billing.

  • Checkout returns a session id, not a subscription id. After the first invoice is paid, trusted code reads the completed session and stores the returned subscription id privately.
  • The initial session settlement covers the first Checkout session only. It does not book later renewal invoices automatically.
  • Later lifecycle state is poll-based through /connect/subscription. Bounded Pay does not fan out renewal, failure, cancellation, refund, or dispute webhooks to each app. Poll from a bounded schedule, cache for app reads, and back off on errors rather than polling on every render.
  • Cancellation at period end is available to the recorded buyer or merchant. There is no public Bounded Pay refund endpoint. Use a direct provider integration for provider webhooks, refunds, disputes, split subscriptions, per-renewal ledger entries, or custom lifecycle behavior.

App-user payments are not Bounded account billing

Money flowWho paysWhat it changes
Bounded Pay, direct USDC, or a direct providerThe app user or buyerSeller revenue; app entitlements change only after trusted, idempotent policy settlement
bounded billingThe app owner / Bounded developer accountThe Bounded plan and platform or AI/external-services buckets

A Pro or Team subscription and bucket top-ups fund the developer’s use of Bounded. They do not charge the app’s customers. Conversely, an end-user checkout does not replenish Bounded infrastructure or ctx.ai/ctx.services credit unless the app owner separately purchases that credit.