Drop-in checkout (hosted Pay)
The fastest path to a paid order: create a checkout session on your server, then redirect the customer to the URL it returns. Cimplify hosts the entire checkout UI at `pay.cimplify.io/s/<sessionId>` (auth, address, payment method, compliance). You get a webhook (or success-URL redirect) when payment lands.
Flow
1. Customer adds items to cart on your storefront.
2. Your server hits POST /v1/checkout/sessions with the cart_id and a secret key.
3. The API returns { id, url, status, expires_at }.
4. You 302 the customer to `url` (or open it in a popup).
5. Customer completes payment on pay.cimplify.io.
6. Cimplify redirects them to your success_url with ?order_id=... &session_id=...
AND fires order.completed / payment.succeeded webhooks.Create a session
Authenticate with a secret key (csk_…). The session is bound to whatever business that key belongs to.
curl https://api.cimplify.io/v1/checkout/sessions \
-H "Authorization: Bearer $CIMPLIFY_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"cart_id": "crt_01J5...",
"public_key": "cpk_live_…",
"success_url": "https://store.example.com/orders/thanks",
"cancel_url": "https://store.example.com/cart",
"default_order_type": "delivery",
"submit_label": "Pay GH₵29.99"
}'Request body
| Field | Type | Required | Notes |
|---|---|---|---|
cart_id | string | yes | An existing cart belonging to the same business as the API key. |
public_key | string | no | The cpk_… key to embed in the hosted page; defaults to the business's primary public key. |
order_types | string[] | no | Subset of delivery | pickup | dine_in. Defaults to whatever the business supports. |
default_order_type | string | no | Pre-select an order type. |
currency | string | no | ISO 4217. Defaults to the cart currency. |
success_url | string | no | Cimplify redirects here on success with order_id and session_id query params. |
cancel_url | string | no | Shown as a "Return to store" button on the hosted page. |
appearance | object | no | ElementAppearance. |
submit_label | string | no | Override the Pay button copy. |
metadata | object | no | Free-form JSON echoed on the resulting order. |
Response
{
"id": "cs_01J5BGM...",
"url": "https://pay.cimplify.io/s/cs_01J5BGM...",
"status": "open",
"expires_at": "2026-05-07T17:00:00Z"
}Redirect the customer
// app/api/checkout/route.ts
import { redirect } from "next/navigation";
export async function POST(request: Request) {
const { cartId } = await request.json();
const r = await fetch("https://api.cimplify.io/v1/checkout/sessions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CIMPLIFY_SECRET_KEY!}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
cart_id: cartId,
success_url: `${process.env.STORE_URL}/orders/thanks`,
cancel_url: `${process.env.STORE_URL}/cart`,
}),
});
if (!r.ok) {
return Response.json({ error: await r.text() }, { status: 500 });
}
const { url } = await r.json() as { url: string };
redirect(url);
}Reading the success redirect
Cimplify appends order_id and session_id to your success_url. Treat the redirect as a UI hint only; the source of truth is the webhook. Don't fulfill orders from the redirect alone.
export default async function ThanksPage({
searchParams,
}: {
searchParams: Promise<{ order_id?: string; session_id?: string }>;
}) {
const { order_id } = await searchParams;
if (!order_id) return <p>Awaiting confirmation…</p>;
const r = await getServerClient().orders.get(order_id);
if (!r.ok) return <p>Order not found.</p>;
return <h1>Thanks! Order #{r.value.order_number}</h1>;
}Webhooks
Wire the order.completed and payment.succeeded events to your fulfillment system. See webhooks for signing and replay.
Session status
| Status | Meaning |
|---|---|
open | Customer has not completed yet. URL is usable. |
completed | Payment captured; an order exists. |
expired | Past expires_at. Issue a new session. |
Next
- Pay sessions reference: Full API surface for the hosted product
- Embedded iframe: Keep the customer on your domain
Performance & Optimization
How Cimplify storefronts cache and render fast, and the page-level knobs you control to keep them that way.
Embedded checkout iframe
Mount the same checkout UI as Cimplify Pay on your own domain. Raw iframe embedding is possible, but saved-details sign-in requires an OAuth bridge; most storefronts should use the SDK controller.