> ## 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.

# Next.js Integration Guide

> This guide shows you how to integrate SolvaPay SDK with Next.js App Router to protect your API routes and add payment flows to your application.

## Table of Contents

* [Installation](#installation)
* [Basic Setup](#basic-setup)
* [Protecting API Routes](#protecting-api-routes)
* [Server Components](#server-components)
* [Client Components](#client-components)
* [Middleware Setup](#middleware-setup)
* [Payment Flow Integration](#payment-flow-integration)
* [Complete Example](#complete-example)

## Installation

Install the required packages:

```bash theme={null}
npm install @solvapay/server @solvapay/next @solvapay/react @solvapay/react-supabase
# or
pnpm add @solvapay/server @solvapay/next @solvapay/react @solvapay/react-supabase
```

## Basic Setup

### 1. Environment Variables

Create a `.env.local` file:

```env theme={null}
SOLVAPAY_SECRET_KEY=sk_...
NEXT_PUBLIC_SOLVAPAY_PRODUCT=prd_YOUR_PRODUCT_ID
```

### 2. Initialize SolvaPay Client

Create a shared SolvaPay instance:

```typescript theme={null}
// lib/solvapay.ts
import { createSolvaPay } from '@solvapay/server'

export const solvaPay = createSolvaPay({
  apiKey: process.env.SOLVAPAY_SECRET_KEY,
})
```

## Protecting API Routes

### Basic API Route Protection

Use the `payable.next()` adapter to protect Next.js App Router API routes:

```typescript theme={null}
// app/api/tasks/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { solvaPay } from '@/lib/solvapay'

const payable = solvaPay.payable({
  product: process.env.NEXT_PUBLIC_SOLVAPAY_PRODUCT!,
})

// Your business logic
async function createTask(req: NextRequest) {
  const body = await req.json()
  const { title } = body

  return { success: true, task: { title } }
}

// Protect the route
export const POST = payable.next(createTask)
```

### Using Next.js Helpers

The `@solvapay/next` package provides route-wrapper helpers that handle auth, body parsing, error formatting, and cache invalidation. Every wrapper returns `Promise<NextResponse>` — `return` it directly from your route handler.

<Warning>
  **Breaking change (SDK 1.1):** wrappers such as `checkPurchase`, `createPaymentIntent`, `processPaymentIntent`, `activatePlan`, `cancelRenewal`, `reactivateRenewal`, `createCheckoutSession`, `createCustomerSession`, `syncCustomer`, `listPlans`, `getMerchant`, `getProduct`, `getPaymentMethod`, `getCustomerBalance`, and `trackUsage` now **always** return `Promise<NextResponse>`. Earlier versions returned `NextResponse | data`, so if you still have `result instanceof NextResponse ? result : NextResponse.json(result)` in your handlers, delete that branch.

  Unchanged: `getAuthenticatedUser`, `getCustomerReference`, and `syncCustomer` helpers that return raw user/customer data stay as-is.
</Warning>

```typescript theme={null}
// app/api/check-purchase/route.ts
import { checkPurchase } from '@solvapay/next'

export async function GET(request: Request) {
  return checkPurchase(request)
}
```

### Available Helper Functions

#### Check Purchase

```typescript theme={null}
// app/api/check-purchase/route.ts
import { checkPurchase } from '@solvapay/next'

export async function GET(request: Request) {
  return checkPurchase(request)
}
```

#### Create Payment Intent

```typescript theme={null}
// app/api/create-payment-intent/route.ts
import { createPaymentIntent } from '@solvapay/next'

export async function POST(request: Request) {
  const { planRef, productRef } = await request.json()
  return createPaymentIntent(request, { planRef, productRef })
}
```

#### Process Payment

```typescript theme={null}
// app/api/process-payment/route.ts
import { processPaymentIntent } from '@solvapay/next'

export async function POST(request: Request) {
  const { paymentIntentId, productRef, planRef } = await request.json()
  return processPaymentIntent(request, { paymentIntentId, productRef, planRef })
}
```

#### Create Checkout Session

```typescript theme={null}
// app/api/create-checkout-session/route.ts
import { createCheckoutSession } from '@solvapay/next'

export async function POST(request: Request) {
  const { planRef, productRef } = await request.json()
  return createCheckoutSession(request, { planRef, productRef })
}
```

#### Create Customer Portal Session

```typescript theme={null}
// app/api/create-customer-session/route.ts
import { createCustomerSession } from '@solvapay/next'

export async function POST(request: Request) {
  return createCustomerSession(request)
}
```

#### Sync Customer

```typescript theme={null}
// app/api/sync-customer/route.ts
import { syncCustomer } from '@solvapay/next'

export async function POST(request: Request) {
  return syncCustomer(request)
}
```

#### Cancel Renewal

```typescript theme={null}
// app/api/cancel-renewal/route.ts
import { cancelRenewal } from '@solvapay/next'

export async function POST(request: Request) {
  const { purchaseRef, reason } = await request.json()
  return cancelRenewal(request, { purchaseRef, reason })
}
```

#### Reactivate Renewal

```typescript theme={null}
// app/api/reactivate-renewal/route.ts
import { reactivateRenewal } from '@solvapay/next'

export async function POST(request: Request) {
  const { purchaseRef } = await request.json()
  return reactivateRenewal(request, { purchaseRef })
}
```

#### Activate Plan

```typescript theme={null}
// app/api/activate-plan/route.ts
import { activatePlan } from '@solvapay/next'

export async function POST(request: Request) {
  const { productRef, planRef } = await request.json()
  return activatePlan(request, { productRef, planRef })
}
```

Response body: `{ status, purchaseRef?, checkoutUrl?, creditBalance?, ... }`. Usage-based plans now activate eagerly at the plan step (no balance gate). See [Purchase lifecycle management](./purchase-management) for all status values.

#### List Plans

```typescript theme={null}
// app/api/list-plans/route.ts
import { listPlans } from '@solvapay/next'

export async function GET(request: Request) {
  return listPlans(request)
}
```

Response body: `{ plans, productRef }`. Pass `?productRef=prd_...` as a query parameter.

#### Merchant, Product, and Payment Method

```typescript theme={null}
// app/api/merchant/route.ts
import { getMerchant } from '@solvapay/next'

export async function GET(request: Request) {
  return getMerchant(request)
}
```

```typescript theme={null}
// app/api/product/route.ts
import { getProduct } from '@solvapay/next'

export async function GET(request: Request) {
  return getProduct(request)
}
```

```typescript theme={null}
// app/api/payment-method/route.ts
import { getPaymentMethod } from '@solvapay/next'

export async function GET(request: Request) {
  return getPaymentMethod(request)
}
```

* `getMerchant` returns `{ name, iconUrl, logoUrl, termsUrl, privacyUrl, ... }` — use it to brand checkout and mandate copy.
* `getProduct` returns the product with public plans for the current authenticated customer.
* `getPaymentMethod` returns `{ kind: 'card', brand, last4, expMonth, expYear } | { kind: 'none' }`, mirrored from the last successful `payment_intent.succeeded` webhook. Safe to poll alongside `check-purchase`.

#### Customer Balance

```typescript theme={null}
// app/api/customer-balance/route.ts
import { getCustomerBalance } from '@solvapay/next'

export async function GET(request: Request) {
  return getCustomerBalance(request)
}
```

#### Track Usage

```typescript theme={null}
// app/api/track-usage/route.ts
import { trackUsage } from '@solvapay/next'

export async function POST(request: Request) {
  const body = await request.json()
  return trackUsage(request, body)
}
```

## Server Components

In Server Components, reach for the `*Core` primitives from `@solvapay/server` instead of the route-wrapper helpers — the wrappers always return `NextResponse`, which isn't useful when you want to read data.

```typescript theme={null}
// app/dashboard/page.tsx
import { checkPurchaseCore, isErrorResult } from '@solvapay/server';
import { cookies } from 'next/headers';
import { solvaPay } from '@/lib/solvapay';

export default async function DashboardPage() {
  const cookieStore = await cookies();
  const headers = new Headers();

  const authToken = cookieStore.get('auth-token');
  if (authToken) {
    headers.set('authorization', `Bearer ${authToken.value}`);
  }

  const request = new Request('http://localhost', { headers });
  const result = await checkPurchaseCore(request, { solvaPay });

  if (isErrorResult(result)) {
    return <div>Error checking purchase</div>;
  }

  return (
    <div>
      <h1>Dashboard</h1>
      {result.hasPaidPurchase ? (
        <p>You have an active purchase!</p>
      ) : (
        <p>Please subscribe to access premium features.</p>
      )}
    </div>
  );
}
```

## Client Components

Use React hooks and components for client-side payment flows:

```typescript theme={null}
// app/checkout/page.tsx
'use client';

import { PaymentForm } from '@solvapay/react';
import { useRouter } from 'next/navigation';

export default function CheckoutPage() {
  const router = useRouter();

  return (
    <PaymentForm
      planRef="pln_premium"
      productRef={process.env.NEXT_PUBLIC_SOLVAPAY_PRODUCT!}
      onSuccess={() => {
        router.push('/dashboard');
      }}
      onError={(error) => {
        console.error('Payment error:', error);
      }}
    />
  );
}
```

## Middleware Setup

### Authentication Middleware

Set up authentication middleware (implemented in `proxy.ts`) to extract user
information and make it available to API routes:

```typescript theme={null}
// proxy.ts
import { NextRequest, NextResponse } from 'next/server'
import { getUserIdFromRequest } from '@solvapay/auth'

export async function proxy(request: NextRequest) {
  // Extract user ID from auth token, session, etc.
  const authToken = request.headers.get('authorization')?.replace('Bearer ', '')

  if (authToken) {
    // Verify token and extract user ID
    // This is a simplified example - use your actual auth logic
    const userId = await extractUserIdFromToken(authToken)

    if (userId) {
      // Add user ID to request headers for SolvaPay
      const requestHeaders = new Headers(request.headers)
      requestHeaders.set('x-user-id', userId)

      return NextResponse.next({
        request: {
          headers: requestHeaders,
        },
      })
    }
  }

  return NextResponse.next()
}

export const config = {
  matcher: '/api/:path*',
}
```

### Supabase Auth Middleware

If using Supabase, use the provided middleware helper (exported from
`@solvapay/next/middleware` and used from `proxy.ts`):

```typescript theme={null}
// proxy.ts
import { createSupabaseAuthMiddleware } from '@solvapay/next/middleware'
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
)

export const proxy = createSupabaseAuthMiddleware({
  supabase,
  // Optional: customize which routes to protect
  protectedPaths: ['/api/protected'],
})
```

## Payment Flow Integration

### 1. Set Up Provider

Wrap your app with `SolvaPayProvider`:

```typescript theme={null}
// app/layout.tsx
import { SolvaPayProvider } from '@solvapay/react';
import { createSupabaseAuthAdapter } from '@solvapay/react-supabase';
import { supabase } from '@/lib/supabase';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  // Optional: Use Supabase auth adapter
  const supabaseAdapter = createSupabaseAuthAdapter({ client: supabase });

  return (
    <html>
      <body>
        <SolvaPayProvider
          config={{
            // Optional: Custom API routes (defaults work out of the box)
            api: {
              checkPurchase: '/api/check-purchase',
              createPayment: '/api/create-payment-intent',
              processPayment: '/api/process-payment',
            },
            // Optional: Supabase auth adapter
            auth: { adapter: supabaseAdapter },
          }}
        >
          {children}
        </SolvaPayProvider>
      </body>
    </html>
  );
}
```

### 2. Create API Routes

Set up the required API routes using Next.js helpers — each is a one-liner because every wrapper returns `Promise<NextResponse>`:

```typescript theme={null}
// app/api/check-purchase/route.ts
import { checkPurchase } from '@solvapay/next'

export async function GET(request: Request) {
  return checkPurchase(request)
}
```

```typescript theme={null}
// app/api/create-payment-intent/route.ts
import { createPaymentIntent } from '@solvapay/next'

export async function POST(request: Request) {
  const { planRef, productRef } = await request.json()
  return createPaymentIntent(request, { planRef, productRef })
}
```

```typescript theme={null}
// app/api/process-payment/route.ts
import { processPaymentIntent } from '@solvapay/next'

export async function POST(request: Request) {
  const { paymentIntentId, productRef, planRef } = await request.json()
  return processPaymentIntent(request, { paymentIntentId, productRef, planRef })
}
```

### 3. Use Payment Components

Use React components and hooks in your pages:

```typescript theme={null}
// app/checkout/page.tsx
'use client';

import { PaymentForm, usePurchase } from '@solvapay/react';
import { useRouter } from 'next/navigation';

export default function CheckoutPage() {
  const router = useRouter();
  const { hasPaidPurchase, loading } = usePurchase();

  if (loading) {
    return <div>Loading...</div>;
  }

  if (hasPaidPurchase) {
    return <div>You already have an active purchase!</div>;
  }

  return (
    <div>
      <h1>Subscribe to Premium</h1>
      <PaymentForm
        planRef="pln_premium"
      productRef={process.env.NEXT_PUBLIC_SOLVAPAY_PRODUCT!}
        onSuccess={() => {
          router.push('/dashboard');
        }}
      />
    </div>
  );
}
```

## Complete Example

Here's a complete Next.js application with SolvaPay integration:

### Project Structure

```
app/
├── layout.tsx
├── page.tsx
├── dashboard/
│   └── page.tsx
├── checkout/
│   └── page.tsx
└── api/
    ├── tasks/
    │   └── route.ts
    ├── check-purchase/
    │   └── route.ts
    ├── create-payment-intent/
    │   └── route.ts
    ├── process-payment/
    │   └── route.ts
    ├── cancel-renewal/
    │   └── route.ts
    ├── reactivate-renewal/
    │   └── route.ts
    ├── activate-plan/
    │   └── route.ts
    └── list-plans/
        └── route.ts
lib/
└── solvapay.ts
proxy.ts
```

### Root Layout

```typescript theme={null}
// app/layout.tsx
import { SolvaPayProvider } from '@solvapay/react';
import { createSupabaseAuthAdapter } from '@solvapay/react-supabase';
import { supabase } from '@/lib/supabase';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  const supabaseAdapter = createSupabaseAuthAdapter({ client: supabase });

  return (
    <html>
      <body>
        <SolvaPayProvider config={{ auth: { adapter: supabaseAdapter } }}>
          {children}
        </SolvaPayProvider>
      </body>
    </html>
  );
}
```

### Protected API Route

```typescript theme={null}
// app/api/tasks/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { solvaPay } from '@/lib/solvapay'

const payable = solvaPay.payable({
  product: process.env.NEXT_PUBLIC_SOLVAPAY_PRODUCT!,
})

async function createTask(req: NextRequest) {
  const body = await req.json()
  const { title } = body

  return { success: true, task: { title, id: Date.now().toString() } }
}

export const POST = payable.next(createTask)
```

### Checkout Page

```typescript theme={null}
// app/checkout/page.tsx
'use client';

import { PaymentForm } from '@solvapay/react';
import { useRouter } from 'next/navigation';

export default function CheckoutPage() {
  const router = useRouter();

  return (
    <div className="container mx-auto p-8">
      <h1 className="text-3xl font-bold mb-8">Subscribe to Premium</h1>
      <PaymentForm
        planRef="pln_premium"
        productRef={process.env.NEXT_PUBLIC_SOLVAPAY_PRODUCT!}
        onSuccess={() => router.push('/dashboard')}
      />
    </div>
  );
}
```

### Dashboard Page

```typescript theme={null}
// app/dashboard/page.tsx
'use client';

import { usePurchase } from '@solvapay/react';
import Link from 'next/link';

export default function DashboardPage() {
  const { hasPaidPurchase, loading, activePurchase } = usePurchase();

  if (loading) {
    return <div>Loading...</div>;
  }

  return (
    <div className="container mx-auto p-8">
      <h1 className="text-3xl font-bold mb-8">Dashboard</h1>

      {hasPaidPurchase ? (
        <div>
          <p className="text-green-600">You have an active purchase!</p>
          {activePurchase && (
            <p>Product: {activePurchase.productName}</p>
          )}
        </div>
      ) : (
        <div>
          <p className="text-gray-600">Please subscribe to access premium features.</p>
          <Link href="/checkout" className="text-blue-600 underline">
            Go to Checkout
          </Link>
        </div>
      )}
    </div>
  );
}
```

## Cache Management

The `@solvapay/next` package includes purchase caching to reduce API calls:

```typescript theme={null}
import { clearPurchaseCache, getPurchaseCacheStats } from '@solvapay/next'

// Clear cache for a specific user
await clearPurchaseCache(userId)

// Clear all caches
await clearAllPurchaseCache()

// Get cache statistics
const stats = await getPurchaseCacheStats()
console.log(stats)
```

## Best Practices

1. **Use Environment Variables**: Store API keys and configuration in `.env.local`.

2. **Separate API Routes**: Keep API routes separate from page routes for better organization.

3. **Error Handling**: Helpers always return `NextResponse` (including error responses). For Server Component / RSC code paths where you need raw data, use the `*Core` primitives from `@solvapay/server` and check with `isErrorResult`.

4. **Type Safety**: Use TypeScript for better type safety.

5. **Cache Management**: Use purchase caching to reduce API calls and improve performance.

6. **Middleware**: Set up authentication middleware to extract user information early.

## Next Steps

* [React Integration Guide](./react) - Learn about React components and hooks
* [Usage Events](./usage-events) - Track and bill usage-based features
* [Webhooks](./webhooks) - Handle payment and subscription events
* [API Reference](/sdks/typescript/intro) - Full API documentation
