Skip to content
Cipay
Esc
navigateopen⌘Jpreview
On this page

Astro

Return the Cipay buyer handler's Web Response from an Astro server endpoint.

Astro server endpoints already receive a Web Request. Keep the SDK in server-rendered code and return the handler response directly.

Connect your application session

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

Configure the handler

Read the API key only in server code.

Mount the endpoint

Rate-limit and return the handler’s Web Response.

Render the catalog

Call the same-origin endpoint from Astro or a React island.

Verify payment separately

Use protected status for UI and signed webhooks for durable state.

Step 1: Connect your application session

readVerifiedSession is an application-owned auth adapter, not a Cipay SDK export. This example uses a Better Auth instance exported from ./auth.

import { auth } from "./auth";

export async function readVerifiedSession(request: Request) {
  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 your auth session does not include cipayCustomerId, load that mapping from your database after verifying the application user ID. Never accept it from browser input.

Step 2: Configure the handler

Configure the fixed locator and map only a verified application session to Cipay ownership.

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

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

export const cipayBuyerHandler = createCipayBuyerHandler({
  client,
  storefront: { locator: import.meta.env.CIPAY_STOREFRONT_LOCATOR },
  resolveCustomer: async (request) => {
    // Resolve ownership from the verified application session.
    const session = await readVerifiedSession(request);
    return session
      ? { subject: session.userId, customerId: session.cipayCustomerId }
      : null;
  },
});

Step 3: Mount and rate-limit the endpoint

Astro supplies the correct request type, so no conversion is required.

import type { APIRoute } from "astro";
import { cipayBuyerHandler } from "../../../lib/cipay-buyer";
import { allowCipayRequest, rateLimited } from "../../../lib/rate-limit";

export const ALL: APIRoute = ({ request }) =>
  allowCipayRequest(request) ? cipayBuyerHandler(request) : rateLimited();

The handler derives the application origin from Astro’s request and sends it to Cipay for the storefront approved-origin check.

Step 4: Render the fetched catalog

An Astro page can call its own endpoint. This uses SDK-exported contract types and never places an API key in page code.

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

const response = await fetch(
  new URL("/api/cipay/v1/products?pageSize=8", Astro.url),
);
const catalog = response.ok
  ? await response.json() as Page<BuyerProduct>
  : { items: [], nextCursor: null };
---

<h1>Products</h1>
<ul>
  {catalog.items.map((product) => <li>{product.name}</li>)}
</ul>

For a React island, configure CipayProvider endpoint="/api/cipay" and render ProductsList; it calls the same mounted endpoint.

Step 5: Verify the integration

  1. Load a published product through /api/cipay/v1/products.
  2. Preview a quote and create gateway-hosted checkout.
  3. Return the buyer without marking payment successful from the URL.
  4. Verify the raw-body webhook and store its signed event ID before effects.
  5. Read the authenticated customer’s order or invoice after the verified event.

Complete example

Use the complete Astro endpoint to see the client, session resolver, limiter, and handler assembled in one file.