Next.js Integration

This guide covers how to integrate mavi pay into a Next.js application using @mavi-pay/sdk. You will set up the SDK, create a checkout flow, and handle post-payment events.


Installation

npm install @mavi-pay/sdk

Or with your preferred package manager:

pnpm add @mavi-pay/sdk
yarn add @mavi-pay/sdk

Configuration

Create a mavi pay client instance. This is typically done in a shared utility file.

// lib/mavi-pay.ts
import { MaviPay } from '@mavi-pay/sdk'

export const maviPay = new MaviPay({
  accessToken: process.env.MAVI_PAY_ACCESS_TOKEN!,
  baseUrl: 'https://api.mavifinans.sh',
})

Generate your access token in the merchant dashboard under Settings > API Keys.


Provider Setup

Wrap your application with the MaviPayProvider to make the SDK available throughout your component tree.

// app/layout.tsx
import { MaviPayProvider } from '@mavi-pay/sdk/react'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <MaviPayProvider
          accessToken={process.env.NEXT_PUBLIC_MAVI_PAY_ACCESS_TOKEN!}
          baseUrl="https://api.mavifinans.sh"
        >
          {children}
        </MaviPayProvider>
      </body>
    </html>
  )
}

Checkout Example

Client-Side Checkout Button

Use the useCheckout hook to initiate a checkout session from a client component.

// components/BuyButton.tsx
'use client'

import { useCheckout } from '@mavi-pay/sdk/react'

export function BuyButton({ productId }: { productId: string }) {
  const { checkout, isLoading } = useCheckout()

  const handleClick = async () => {
    await checkout({
      productId,
      successUrl: `${window.location.origin}/success`,
    })
  }

  return (
    <button onClick={handleClick} disabled={isLoading}>
      {isLoading ? 'Redirecting...' : 'Buy Now'}
    </button>
  )
}

Embedded Checkout

Render the checkout form inline on your page without redirecting.

// app/buy/page.tsx
'use client'

import { MaviPayCheckout } from '@mavi-pay/sdk/react'

export default function BuyPage() {
  return (
    <div className="mx-auto max-w-lg py-12">
      <h1 className="mb-6 text-2xl font-bold">Complete Your Purchase</h1>
      <MaviPayCheckout
        productId="prod_xxxxxxxx"
        onSuccess={(result) => {
          window.location.href = `/success?order=${result.orderId}`
        }}
        onError={(error) => {
          console.error('Checkout failed:', error.message)
        }}
        theme="dark"
      />
    </div>
  )
}

Server-Side: Listing Products

Fetch products from the mavi pay API in a Server Component.

// app/products/page.tsx
import { maviPay } from '@/lib/mavi-pay'

export default async function ProductsPage() {
  const products = await maviPay.products.list()

  return (
    <div>
      <h1>Products</h1>
      <ul>
        {products.items.map((product) => (
          <li key={product.id}>
            <h2>{product.name}</h2>
            <p>{product.description}</p>
            <p>
              {product.price.amount / 100} {product.price.currency}
            </p>
          </li>
        ))}
      </ul>
    </div>
  )
}

Handling Webhooks

Set up an API route to receive mavi pay webhook events.

// app/api/mavi-pay/webhook/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { verifyWebhookSignature } from '@mavi-pay/sdk'

export async function POST(request: NextRequest) {
  const body = await request.text()
  const signature = request.headers.get('x-mavi-pay-signature')

  if (!signature) {
    return NextResponse.json({ error: 'Missing signature' }, { status: 401 })
  }

  const isValid = verifyWebhookSignature(
    body,
    signature,
    process.env.MAVI_PAY_WEBHOOK_SECRET!
  )

  if (!isValid) {
    return NextResponse.json({ error: 'Invalid signature' }, { status: 401 })
  }

  const event = JSON.parse(body)

  switch (event.type) {
    case 'checkout.completed':
      // Handle successful purchase
      break
    case 'subscription.created':
      // Handle new subscription
      break
    case 'subscription.cancelled':
      // Handle cancellation
      break
  }

  return NextResponse.json({ received: true })
}

Register your webhook URL in the dashboard under Settings > Webhooks.


Environment Variables

Add these to your .env.local:

# Server-side (never expose to the client)
MAVI_PAY_ACCESS_TOKEN=your_api_key_here
MAVI_PAY_WEBHOOK_SECRET=your_webhook_secret_here

# Client-side (safe to expose)
NEXT_PUBLIC_MAVI_PAY_ACCESS_TOKEN=your_public_key_here

Next Steps