@usethrottle/checkout-react
v1.0.1
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 Gr4vy-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({ allowed_origins: ['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 *" />;
}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.
