/**
* REST Handler — Checkout
* Handles /ucp/v1/checkout routes for session management and checkout execution.
* Uses getDeps() for shared dependencies and Zod for input validation.
*/
import type { RESTRequest, RESTResponse } from './types.js';
import { ok, created, notFound, badRequest, methodNotAllowed, serverError } from './types.js';
import { executeCheckout } from '../tools/execute-checkout.js';
import type { ExecuteCheckoutDeps } from '../tools/execute-checkout.js';
import { getDeps } from '../dynamo/factory.js';
import { createCheckoutSchema, updateCheckoutSchema, completeCheckoutSchema } from './schemas.js';
// ─── Handler ───
export async function handleCheckout(req: RESTRequest): Promise<RESTResponse> {
const deps = getDeps();
const mgr = deps.sessionManager;
try {
// POST /ucp/v1/checkout — create new session
if (req.method === 'POST' && req.segments.length === 0) {
const parsed = createCheckoutSchema.safeParse(req.body ?? undefined);
if (!parsed.success) {
const msg = parsed.error.issues.map((i) => i.message).join('; ');
return badRequest(msg);
}
const currency = parsed.data?.currency;
const session = await mgr.create(currency);
return created(session);
}
// POST /ucp/v1/checkout/{checkoutId}/complete — execute checkout
if (req.method === 'POST' && req.segments.length === 2 && req.segments[1] === 'complete') {
const checkoutId = req.segments[0];
const parsed = completeCheckoutSchema.safeParse(req.body);
if (!parsed.success) {
const msg = parsed.error.issues.map((i) => i.message).join('; ');
return badRequest(msg);
}
const checkoutDeps: ExecuteCheckoutDeps = {
sessionManager: deps.sessionManager,
verifier: deps.verifier,
mandateStore: deps.mandateStore,
guardrail: deps.guardrail,
feeCollector: deps.feeCollector,
storefrontAPI: deps.storefrontAPI,
};
const result = await executeCheckout(
{
checkout_id: checkoutId ?? '',
intent_mandate: parsed.data.intent_mandate,
cart_mandate: parsed.data.cart_mandate,
payment_mandate: parsed.data.payment_mandate,
},
checkoutDeps,
);
return ok(result);
}
// GET /ucp/v1/checkout/{checkoutId} — get session
if (req.method === 'GET' && req.segments.length > 0) {
const checkoutId = req.segments[0] ?? '';
const session = await mgr.get(checkoutId);
if (!session) {
return notFound(`Checkout session not found: ${checkoutId}`);
}
return ok(session);
}
// PUT /ucp/v1/checkout/{checkoutId} — update session
if (req.method === 'PUT' && req.segments.length > 0) {
const parsed = updateCheckoutSchema.safeParse(req.body);
if (!parsed.success) {
const msg = parsed.error.issues.map((i) => i.message).join('; ');
return badRequest(msg);
}
const session = await mgr.update(req.segments[0] ?? '', parsed.data);
return ok(session);
}
return methodNotAllowed(['GET', 'POST', 'PUT']);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (message.toLowerCase().includes('not found')) {
return notFound(message);
}
return serverError(message);
}
}