Skip to content
Cipay
Esc
navigateopen⌘Jpreview
On this page

Hono

Pass Hono's native Web Request to the Cipay buyer handler.

Hono exposes the raw Web Request as context.req.raw, so no request conversion is needed.

Connect your application session

Adapt your existing server auth to the customer shape Cipay needs.

Configure the handler

Keep the API key and locator in server configuration.

Mount the route

Rate-limit, then pass context.req.raw to the handler.

Call from the storefront

Use React components or same-origin fetch.

Verify the flow

Test catalog, checkout, status, and webhooks independently.

Step 1: Connect your application session

readVerifiedSession is application code, not a Cipay SDK export. This example uses a Better Auth instance exported from ./auth and assumes the verified session includes your stored Cipay customer mapping.

import { auth } from "./auth";

export async function readVerifiedSession(request: Request) {
  // Hono exposes standard headers, so the auth adapter stays framework-light.
  const session = await auth.api.getSession({ headers: request.headers });
  const cipayCustomerId = session?.user.cipayCustomerId;

  if (!session || typeof cipayCustomerId !== "string") return null;

  return { userId: session.user.id, cipayCustomerId };
}

If the mapping is not stored in the session, query it by the verified application user ID. Do not read a Cipay customer ID from the request body or query string.

Step 2: Configure the handler

Create the handler with a fixed storefront locator. resolveCustomer trusts only your verified session and stored customer mapping.

import {
  createCipayBuyerHandler,
  createCipayClient,
} from "@cipay/client-sdk/api";
import { readVerifiedSession } from "./cipay-session";

const client = createCipayClient({
  apiKey: process.env.CIPAY_SANDBOX_API_KEY!,
  mode: "sandbox",
});

export const cipayBuyerHandler = createCipayBuyerHandler({
  client,
  storefront: {
    locator: process.env.CIPAY_STOREFRONT_LOCATOR!,
  },
  resolveCustomer: async (request) => {
    // The application session, not browser input, establishes ownership.
    const session = await readVerifiedSession(request);
    return session
      ? { subject: session.userId, customerId: session.cipayCustomerId }
      : null;
  },
});

Step 3: Mount the Hono route

Hono already exposes a Web Request. Apply the rate limiter before Cipay reads a body or makes an upstream call.

import { Hono } from "hono";
import { cipayBuyerHandler } from "./cipay-buyer";
import { allowCipayRequest, rateLimited } from "./rate-limit";

const app = new Hono();

app.all("/api/cipay/*", (context) => {
  // Rate-limit before the handler reads a body or calls Cipay.
  if (!allowCipayRequest(context.req.raw)) return rateLimited();
  return cipayBuyerHandler(context.req.raw);
});

export default app;

The handler derives the origin from context.req.raw, enforces same-origin browser writes, and lets Cipay compare that origin with the storefront’s approved origins.

Step 4: Call the mounted API

Your browser code calls the Hono route, not Cipay directly. This plain client-side fetch lists the first eight published products through GET /api/cipay/v1/products.

import type { BuyerProduct, Page } from "@cipay/client-sdk/api";

export async function loadCatalog(): Promise<Page<BuyerProduct>> {
  const response = await fetch("/api/cipay/v1/products?pageSize=8", {
    credentials: "same-origin",
    cache: "no-store",
  });

  if (!response.ok) throw new Error("Could not load the catalog");
  return await response.json() as Page<BuyerProduct>;
}

For a React storefront, use CipayProvider endpoint="/api/cipay" and useProductsList instead; it sends this same request and provides TanStack Query caching and state.

Step 5: Verify the journey

List a published product, preview its offer, create hosted checkout, then receive the signed webhook separately. Persist the event ID before processing and read the owned order or invoice through the authenticated customer mapping.

Complete example

Use the complete Hono example to see the configuration, session resolver, rate limiter, and route assembled in one file.