@usethrottle/checkout-react
v1.2.0
Published
React component for embedded Throttle checkout
Readme
@usethrottle/checkout-react
React components for embedding Throttle checkout in your site. Two embed products + one helper hook:
<PaymentEmbed/>— payment-only iframe. Drops a provider-backed card form into your existing checkout page.<CheckoutEmbed/>— full-checkout iframe. Address + shipping + payment. Single iframe, single drop-in.useThrottleEvents()— hook for parents who build their own iframe wrapper but want Throttle's strict postMessage validation.
Install
npm install @usethrottle/checkout-reactOne-time setup: allow your site's origin
Each embed posts events to your page over postMessage with a strict
targetOrigin. Your origin must be on the merchant's allowlist:
throttle embed-config set --origins https://shop.example.comOr via API:
fetch('https://api.usethrottle.dev/api/v1/embed-config', {
method: 'PUT',
headers: { 'x-api-key': 'sk_...', 'content-type': 'application/json' },
body: JSON.stringify({ allowedOrigins: ['https://shop.example.com'] }),
});<PaymentEmbed/> — payment-only
import { PaymentEmbed } from '@usethrottle/checkout-react';
export function CheckoutPage({ sessionId }: { sessionId: string }) {
return (
<PaymentEmbed
sessionId={sessionId}
parentOrigin="https://shop.example.com"
primary="#ff6b00"
onReady={() => console.log('ready')}
onProcessing={() => console.log('paying...')}
onSucceeded={({ orderId, paymentId }) => {
// navigate to thank-you page, clear cart, etc.
window.location.href = `/orders/${orderId}`;
}}
onFailed={({ code, message }) => {
// surface to user
}}
onCanceled={() => {
// user dismissed
}}
/>
);
}<CheckoutEmbed/> — full checkout
import { CheckoutEmbed } from '@usethrottle/checkout-react';
export function CheckoutPage({ sessionId }: { sessionId: string }) {
return (
<CheckoutEmbed
sessionId={sessionId}
parentOrigin="https://shop.example.com"
primary="#ff6b00"
logo="https://shop.example.com/logo.png"
onSucceeded={({ orderId, paymentId }) => {
/* ... */
}}
onStepChanged={({ step }) => {
// step: 'cart' | 'address' | 'shipping' | 'payment'
}}
/>
);
}useThrottleEvents — DIY iframe wrapper
import { useThrottleEvents, type ThrottleMessage } from '@usethrottle/checkout-react';
import { useRef } from 'react';
export function MyEmbed({ sessionId }: { sessionId: string }) {
const iframeRef = useRef<HTMLIFrameElement>(null);
const baseUrl = 'https://checkout.usethrottle.dev';
useThrottleEvents({
iframeRef,
expectedOrigin: new URL(baseUrl).origin,
onMessage: (msg: ThrottleMessage) => {
if (msg.type === 'throttle.completed') console.log('paid', msg.orderId);
},
});
const src = `${baseUrl}/c/${sessionId}?embed=1&mode=payment-only&parentOrigin=${window.location.origin}`;
return <iframe ref={iframeRef} src={src} allow="payment *" />;
}useCartSession — backend-less cart
Own a Throttle cart directly from the browser — no backend, no secret key. The
hook wraps @usethrottle/cart's CartSessionClient: it creates the cart lazily
on the first addItem, persists the session id to localStorage, resumes it on
the next visit, and exposes reactive cart state. Authorize with a publishable
pk_ quote token + your application's origin allowlist (set origins via
PUT /api/v1/embed-config).
import { useCartSession } from '@usethrottle/checkout-react';
function Cart() {
const cart = useCartSession({
applicationId: '7f9d4c8a-5b2e-4f16-9a73-2d1e5c8b6f40',
environmentId: 'a1b2c3d4-...',
quoteToken: 'pk_live_...',
});
return (
<>
<button onClick={() => cart.addItem({ name: 'Widget', unitPrice: 2999, quantity: 1 })}>
Add to cart
</button>
{cart.cart && <p>Total: {cart.cart.total}</p>}
<button
disabled={cart.loading}
onClick={async () => {
const { checkoutUrl } = await cart.checkout({
returnUrl: 'https://shop.example.com/thanks',
cancelUrl: 'https://shop.example.com/cart',
});
window.location.href = checkoutUrl;
}}
>
Checkout
</button>
{cart.error && <p role="alert">{cart.error.message}</p>}
</>
);
}Also exposes updateItem, removeItem, selectShipping, clearShipping,
applyDiscount, removeDiscount, refresh, and reset. See the
cart sessions guide.
Events
Both <PaymentEmbed/> and <CheckoutEmbed/> fire these events to the
parent page over postMessage. They're complementary to Throttle's
outbound webhooks (which fire from Throttle's servers to your backend
over signed HTTP).
| Event | When | Callback |
| ----------------------- | ------------------------------- | ----------------------------------- |
| throttle.ready | iframe mounted | onReady() |
| throttle.processing | user clicked Pay; awaiting auth | onProcessing() |
| throttle.completed | payment captured | onSucceeded({orderId, paymentId}) |
| throttle.error | card declined / network failure | onFailed({code, message}) |
| throttle.cancelled | user dismissed | onCanceled() |
| throttle.step.changed | (CheckoutEmbed) step transition | onStepChanged({step}) |
| throttle.resize | iframe content resized | (auto: updates iframe height) |
Every message is wrapped in
{ source: 'throttle', version: 1, ...event } so your page can
disambiguate Throttle messages from other senders. The hook validates
origin, source window, and envelope before invoking your callback.
Backwards-compat alias: <ThrottleCheckout/>
ThrottleCheckout is a v0.1 alias that resolves to CheckoutEmbed.
The callback shape changed in v0.2: onCompleted(orderId, paymentId)
is now onSucceeded({orderId, paymentId}) and onError(code, message)
is now onFailed({code, message}). Update call sites accordingly.
