@paykernel/example-cloudflare-workers-fetch is a private workspace host (private: true). It is not published. The checkout kernel owns checkout, inbox, and reconciliation. This package maps fetch.
Source: examples/cloudflare-workers-fetch (README). There is no Wrangler config and no index.ts in this example — tests in Bun only.
Mapping
createCloudflareCheckoutFetch:
import { handleCloudflareWebhook } from "@paykernel/integration-cloudflare-workers";
import {
dispatchCheckoutRequest,
type CheckoutFetchApp,
type CheckoutHttpOptions,
type CheckoutKernel,
} from "@paykernel/example-checkout-kernel";
export function createCloudflareCheckoutFetch(
kernel: CheckoutKernel,
options: CheckoutHttpOptions = {},
): CheckoutFetchApp {
return {
async fetch(req: Request): Promise<Response> {
const url = new URL(req.url);
if (req.method === "POST" && url.pathname === "/webhooks/stripe") {
return handleCloudflareWebhook(req, {
gateway: kernel.webhook.gateway,
client: kernel.webhook.client,
engine: kernel.webhook.engine,
handler: kernel.webhook.handler,
});
}
return dispatchCheckoutRequest(kernel, req, options);
},
};
}handleCloudflareWebhook reads request.text(), request.headers, and URL.searchParams, then processWebhookHttp. Correlation id is x-request-id, then x-correlation-id, then cf-ray (resolveCorrelationId).
The helper only rejects non-POST with 405. Callers must guard the path (POST /webhooks/stripe) before delegating. Without that guard every POST on the Worker would hit the Stripe verifier. This example already checks pathname.
Other routes (/payments, /orders/:orderId, test hooks) go through dispatchCheckoutRequest (raw req.text() on the Stripe path is not used there because the path already branched).
Fulfillment is only kernel.webhook.handler after the inbox claim. Never fulfill in onWebhookVerified. Status codes come from mapInboxOutcome, not @paykernel/webhooks.
Tests use SQLite — not production Workers
runCheckoutHttpScenarios("cloudflare-workers", …) calls createCheckoutKernel() with the default in-memory Bun SQLite store. That is single-host and one process. Do not use this example’s SQLite in production Workers.
D1 ≠ Durable Objects ≠ Turso ≠ local SQLite. Pick one production store and migrate explicitly (never on import / every request).
| Store | Package | Factories in source (not invented) |
|---|---|---|
| D1 | @paykernel/store-d1 |
createD1PaymentStores({ db: env.PAYMENTS_DB, clock? }) after migrateD1Adapter(env.PAYMENTS_DB). Or createD1Stores({ executor, clock? }) with createD1Executor(db). |
| Durable Objects | @paykernel/store-durable-objects |
createDoPaymentStores({ namespace: env.PAYMENTS_DO, sharding }). Never one global DO. Hash sharding needs bindHashPartitionLayout / ensureDoHashPartitionLayout. |
| Local SQLite | @paykernel/store-sqlite |
What this example’s tests actually run. Not a Workers production store. |
CreateCheckoutKernelOptions accepts stores | storeFactory | executor (SQLite-shaped executor builds createSqliteStores).
Routes
| Method | Path | Notes |
|---|---|---|
POST |
/payments |
dispatchCheckoutRequest |
POST |
/webhooks/stripe |
handleCloudflareWebhook after path guard |
GET |
/orders/:orderId |
Order book |
POST |
/internal/reconcile |
Test hook. Unauthenticated. Do not deploy. |
POST |
/internal/provider-paid |
Test hook. Unauthenticated. Do not deploy. |
GET |
/internal/create-count |
Test hook. Unauthenticated. Do not deploy. |
Shared failure paths: Checkout kernel. Non-POST on the webhook helper → 405 from handleCloudflareWebhook.
Run
bun test examples/cloudflare-workers-fetchThat is Bun + sqlite, not wrangler dev.