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

# Core concepts

> Understand products, plans, customers, paywall checks, and usage tracking.

## Table of contents

* [Products and plans](#products-and-plans)
* [Plan types](#plan-types)
* [Meters](#meters)
* [Customer references](#customer-references)
* [Paywall protection flow](#paywall-protection-flow)
* [Purchase lifecycle](#purchase-lifecycle)
* [Authentication flow](#authentication-flow)
* [Usage tracking](#usage-tracking)
* [Next steps](#next-steps)

## Products and plans

### Products

A product represents the API, app, or MCP server you monetize.

* Product reference (`prd_...`)
* Name and description

Products are created in SolvaPay Console and are available for SDK integration by default.

```typescript theme={null}
const payable = solvaPay.payable({
  product: 'prd_myapi',
})
```

### Plans

A plan defines pricing and access for a product.

* Plan reference (`pln_...`)
* Type: `recurring`, `usage-based`, `one-time`, or `hybrid`
* Price and currency
* Features
* Status: `active`, `inactive`, or `archived`

Plans are configured as part of product setup. Customers select a plan during activation; the SDK resolves the correct plan from the customer's purchase automatically.

### Product-plan relationship

* One product can have multiple plans
* Plans are embedded in products
* One default plan per product
* Paywall checks verify plan access for a product

## Plan types

| Type        | Description                               | Best for                          |
| ----------- | ----------------------------------------- | --------------------------------- |
| Recurring   | Fixed price billed on a cycle             | SaaS subscriptions                |
| Usage-based | Pay-per-use tracked by a meter            | APIs and token services           |
| Hybrid      | Recurring fee plus usage overage          | Enterprise mixed workloads        |
| One-time    | Single purchase without recurring billing | Digital goods and lifetime access |

See [Plans overview](/plans/overview) for full plan behavior.

## Meters

Meters define what usage to measure.

* Default meters: `api_requests`, `requests`, `api_calls`, `tokens`
* MCP tool meters: `tool:{toolName}`
* Custom meters for business-specific usage

Plans can enforce limits using meter data.

See [Meters overview](/meters/overview) for details.

## Customer references

A customer reference (`customerRef`) maps your user identity to SolvaPay.

```typescript theme={null}
const customerRef = await solvaPay.ensureCustomer('user_123', 'user_123', {
  email: 'user@example.com',
  name: 'Jane Doe',
})
```

For HTTP adapters, pass customer identity in headers or auth context.

```typescript theme={null}
app.post('/tasks', payable.http(async req => {
  const customerRef = req.headers['x-customer-ref']
  return { ok: true, customerRef }
}))
```

### Create-or-link semantics

When you call `POST /v1/sdk/customers` (or `ensureCustomer` / `syncCustomer`) with an email that already exists under this provider and the caller supplies an `externalRef`, the API links the `externalRef` onto the existing customer instead of returning `409 Conflict`. This lets you idempotently adopt pre-existing SolvaPay customers when wiring up your own auth layer for the first time. Callers without `externalRef` still get `409` on duplicate email so the behavior is explicit.

### Update a customer

Use `PATCH /v1/sdk/customers/:reference` to backfill or change `externalRef`, `name`, or `email`. Only the fields you supply are modified.

```typescript theme={null}
await fetch(`https://api.solvapay.com/v1/sdk/customers/${ref}`, {
  method: 'PATCH',
  headers: { Authorization: `Bearer ${process.env.SOLVAPAY_SECRET_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ externalRef: 'auth_user_12345' }),
})
```

## Data helpers

Three read-only endpoints power customer-facing UI without bloating `check_purchase`:

* **`GET /v1/sdk/platform-config`** — browser-safe platform values (today: the SolvaPay Stripe publishable key used to initialize the Stripe.js client). Resolves against the sandbox or live environment matching your secret key. Call it once on page load before mounting `<PaymentForm>`; additions will land here rather than in `/sdk/merchant`.
* **`GET /v1/sdk/merchant`** — provider identity safe to render in checkout and mandate copy: `name`, `legalName`, `supportContact`, `termsUrl`, `privacyUrl`, `iconUrl` (square app icon), and `logoUrl` (absolute URL resolved against `ASSETS_BASE_URL`). Used by SDK components like `MandateText` and `CheckoutSummary`.
* **`GET /v1/sdk/payment-method?customerRef=…`** — returns `{ kind: 'card', brand, last4, expMonth, expYear }` or `{ kind: 'none' }`. The card brand and last4 are mirrored onto the Customer by the `payment_intent.succeeded` webhook, so this endpoint reads from SolvaPay's database with no Stripe round-trip. Safe to poll alongside `check_purchase`.

All three are exposed as route-wrapper helpers in `@solvapay/next` (`getMerchant`, `getProduct`, `getPaymentMethod`), as fetch handlers in `@solvapay/server/fetch`, and as MCP tools in the default `createSolvaPayMcpServer` tool surface.

## Paywall protection flow

When your handler is wrapped with `payable()`, the SDK:

1. Resolves customer reference
2. Checks purchase status
3. Checks usage limits
4. Executes business logic when allowed
5. Tracks usage
6. Returns a paywall response when blocked

### Example blocked response

```typescript theme={null}
{
  error: 'PaywallError',
  message: 'Purchase required',
  checkoutUrl: 'https://checkout.solvapay.com/...',
}
```

## Purchase lifecycle

Purchase states:

* **Active** — customer has access
* **Pending cancellation** — active with `cancelledAt` set; access continues until period end, can be reactivated
* **Cancelled** — cancel confirmed at period end
* **Expired** — purchase ended (period elapsed or plan switch)
* **Past due** — payment failed; may be suspended

### Cancel and reactivate renewal

```typescript theme={null}
import { cancelRenewal, reactivateRenewal } from '@solvapay/next'

// Cancel auto-renewal (access continues until period end)
await cancelRenewal(request, { purchaseRef: 'pur_...', reason: 'Too expensive' })

// Undo cancellation while still in active period
await reactivateRenewal(request, { purchaseRef: 'pur_...' })
```

### Activate a plan directly

Activate a product for a customer on a specific plan without checkout. Handles free plans, credit-based activation, usage-based (PAYG) plans, and plan switching.

```typescript theme={null}
import { activatePlan } from '@solvapay/next'

const result = await activatePlan(request, {
  productRef: 'prd_...',
  planRef: 'pln_...',
})
// result.status: 'activated' | 'already_active' | 'topup_required' | 'payment_required' | 'invalid'
```

Activation policy by plan type:

* **Free** — always `activated`.
* **Usage-based (PAYG)** — `activated` immediately at zero balance. The customer can start using the product and pay per action; top-up is a separate optional step via `createTopupPaymentIntent`.
* **Recurring / hybrid** — return `topup_required` or `payment_required` when the customer has no credits and no card on file. Route them to checkout before granting access.

When the customer already has an active purchase on a different plan, `activatePlan` automatically expires the old purchase and creates a new one.

See [Purchase lifecycle management](/sdks/typescript/guides/purchase-management) for the full guide.

## Authentication flow

You keep your existing auth and map identities to SolvaPay customers.

Common pattern:

1. Extract user ID from your auth layer
2. Use ID as `customerRef`
3. Sync email/name as needed
4. Run purchase checks

## Usage tracking

Use `payable()` for automatic usage tracking, or record usage directly.

```typescript theme={null}
await solvaPay.trackUsage({
  customerRef: 'user_123',
  actionType: 'api_call',
  units: 1,
  outcome: 'success',
  productRef: 'prd_P9Q0R1S2',
})
```

Usage-based and hybrid plans enforce limits from meter data.

## Next steps

* [Quick start](./quick-start)
* [CLI setup](./cli)
* [Installation](./installation)
* [Plans overview](/plans/overview)
* [Meters overview](/meters/overview)
