@checkoutkit/stripe
v0.2.0
Published
Stripe payment handler for CheckoutKit — charge delegated payment tokens
Maintainers
Readme
@checkoutkit/stripe
Charge delegated payment tokens through Stripe.
pnpm add @checkoutkit/server @checkoutkit/stripeHow delegated payment works
The agent calls Stripe's delegate_payment endpoint with the buyer's credential
and an allowance scoped to one checkout session, one merchant and a maximum
amount. It receives an opaque vault token and sends that on /complete. You
charge the token through your own Stripe account.
Raw card data never touches CheckoutKit — that leg is between the agent and Stripe, which is why there is no delegate-payment client in this library.
Use
import { createStripePaymentHandler } from "@checkoutkit/stripe";
import { captureOrThrow } from "@checkoutkit/server";
const payments = createStripePaymentHandler({ secretKey: process.env.STRIPE_SECRET_KEY! });
// inside your CommerceBackend
async completeSession(sessionId, input, ctx) {
const session = await load(sessionId);
const total = session.totals.find((t) => t.type === "total")!;
const capture = await captureOrThrow(payments, {
paymentData: input.payment_data,
amount: total.amount, // the session's price, never the request's
currency: session.currency,
checkoutSessionId: sessionId,
idempotencyKey: ctx.idempotencyKey,
billingAddress: input.payment_data.billing_address,
});
return { ...session, status: "completed", order: createOrder(capture.id) };
}Three things this gets right that are easy to get wrong:
- Charge the session total, not the request. Taking the amount from
payment_datawould let an agent name its own price. - Pass
ctx.idempotencyKeythrough. Stripe then dedupes the charge too, so a retry that somehow bypasses the engine's replay still cannot double-charge. - A network failure is not a decline. If Stripe is unreachable the charge may or may not have landed, so the handler throws rather than telling the buyer their card failed.
Use handler.capture() directly instead of captureOrThrow when you want to
turn requires_action into an authentication_required session for 3DS rather
than an error.
Options
| | |
|---|---|
| secretKey | Your Stripe secret key. Required. |
| id | Matched against payment_data.handler_id. Default stripe. |
| stripeAccount | Stripe Connect: charge on behalf of a connected account. |
| buildCaptureBody | Add or override PaymentIntent fields. |
| fetch, apiBase, apiVersion, timeoutMs | Injectable, for tests and pinning. |
Refunds: handler.refund({ captureId, amount? }).
Verifying against real Stripe
The test suite drives a stub returning Stripe's documented response shapes. That
proves the mapping — what a card_error becomes, what requires_action
becomes — but not that those shapes still match the live API. To check against
your own account:
export STRIPE_SECRET_KEY=sk_test_...Mint a token in test mode, then capture with a small amount and confirm the
PaymentIntent appears in your Stripe dashboard. pm_card_visa works as a stand-in
payment method for a smoke test; pm_card_chargeDeclined exercises the decline
path and pm_card_authenticationRequired the 3DS path.
The token is sent as payment_method on a confirmed PaymentIntent. If Stripe's
agentic-commerce binding differs for your account, override it with
buildCaptureBody rather than forking.
CheckoutKit is an independent community implementation of the Agentic Commerce Protocol. It is not affiliated with, sponsored by, or endorsed by OpenAI or Stripe.
Part of CheckoutKit · Apache-2.0
