Skip to content
Cipay
Esc
↑↓navigate↵open⌘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 an account mapping when needed

Guest catalog and Storefront checkout do not need an authenticated Cipay customer. Add your server auth mapping only for account operations.

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({
  basePath: "/api/cipay/buyer",
  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/buyer/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/buyer" and render ProductsList; it calls the same mounted endpoint.

Step 5: Verify the integration

  1. Load a published product through /api/cipay/buyer/products.
  2. Open the configured Storefront URL for the selected product and offer, or use the API-managed checkout guide when your server needs to create the session.
  3. Treat the return page as buyer feedback, not payment confirmation.
  4. Verify the signed webhook, atomically claim it with persistOnce, and process the durable job idempotently.
  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.