Files
route-commerce/src/lib/stripe-client.ts
T
tyler 015b1cf7b5 feat(checkout): real Stripe Express + Elements on /checkout
- Add @stripe/stripe-js + @stripe/react-stripe-js
- New src/lib/stripe-client.ts: cached loadStripe helper
- New src/actions/billing/retail-payment-intent.ts: server action
  that creates a PaymentIntent with automatic_payment_methods
- New src/components/storefront/StripeExpressCheckout.tsx: embedded
  ExpressCheckoutElement (Apple Pay, Google Pay, Link, PayPal) +
  PaymentElement (card) + hosted-checkout fallback
- /checkout form is now controlled; StripeExpressCheckout reads
  name/email/stop from form state and confirms in-page via
  stripe.confirmPayment
- /checkout/success handles both ?session_id= and ?payment_intent=
  so embedded + hosted flows both land on the same order-creation
  page using the pending_checkout sessionStorage payload
- Export StopInfo from CartContext and select 'time' on the stops
  fetch so the local Stop shape matches the context's StopInfo
- QuickCartSheet express buttons are visual shortcuts only;
  /checkout auto-renders the real Apple Pay / Google Pay buttons

Requires NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY for the embedded path;
if missing the embedded section falls back to the hosted
Stripe Checkout button. npx tsc --noEmit passes.
2026-06-04 18:38:21 +00:00

36 lines
1.2 KiB
TypeScript

/**
* Browser-only Stripe.js loader.
*
* Reads `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` and returns a cached
* `loadStripe` promise. Never call this from a server component — it
* pulls in `@stripe/stripe-js` which depends on `window`.
*
* The published key is safe to ship to the browser; the secret key
* never leaves the server (see `src/actions/billing/retail-checkout.ts`).
*/
"use client";
import { loadStripe, type Stripe } from "@stripe/stripe-js";
let stripePromise: Promise<Stripe | null> | null = null;
/**
* Returns the cached Stripe.js promise, creating it on the first call.
* Returns `null` if the publishable key is not configured — callers
* should fall back to the hosted Stripe Checkout flow in that case.
*/
export function getStripe(): Promise<Stripe | null> | null {
if (typeof window === "undefined") return null;
const key = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
if (!key) return null;
if (!stripePromise) {
stripePromise = loadStripe(key);
}
return stripePromise;
}
/** Synchronous check — useful for deciding whether to render Stripe Elements. */
export function hasStripePublishableKey(): boolean {
return Boolean(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY);
}