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
Configure the handler
Mount the endpoint
Render the catalog
Verify payment separately
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
- Load a published product through
/api/cipay/v1/products. - Preview a quote and create gateway-hosted checkout.
- Return the buyer without marking payment successful from the URL.
- Verify the raw-body webhook and store its signed event ID before effects.
- 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.