Hono Integration

This guide covers how to integrate mavi pay with a Hono application. Hono is a lightweight web framework that runs on Cloudflare Workers, Deno, Bun, and Node.js. The integration uses @mavi-pay/sdk for API calls and webhook verification.


Installation

npm install @mavi-pay/sdk hono

Setup

Create a mavi pay client instance.

// 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',
})

Webhook Handler

Set up a route to receive and verify mavi pay webhooks.

// src/index.ts
import { Hono } from 'hono'
import { verifyWebhookSignature } from '@mavi-pay/sdk'
import { maviPay } from './lib/mavi-pay'

const app = new Hono()

app.post('/webhooks/mavi-pay', async (c) => {
  const body = await c.req.text()
  const signature = c.req.header('x-mavi-pay-signature')

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

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

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

  const event = JSON.parse(body)

  switch (event.type) {
    case 'checkout.completed': {
      const { customer_email, product_id, order_id } = event.data
      // Fulfil the order: grant access, send confirmation, etc.
      console.log(`Order ${order_id} completed for ${customer_email}`)
      break
    }
    case 'subscription.cancelled': {
      const { customer_id, product_id } = event.data
      // Revoke access for the cancelled subscription
      console.log(`Subscription cancelled for customer ${customer_id}`)
      break
    }
  }

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

export default app

Creating a Checkout Session

Generate a checkout URL from your Hono API and redirect the user.

app.post('/api/checkout', async (c) => {
  const { productId, customerEmail } = await c.req.json()

  const checkout = await maviPay.checkouts.create({
    productId,
    customerEmail,
    successUrl: 'https://yourapp.com/success',
  })

  return c.json({ checkoutUrl: checkout.url })
})

Listing Products

Expose your mavi pay products through a Hono endpoint.

app.get('/api/products', async (c) => {
  const products = await maviPay.products.list()
  return c.json(products)
})

Middleware: Require Purchase

Create a Hono middleware to protect routes behind a purchase check.

import { createMiddleware } from 'hono/factory'

const requirePurchase = (productId: string) =>
  createMiddleware(async (c, next) => {
    const customerId = c.get('customerId') // Set by your auth middleware

    if (!customerId) {
      return c.json({ error: 'Unauthorized' }, 401)
    }

    const orders = await maviPay.orders.list({
      customerId,
      productId,
    })

    const hasAccess = orders.items.some(
      (order) => order.status === 'paid' || order.status === 'active'
    )

    if (!hasAccess) {
      return c.json({ error: 'Purchase required' }, 403)
    }

    await next()
  })

// Use it on protected routes
app.get('/api/premium-content', requirePurchase('prod_xxxxxxxx'), async (c) => {
  return c.json({ content: 'This is premium content.' })
})

Environment Variables

MAVI_PAY_ACCESS_TOKEN=your_api_key_here
MAVI_PAY_WEBHOOK_SECRET=your_webhook_secret_here

For Cloudflare Workers, set these as secrets:

wrangler secret put MAVI_PAY_ACCESS_TOKEN
wrangler secret put MAVI_PAY_WEBHOOK_SECRET

Deployment

Hono works across multiple runtimes. The mavi pay SDK is compatible with all of them:

  • Cloudflare Workers -- wrangler deploy
  • Bun -- bun run src/index.ts
  • Deno -- deno run --allow-net src/index.ts
  • Node.js -- node dist/index.js

Next Steps