@usethrottle/checkout-react 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # @usethrottle/checkout-react
2
2
 
3
- React component for embedding Throttle checkout in your app.
3
+ React components for embedding Throttle checkout in your site. Two embed
4
+ products + one helper hook:
5
+
6
+ - **`<PaymentEmbed/>`** — payment-only iframe. Drops a Gr4vy-backed
7
+ card form into your existing checkout page.
8
+ - **`<CheckoutEmbed/>`** — full-checkout iframe. Address + shipping +
9
+ payment. Single iframe, single drop-in.
10
+ - **`useThrottleEvents()`** — hook for parents who build their own
11
+ iframe wrapper but want Throttle's strict postMessage validation.
4
12
 
5
13
  ## Install
6
14
 
@@ -8,34 +16,127 @@ React component for embedding Throttle checkout in your app.
8
16
  npm install @usethrottle/checkout-react
9
17
  ```
10
18
 
11
- ## Usage
19
+ ## One-time setup: allow your site's origin
20
+
21
+ Each embed posts events to your page over `postMessage` with a strict
22
+ `targetOrigin`. Your origin must be on the merchant's allowlist:
23
+
24
+ ```bash
25
+ throttle embed-config set --origins https://shop.example.com
26
+ ```
27
+
28
+ Or via API:
29
+
30
+ ```ts
31
+ fetch('https://throttle-api-gff1.onrender.com/api/v1/embed-config', {
32
+ method: 'PUT',
33
+ headers: { 'x-api-key': 'sk_...', 'content-type': 'application/json' },
34
+ body: JSON.stringify({ allowed_origins: ['https://shop.example.com'] }),
35
+ });
36
+ ```
37
+
38
+ ## `<PaymentEmbed/>` — payment-only
12
39
 
13
40
  ```tsx
14
- import { ThrottleCheckout } from '@usethrottle/checkout-react';
41
+ import { PaymentEmbed } from '@usethrottle/checkout-react';
15
42
 
16
43
  export function CheckoutPage({ sessionId }: { sessionId: string }) {
17
44
  return (
18
- <ThrottleCheckout
45
+ <PaymentEmbed
19
46
  sessionId={sessionId}
20
- onCompleted={(orderId, paymentId) => {
47
+ parentOrigin="https://shop.example.com"
48
+ primary="#ff6b00"
49
+ onReady={() => console.log('ready')}
50
+ onProcessing={() => console.log('paying...')}
51
+ onSucceeded={({ orderId, paymentId }) => {
21
52
  // navigate to thank-you page, clear cart, etc.
53
+ window.location.href = `/orders/${orderId}`;
22
54
  }}
23
- onError={(code, message) => {
55
+ onFailed={({ code, message }) => {
24
56
  // surface to user
25
57
  }}
58
+ onCanceled={() => {
59
+ // user dismissed
60
+ }}
26
61
  />
27
62
  );
28
63
  }
29
64
  ```
30
65
 
31
- The component renders an iframe pointed at `https://throttle-checkout-web.onrender.com/c/<sessionId>?embed=1`. Override `baseUrl` to point at a different deployment.
66
+ ## `<CheckoutEmbed/>` full checkout
67
+
68
+ ```tsx
69
+ import { CheckoutEmbed } from '@usethrottle/checkout-react';
70
+
71
+ export function CheckoutPage({ sessionId }: { sessionId: string }) {
72
+ return (
73
+ <CheckoutEmbed
74
+ sessionId={sessionId}
75
+ parentOrigin="https://shop.example.com"
76
+ primary="#ff6b00"
77
+ logo="https://shop.example.com/logo.png"
78
+ onSucceeded={({ orderId, paymentId }) => { /* ... */ }}
79
+ onStepChanged={({ step }) => {
80
+ // step: 'cart' | 'address' | 'shipping' | 'payment'
81
+ }}
82
+ />
83
+ );
84
+ }
85
+ ```
86
+
87
+ ## `useThrottleEvents` — DIY iframe wrapper
88
+
89
+ ```tsx
90
+ import { useThrottleEvents, type ThrottleMessage } from '@usethrottle/checkout-react';
91
+ import { useRef } from 'react';
92
+
93
+ export function MyEmbed({ sessionId }: { sessionId: string }) {
94
+ const iframeRef = useRef<HTMLIFrameElement>(null);
95
+ const baseUrl = 'https://throttle-checkout.vercel.app';
96
+
97
+ useThrottleEvents({
98
+ iframeRef,
99
+ expectedOrigin: new URL(baseUrl).origin,
100
+ onMessage: (msg: ThrottleMessage) => {
101
+ if (msg.type === 'throttle.completed') console.log('paid', msg.orderId);
102
+ },
103
+ });
104
+
105
+ const src = `${baseUrl}/c/${sessionId}?embed=1&mode=payment-only&parentOrigin=${window.location.origin}`;
106
+ return <iframe ref={iframeRef} src={src} allow="payment *" />;
107
+ }
108
+ ```
32
109
 
33
110
  ## Events
34
111
 
35
- | Event | Callback | Payload |
112
+ Both `<PaymentEmbed/>` and `<CheckoutEmbed/>` fire these events to the
113
+ parent page over `postMessage`. They're complementary to Throttle's
114
+ outbound webhooks (which fire from Throttle's servers to your backend
115
+ over signed HTTP).
116
+
117
+ | Event | When | Callback |
36
118
  |---|---|---|
37
- | `throttle.ready` | `onReady` | — |
38
- | `throttle.resize` | (auto-handled, resizes iframe) | `{ height: number }` |
39
- | `throttle.completed` | `onCompleted(orderId, paymentId)` | `{ orderId, paymentId }` |
40
- | `throttle.cancelled` | `onCancelled` | |
41
- | `throttle.error` | `onError(code, message)` | `{ code, message }` |
119
+ | `throttle.ready` | iframe mounted | `onReady()` |
120
+ | `throttle.processing` | user clicked Pay; awaiting auth | `onProcessing()` |
121
+ | `throttle.completed` | payment captured | `onSucceeded({orderId, paymentId})` |
122
+ | `throttle.error` | card declined / network failure | `onFailed({code, message})` |
123
+ | `throttle.cancelled` | user dismissed | `onCanceled()` |
124
+ | `throttle.step.changed` | (CheckoutEmbed) step transition | `onStepChanged({step})` |
125
+ | `throttle.resize` | iframe content resized | (auto: updates iframe height) |
126
+
127
+ Every message is wrapped in
128
+ `{ source: 'throttle', version: 1, ...event }` so your page can
129
+ disambiguate Throttle messages from other senders. The hook validates
130
+ origin, source window, and envelope before invoking your callback.
131
+
132
+ ## Backwards-compat alias: `<ThrottleCheckout/>`
133
+
134
+ `ThrottleCheckout` is a v0.1 alias that resolves to `CheckoutEmbed`.
135
+ The callback shape changed in v0.2: `onCompleted(orderId, paymentId)`
136
+ is now `onSucceeded({orderId, paymentId})` and `onError(code, message)`
137
+ is now `onFailed({code, message})`. Update call sites accordingly.
138
+
139
+ ## See also
140
+
141
+ - [CLI: `throttle embed-config`](https://www.npmjs.com/package/@usethrottle/cli)
142
+ - [API client: `@usethrottle/api-client`](https://www.npmjs.com/package/@usethrottle/api-client)
package/dist/index.cjs CHANGED
@@ -20,43 +20,89 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.tsx
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- ThrottleCheckout: () => ThrottleCheckout
23
+ CheckoutEmbed: () => CheckoutEmbed,
24
+ PaymentEmbed: () => PaymentEmbed,
25
+ ThrottleCheckout: () => CheckoutEmbed,
26
+ useThrottleEvents: () => useThrottleEvents
24
27
  });
25
28
  module.exports = __toCommonJS(index_exports);
29
+
30
+ // src/CheckoutEmbed.tsx
31
+ var import_react2 = require("react");
32
+
33
+ // src/useThrottleEvents.ts
26
34
  var import_react = require("react");
35
+ function useThrottleEvents({ iframeRef, expectedOrigin, onMessage }) {
36
+ (0, import_react.useEffect)(() => {
37
+ function handle(ev) {
38
+ if (ev.origin !== expectedOrigin) return;
39
+ if (iframeRef.current && ev.source !== iframeRef.current.contentWindow) return;
40
+ const data = ev.data;
41
+ if (!data || typeof data !== "object") return;
42
+ if (data.source !== "throttle" || data.version !== 1) return;
43
+ if (typeof data.type !== "string" || !data.type.startsWith("throttle.")) return;
44
+ onMessage(data);
45
+ }
46
+ window.addEventListener("message", handle);
47
+ return () => window.removeEventListener("message", handle);
48
+ }, [iframeRef, expectedOrigin, onMessage]);
49
+ }
50
+
51
+ // src/CheckoutEmbed.tsx
27
52
  var import_jsx_runtime = require("react/jsx-runtime");
28
- var DEFAULT_BASE_URL = "https://throttle-checkout-web.onrender.com";
29
- function ThrottleCheckout({
53
+ var DEFAULT_BASE_URL = "https://checkout.usethrottle.dev";
54
+ function CheckoutEmbed({
30
55
  sessionId,
56
+ parentOrigin,
31
57
  baseUrl = DEFAULT_BASE_URL,
32
- initialHeight = 480,
58
+ primary,
59
+ logo,
60
+ initialHeight = 600,
33
61
  className,
34
62
  style,
35
63
  onReady,
36
- onCompleted,
37
- onCancelled,
38
- onError
64
+ onProcessing,
65
+ onSucceeded,
66
+ onFailed,
67
+ onCanceled,
68
+ onStepChanged
39
69
  }) {
40
- const iframeRef = (0, import_react.useRef)(null);
41
- (0, import_react.useEffect)(() => {
42
- function handle(ev) {
43
- const data = ev?.data;
44
- if (!data || typeof data !== "object" || typeof data.type !== "string") return;
45
- if (!data.type.startsWith("throttle.")) return;
46
- if (iframeRef.current && ev.source !== iframeRef.current.contentWindow) return;
47
- if (data.type === "throttle.resize" && typeof data.height === "number" && iframeRef.current) {
48
- iframeRef.current.style.height = `${data.height}px`;
70
+ const iframeRef = (0, import_react2.useRef)(null);
71
+ const expectedOrigin = new URL(baseUrl).origin;
72
+ const dispatch = (0, import_react2.useCallback)(
73
+ (msg) => {
74
+ switch (msg.type) {
75
+ case "throttle.ready":
76
+ onReady?.();
77
+ break;
78
+ case "throttle.processing":
79
+ onProcessing?.();
80
+ break;
81
+ case "throttle.completed":
82
+ onSucceeded?.({ orderId: msg.orderId, paymentId: msg.paymentId });
83
+ break;
84
+ case "throttle.error":
85
+ onFailed?.({ code: msg.code, message: msg.message });
86
+ break;
87
+ case "throttle.cancelled":
88
+ onCanceled?.();
89
+ break;
90
+ case "throttle.step.changed":
91
+ onStepChanged?.({ step: msg.step });
92
+ break;
93
+ case "throttle.resize":
94
+ if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;
95
+ break;
49
96
  }
50
- if (data.type === "throttle.ready") onReady?.();
51
- else if (data.type === "throttle.completed") onCompleted?.(data.orderId, data.paymentId);
52
- else if (data.type === "throttle.cancelled") onCancelled?.();
53
- else if (data.type === "throttle.error") onError?.(data.code, data.message);
54
- }
55
- window.addEventListener("message", handle);
56
- return () => window.removeEventListener("message", handle);
57
- }, [onReady, onCompleted, onCancelled, onError]);
97
+ },
98
+ [onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged]
99
+ );
100
+ useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });
101
+ const params = new URLSearchParams({ embed: "1", mode: "checkout-full", parentOrigin });
102
+ if (primary) params.set("primary", primary);
103
+ if (logo) params.set("logo", logo);
58
104
  const cleanBase = baseUrl.replace(/\/$/, "");
59
- const src = `${cleanBase}/c/${encodeURIComponent(sessionId)}?embed=1`;
105
+ const src = `${cleanBase}/c/${encodeURIComponent(sessionId)}?${params.toString()}`;
60
106
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
61
107
  "iframe",
62
108
  {
@@ -69,8 +115,76 @@ function ThrottleCheckout({
69
115
  }
70
116
  );
71
117
  }
118
+
119
+ // src/PaymentEmbed.tsx
120
+ var import_react3 = require("react");
121
+ var import_jsx_runtime2 = require("react/jsx-runtime");
122
+ var DEFAULT_BASE_URL2 = "https://checkout.usethrottle.dev";
123
+ function PaymentEmbed({
124
+ sessionId,
125
+ parentOrigin,
126
+ baseUrl = DEFAULT_BASE_URL2,
127
+ primary,
128
+ logo,
129
+ initialHeight = 480,
130
+ className,
131
+ style,
132
+ onReady,
133
+ onProcessing,
134
+ onSucceeded,
135
+ onFailed,
136
+ onCanceled
137
+ }) {
138
+ const iframeRef = (0, import_react3.useRef)(null);
139
+ const expectedOrigin = new URL(baseUrl).origin;
140
+ const dispatch = (0, import_react3.useCallback)(
141
+ (msg) => {
142
+ switch (msg.type) {
143
+ case "throttle.ready":
144
+ onReady?.();
145
+ break;
146
+ case "throttle.processing":
147
+ onProcessing?.();
148
+ break;
149
+ case "throttle.completed":
150
+ onSucceeded?.({ orderId: msg.orderId, paymentId: msg.paymentId });
151
+ break;
152
+ case "throttle.error":
153
+ onFailed?.({ code: msg.code, message: msg.message });
154
+ break;
155
+ case "throttle.cancelled":
156
+ onCanceled?.();
157
+ break;
158
+ case "throttle.resize":
159
+ if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;
160
+ break;
161
+ }
162
+ },
163
+ [onReady, onProcessing, onSucceeded, onFailed, onCanceled]
164
+ );
165
+ useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });
166
+ const params = new URLSearchParams({ embed: "1", mode: "payment-only", parentOrigin });
167
+ if (primary) params.set("primary", primary);
168
+ if (logo) params.set("logo", logo);
169
+ const cleanBase = baseUrl.replace(/\/$/, "");
170
+ const src = `${cleanBase}/c/${encodeURIComponent(sessionId)}?${params.toString()}`;
171
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
172
+ "iframe",
173
+ {
174
+ ref: iframeRef,
175
+ src,
176
+ className,
177
+ style: { border: 0, width: "100%", minHeight: initialHeight, ...style },
178
+ allow: "payment *",
179
+ title: "Throttle payment"
180
+ }
181
+ );
182
+ }
72
183
  // Annotate the CommonJS export names for ESM import in node:
73
184
  0 && (module.exports = {
74
- ThrottleCheckout
185
+ CheckoutEmbed,
186
+ PaymentEmbed,
187
+ ThrottleCheckout,
188
+ useThrottleEvents
75
189
  });
76
190
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\nexport interface ThrottleCheckoutProps {\n /** The throttle checkout session id (sess_*). Required. */\n sessionId: string;\n /** Base URL of the deployed checkout-web app. Defaults to https://throttle-checkout-web.onrender.com */\n baseUrl?: string;\n /** Initial iframe height in pixels. Auto-resizes on throttle.resize events. */\n initialHeight?: number;\n /** Optional className for the iframe. */\n className?: string;\n /** Optional inline style for the iframe. */\n style?: React.CSSProperties;\n /** Fires once the iframe is ready to receive interactions. */\n onReady?: () => void;\n /** Fires after a successful payment. */\n onCompleted?: (orderId: string, paymentId: string) => void;\n /** Fires when the buyer cancels checkout. */\n onCancelled?: () => void;\n /** Fires on any error inside the iframe (Gr4vy decline, network, etc.). */\n onError?: (code: string, message: string) => void;\n}\n\nconst DEFAULT_BASE_URL = 'https://throttle-checkout-web.onrender.com';\n\n/**\n * Embedded Throttle checkout for React apps.\n *\n * Renders an iframe pointed at <baseUrl>/c/<sessionId>?embed=1 and\n * subscribes to the postMessage v1 protocol. Events fire on the\n * provided callbacks. Auto-resizes the iframe on throttle.resize.\n */\nexport function ThrottleCheckout({\n sessionId,\n baseUrl = DEFAULT_BASE_URL,\n initialHeight = 480,\n className,\n style,\n onReady,\n onCompleted,\n onCancelled,\n onError,\n}: ThrottleCheckoutProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n\n useEffect(() => {\n function handle(ev: MessageEvent) {\n const data = ev?.data;\n if (!data || typeof data !== 'object' || typeof data.type !== 'string') return;\n if (!data.type.startsWith('throttle.')) return;\n if (iframeRef.current && ev.source !== iframeRef.current.contentWindow) return;\n\n if (data.type === 'throttle.resize' && typeof data.height === 'number' && iframeRef.current) {\n iframeRef.current.style.height = `${data.height}px`;\n }\n if (data.type === 'throttle.ready') onReady?.();\n else if (data.type === 'throttle.completed') onCompleted?.(data.orderId, data.paymentId);\n else if (data.type === 'throttle.cancelled') onCancelled?.();\n else if (data.type === 'throttle.error') onError?.(data.code, data.message);\n }\n window.addEventListener('message', handle);\n return () => window.removeEventListener('message', handle);\n }, [onReady, onCompleted, onCancelled, onError]);\n\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/c/${encodeURIComponent(sessionId)}?embed=1`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle checkout\"\n />\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkC;AAoE9B;AA7CJ,IAAM,mBAAmB;AASlB,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,QAAM,gBAAY,qBAA0B,IAAI;AAEhD,8BAAU,MAAM;AACd,aAAS,OAAO,IAAkB;AAChC,YAAM,OAAO,IAAI;AACjB,UAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,OAAO,KAAK,SAAS,SAAU;AACxE,UAAI,CAAC,KAAK,KAAK,WAAW,WAAW,EAAG;AACxC,UAAI,UAAU,WAAW,GAAG,WAAW,UAAU,QAAQ,cAAe;AAExE,UAAI,KAAK,SAAS,qBAAqB,OAAO,KAAK,WAAW,YAAY,UAAU,SAAS;AAC3F,kBAAU,QAAQ,MAAM,SAAS,GAAG,KAAK,MAAM;AAAA,MACjD;AACA,UAAI,KAAK,SAAS,iBAAkB,WAAU;AAAA,eACrC,KAAK,SAAS,qBAAsB,eAAc,KAAK,SAAS,KAAK,SAAS;AAAA,eAC9E,KAAK,SAAS,qBAAsB,eAAc;AAAA,eAClD,KAAK,SAAS,iBAAkB,WAAU,KAAK,MAAM,KAAK,OAAO;AAAA,IAC5E;AACA,WAAO,iBAAiB,WAAW,MAAM;AACzC,WAAO,MAAM,OAAO,oBAAoB,WAAW,MAAM;AAAA,EAC3D,GAAG,CAAC,SAAS,aAAa,aAAa,OAAO,CAAC;AAE/C,QAAM,YAAY,QAAQ,QAAQ,OAAO,EAAE;AAC3C,QAAM,MAAM,GAAG,SAAS,MAAM,mBAAmB,SAAS,CAAC;AAE3D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,OAAO,EAAE,QAAQ,GAAG,OAAO,QAAQ,WAAW,eAAe,GAAG,MAAM;AAAA,MACtE,OAAM;AAAA,MACN,OAAM;AAAA;AAAA,EACR;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../src/index.tsx","../src/CheckoutEmbed.tsx","../src/useThrottleEvents.ts","../src/PaymentEmbed.tsx"],"sourcesContent":["export { CheckoutEmbed } from './CheckoutEmbed.js';\nexport { PaymentEmbed } from './PaymentEmbed.js';\nexport { useThrottleEvents } from './useThrottleEvents.js';\nexport type {\n ThrottleEvent,\n ThrottleMessage,\n ThrottleEnvelope,\n ThrottleEmbedProps,\n CheckoutEmbedExtraProps,\n} from './types.js';\n\n// v0.1.x compat alias. v0.1's <ThrottleCheckout> shape (positional\n// onCompleted/onError args, optional baseUrl, no parentOrigin) is\n// gone in v0.2 — the alias resolves to <CheckoutEmbed> which now\n// requires `parentOrigin`. Update call sites accordingly.\nexport { CheckoutEmbed as ThrottleCheckout } from './CheckoutEmbed.js';\n","import { useCallback, useRef } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport type {\n ThrottleEmbedProps,\n CheckoutEmbedExtraProps,\n ThrottleMessage,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Full-checkout iframe embed. Renders the entire Throttle hosted\n * checkout (address / shipping / payment) inside an iframe and\n * surfaces lifecycle + terminal events through callbacks. The\n * iframe auto-resizes on `throttle.resize` events.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list\n * (`throttle embed-config set --origins ...`). Mismatches render\n * an in-iframe \"Embed not authorized\" panel.\n */\nexport function CheckoutEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n primary,\n logo,\n initialHeight = 600,\n className,\n style,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n onStepChanged,\n}: ThrottleEmbedProps & CheckoutEmbedExtraProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({ orderId: msg.orderId, paymentId: msg.paymentId });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.step.changed':\n onStepChanged?.({ step: msg.step });\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n\n const params = new URLSearchParams({ embed: '1', mode: 'checkout-full', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/c/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle checkout\"\n />\n );\n}\n","import { useEffect, type RefObject } from 'react';\nimport type { ThrottleMessage } from './types.js';\n\nexport interface UseThrottleEventsOptions {\n iframeRef: RefObject<HTMLIFrameElement | null>;\n /**\n * The Throttle origin (e.g. https://checkout.usethrottle.dev).\n * Strict-equals check against `MessageEvent.origin`. Anything else\n * is silently dropped.\n */\n expectedOrigin: string;\n onMessage: (msg: ThrottleMessage) => void;\n}\n\n/**\n * Subscribe to validated postMessage events from a Throttle iframe.\n *\n * Validation:\n * - event.origin === expectedOrigin (strict string match)\n * - event.source === iframeRef.current.contentWindow (defends against\n * other iframes on the same origin spoofing events)\n * - event.data.source === 'throttle' && event.data.version === 1\n * - event.data.type starts with 'throttle.'\n *\n * Anything failing validation is silently dropped — the parent page\n * may receive postMessages from analytics SDKs, browser extensions,\n * other iframes, etc. We must never throw on those.\n */\nexport function useThrottleEvents({ iframeRef, expectedOrigin, onMessage }: UseThrottleEventsOptions) {\n useEffect(() => {\n function handle(ev: MessageEvent) {\n if (ev.origin !== expectedOrigin) return;\n if (iframeRef.current && ev.source !== iframeRef.current.contentWindow) return;\n const data = ev.data as Partial<ThrottleMessage> | null | undefined;\n if (!data || typeof data !== 'object') return;\n if (data.source !== 'throttle' || data.version !== 1) return;\n if (typeof data.type !== 'string' || !data.type.startsWith('throttle.')) return;\n onMessage(data as ThrottleMessage);\n }\n window.addEventListener('message', handle);\n return () => window.removeEventListener('message', handle);\n }, [iframeRef, expectedOrigin, onMessage]);\n}\n","import { useCallback, useRef } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport type { ThrottleEmbedProps, ThrottleMessage } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Payment-only iframe embed. Renders just the Gr4vy provider iframe\n * (no address / shipping / cart UI). Email is collected by the Gr4vy\n * iframe in proxy-mode sessions. Used when the merchant has built\n * their own checkout UI and only needs Throttle to handle payment\n * authorization + capture.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list.\n * The iframe auto-resizes on `throttle.resize` events.\n */\nexport function PaymentEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n primary,\n logo,\n initialHeight = 480,\n className,\n style,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n}: ThrottleEmbedProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({ orderId: msg.orderId, paymentId: msg.paymentId });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n\n const params = new URLSearchParams({ embed: '1', mode: 'payment-only', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/c/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle payment\"\n />\n );\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime","DEFAULT_BASE_URL"]}
package/dist/index.d.cts CHANGED
@@ -1,32 +1,123 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { CSSProperties, RefObject } from 'react';
2
3
 
3
- interface ThrottleCheckoutProps {
4
- /** The throttle checkout session id (sess_*). Required. */
4
+ /**
5
+ * Envelope every Throttle iframe message carries. Lets parents
6
+ * disambiguate Throttle messages from other postMessage senders on the
7
+ * page (analytics SDKs, ad pixels, other iframes).
8
+ */
9
+ interface ThrottleEnvelope {
10
+ source: 'throttle';
11
+ version: 1;
12
+ }
13
+ type ThrottleEvent = {
14
+ type: 'throttle.ready';
15
+ } | {
16
+ type: 'throttle.resize';
17
+ height: number;
18
+ } | {
19
+ type: 'throttle.processing';
20
+ } | {
21
+ type: 'throttle.completed';
22
+ orderId: string;
23
+ paymentId: string;
24
+ } | {
25
+ type: 'throttle.cancelled';
26
+ } | {
27
+ type: 'throttle.error';
28
+ code: string;
29
+ message: string;
30
+ } | {
31
+ type: 'throttle.step.changed';
32
+ step: 'cart' | 'address' | 'shipping' | 'payment';
33
+ };
34
+ type ThrottleMessage = ThrottleEvent & ThrottleEnvelope;
35
+ interface ThrottleEmbedProps {
36
+ /** Throttle checkout session id (cs_*). Required. */
5
37
  sessionId: string;
6
- /** Base URL of the deployed checkout-web app. Defaults to https://throttle-checkout-web.onrender.com */
38
+ /**
39
+ * Parent origin where this embed is mounted (e.g. https://shop.example.com).
40
+ * Must be in the merchant's allowed_origins list, set via
41
+ * `throttle embed-config set --origins ...` or PUT /api/v1/embed-config.
42
+ * Required.
43
+ */
44
+ parentOrigin: string;
45
+ /** Base URL of the deployed checkout-web app. Defaults to https://checkout.usethrottle.dev. */
7
46
  baseUrl?: string;
8
- /** Initial iframe height in pixels. Auto-resizes on throttle.resize events. */
47
+ /** Optional brand color override (#rrggbb). */
48
+ primary?: string;
49
+ /** Optional logo URL override (https). */
50
+ logo?: string;
51
+ /** Initial iframe height in px. Auto-resizes on throttle.resize events. */
9
52
  initialHeight?: number;
10
- /** Optional className for the iframe. */
11
53
  className?: string;
12
- /** Optional inline style for the iframe. */
13
- style?: React.CSSProperties;
14
- /** Fires once the iframe is ready to receive interactions. */
54
+ style?: CSSProperties;
15
55
  onReady?: () => void;
16
- /** Fires after a successful payment. */
17
- onCompleted?: (orderId: string, paymentId: string) => void;
18
- /** Fires when the buyer cancels checkout. */
19
- onCancelled?: () => void;
20
- /** Fires on any error inside the iframe (Gr4vy decline, network, etc.). */
21
- onError?: (code: string, message: string) => void;
56
+ onProcessing?: () => void;
57
+ onSucceeded?: (evt: {
58
+ orderId: string;
59
+ paymentId: string;
60
+ }) => void;
61
+ onFailed?: (evt: {
62
+ code: string;
63
+ message: string;
64
+ }) => void;
65
+ onCanceled?: () => void;
66
+ }
67
+ interface CheckoutEmbedExtraProps {
68
+ onStepChanged?: (evt: {
69
+ step: 'cart' | 'address' | 'shipping' | 'payment';
70
+ }) => void;
22
71
  }
72
+
23
73
  /**
24
- * Embedded Throttle checkout for React apps.
74
+ * Full-checkout iframe embed. Renders the entire Throttle hosted
75
+ * checkout (address / shipping / payment) inside an iframe and
76
+ * surfaces lifecycle + terminal events through callbacks. The
77
+ * iframe auto-resizes on `throttle.resize` events.
78
+ *
79
+ * `parentOrigin` must match an entry in the merchant's allow-list
80
+ * (`throttle embed-config set --origins ...`). Mismatches render
81
+ * an in-iframe "Embed not authorized" panel.
82
+ */
83
+ declare function CheckoutEmbed({ sessionId, parentOrigin, baseUrl, primary, logo, initialHeight, className, style, onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged, }: ThrottleEmbedProps & CheckoutEmbedExtraProps): react_jsx_runtime.JSX.Element;
84
+
85
+ /**
86
+ * Payment-only iframe embed. Renders just the Gr4vy provider iframe
87
+ * (no address / shipping / cart UI). Email is collected by the Gr4vy
88
+ * iframe in proxy-mode sessions. Used when the merchant has built
89
+ * their own checkout UI and only needs Throttle to handle payment
90
+ * authorization + capture.
91
+ *
92
+ * `parentOrigin` must match an entry in the merchant's allow-list.
93
+ * The iframe auto-resizes on `throttle.resize` events.
94
+ */
95
+ declare function PaymentEmbed({ sessionId, parentOrigin, baseUrl, primary, logo, initialHeight, className, style, onReady, onProcessing, onSucceeded, onFailed, onCanceled, }: ThrottleEmbedProps): react_jsx_runtime.JSX.Element;
96
+
97
+ interface UseThrottleEventsOptions {
98
+ iframeRef: RefObject<HTMLIFrameElement | null>;
99
+ /**
100
+ * The Throttle origin (e.g. https://checkout.usethrottle.dev).
101
+ * Strict-equals check against `MessageEvent.origin`. Anything else
102
+ * is silently dropped.
103
+ */
104
+ expectedOrigin: string;
105
+ onMessage: (msg: ThrottleMessage) => void;
106
+ }
107
+ /**
108
+ * Subscribe to validated postMessage events from a Throttle iframe.
109
+ *
110
+ * Validation:
111
+ * - event.origin === expectedOrigin (strict string match)
112
+ * - event.source === iframeRef.current.contentWindow (defends against
113
+ * other iframes on the same origin spoofing events)
114
+ * - event.data.source === 'throttle' && event.data.version === 1
115
+ * - event.data.type starts with 'throttle.'
25
116
  *
26
- * Renders an iframe pointed at <baseUrl>/c/<sessionId>?embed=1 and
27
- * subscribes to the postMessage v1 protocol. Events fire on the
28
- * provided callbacks. Auto-resizes the iframe on throttle.resize.
117
+ * Anything failing validation is silently dropped — the parent page
118
+ * may receive postMessages from analytics SDKs, browser extensions,
119
+ * other iframes, etc. We must never throw on those.
29
120
  */
30
- declare function ThrottleCheckout({ sessionId, baseUrl, initialHeight, className, style, onReady, onCompleted, onCancelled, onError, }: ThrottleCheckoutProps): react_jsx_runtime.JSX.Element;
121
+ declare function useThrottleEvents({ iframeRef, expectedOrigin, onMessage }: UseThrottleEventsOptions): void;
31
122
 
32
- export { ThrottleCheckout, type ThrottleCheckoutProps };
123
+ export { CheckoutEmbed, type CheckoutEmbedExtraProps, PaymentEmbed, CheckoutEmbed as ThrottleCheckout, type ThrottleEmbedProps, type ThrottleEnvelope, type ThrottleEvent, type ThrottleMessage, useThrottleEvents };
package/dist/index.d.ts CHANGED
@@ -1,32 +1,123 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { CSSProperties, RefObject } from 'react';
2
3
 
3
- interface ThrottleCheckoutProps {
4
- /** The throttle checkout session id (sess_*). Required. */
4
+ /**
5
+ * Envelope every Throttle iframe message carries. Lets parents
6
+ * disambiguate Throttle messages from other postMessage senders on the
7
+ * page (analytics SDKs, ad pixels, other iframes).
8
+ */
9
+ interface ThrottleEnvelope {
10
+ source: 'throttle';
11
+ version: 1;
12
+ }
13
+ type ThrottleEvent = {
14
+ type: 'throttle.ready';
15
+ } | {
16
+ type: 'throttle.resize';
17
+ height: number;
18
+ } | {
19
+ type: 'throttle.processing';
20
+ } | {
21
+ type: 'throttle.completed';
22
+ orderId: string;
23
+ paymentId: string;
24
+ } | {
25
+ type: 'throttle.cancelled';
26
+ } | {
27
+ type: 'throttle.error';
28
+ code: string;
29
+ message: string;
30
+ } | {
31
+ type: 'throttle.step.changed';
32
+ step: 'cart' | 'address' | 'shipping' | 'payment';
33
+ };
34
+ type ThrottleMessage = ThrottleEvent & ThrottleEnvelope;
35
+ interface ThrottleEmbedProps {
36
+ /** Throttle checkout session id (cs_*). Required. */
5
37
  sessionId: string;
6
- /** Base URL of the deployed checkout-web app. Defaults to https://throttle-checkout-web.onrender.com */
38
+ /**
39
+ * Parent origin where this embed is mounted (e.g. https://shop.example.com).
40
+ * Must be in the merchant's allowed_origins list, set via
41
+ * `throttle embed-config set --origins ...` or PUT /api/v1/embed-config.
42
+ * Required.
43
+ */
44
+ parentOrigin: string;
45
+ /** Base URL of the deployed checkout-web app. Defaults to https://checkout.usethrottle.dev. */
7
46
  baseUrl?: string;
8
- /** Initial iframe height in pixels. Auto-resizes on throttle.resize events. */
47
+ /** Optional brand color override (#rrggbb). */
48
+ primary?: string;
49
+ /** Optional logo URL override (https). */
50
+ logo?: string;
51
+ /** Initial iframe height in px. Auto-resizes on throttle.resize events. */
9
52
  initialHeight?: number;
10
- /** Optional className for the iframe. */
11
53
  className?: string;
12
- /** Optional inline style for the iframe. */
13
- style?: React.CSSProperties;
14
- /** Fires once the iframe is ready to receive interactions. */
54
+ style?: CSSProperties;
15
55
  onReady?: () => void;
16
- /** Fires after a successful payment. */
17
- onCompleted?: (orderId: string, paymentId: string) => void;
18
- /** Fires when the buyer cancels checkout. */
19
- onCancelled?: () => void;
20
- /** Fires on any error inside the iframe (Gr4vy decline, network, etc.). */
21
- onError?: (code: string, message: string) => void;
56
+ onProcessing?: () => void;
57
+ onSucceeded?: (evt: {
58
+ orderId: string;
59
+ paymentId: string;
60
+ }) => void;
61
+ onFailed?: (evt: {
62
+ code: string;
63
+ message: string;
64
+ }) => void;
65
+ onCanceled?: () => void;
66
+ }
67
+ interface CheckoutEmbedExtraProps {
68
+ onStepChanged?: (evt: {
69
+ step: 'cart' | 'address' | 'shipping' | 'payment';
70
+ }) => void;
22
71
  }
72
+
23
73
  /**
24
- * Embedded Throttle checkout for React apps.
74
+ * Full-checkout iframe embed. Renders the entire Throttle hosted
75
+ * checkout (address / shipping / payment) inside an iframe and
76
+ * surfaces lifecycle + terminal events through callbacks. The
77
+ * iframe auto-resizes on `throttle.resize` events.
78
+ *
79
+ * `parentOrigin` must match an entry in the merchant's allow-list
80
+ * (`throttle embed-config set --origins ...`). Mismatches render
81
+ * an in-iframe "Embed not authorized" panel.
82
+ */
83
+ declare function CheckoutEmbed({ sessionId, parentOrigin, baseUrl, primary, logo, initialHeight, className, style, onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged, }: ThrottleEmbedProps & CheckoutEmbedExtraProps): react_jsx_runtime.JSX.Element;
84
+
85
+ /**
86
+ * Payment-only iframe embed. Renders just the Gr4vy provider iframe
87
+ * (no address / shipping / cart UI). Email is collected by the Gr4vy
88
+ * iframe in proxy-mode sessions. Used when the merchant has built
89
+ * their own checkout UI and only needs Throttle to handle payment
90
+ * authorization + capture.
91
+ *
92
+ * `parentOrigin` must match an entry in the merchant's allow-list.
93
+ * The iframe auto-resizes on `throttle.resize` events.
94
+ */
95
+ declare function PaymentEmbed({ sessionId, parentOrigin, baseUrl, primary, logo, initialHeight, className, style, onReady, onProcessing, onSucceeded, onFailed, onCanceled, }: ThrottleEmbedProps): react_jsx_runtime.JSX.Element;
96
+
97
+ interface UseThrottleEventsOptions {
98
+ iframeRef: RefObject<HTMLIFrameElement | null>;
99
+ /**
100
+ * The Throttle origin (e.g. https://checkout.usethrottle.dev).
101
+ * Strict-equals check against `MessageEvent.origin`. Anything else
102
+ * is silently dropped.
103
+ */
104
+ expectedOrigin: string;
105
+ onMessage: (msg: ThrottleMessage) => void;
106
+ }
107
+ /**
108
+ * Subscribe to validated postMessage events from a Throttle iframe.
109
+ *
110
+ * Validation:
111
+ * - event.origin === expectedOrigin (strict string match)
112
+ * - event.source === iframeRef.current.contentWindow (defends against
113
+ * other iframes on the same origin spoofing events)
114
+ * - event.data.source === 'throttle' && event.data.version === 1
115
+ * - event.data.type starts with 'throttle.'
25
116
  *
26
- * Renders an iframe pointed at <baseUrl>/c/<sessionId>?embed=1 and
27
- * subscribes to the postMessage v1 protocol. Events fire on the
28
- * provided callbacks. Auto-resizes the iframe on throttle.resize.
117
+ * Anything failing validation is silently dropped — the parent page
118
+ * may receive postMessages from analytics SDKs, browser extensions,
119
+ * other iframes, etc. We must never throw on those.
29
120
  */
30
- declare function ThrottleCheckout({ sessionId, baseUrl, initialHeight, className, style, onReady, onCompleted, onCancelled, onError, }: ThrottleCheckoutProps): react_jsx_runtime.JSX.Element;
121
+ declare function useThrottleEvents({ iframeRef, expectedOrigin, onMessage }: UseThrottleEventsOptions): void;
31
122
 
32
- export { ThrottleCheckout, type ThrottleCheckoutProps };
123
+ export { CheckoutEmbed, type CheckoutEmbedExtraProps, PaymentEmbed, CheckoutEmbed as ThrottleCheckout, type ThrottleEmbedProps, type ThrottleEnvelope, type ThrottleEvent, type ThrottleMessage, useThrottleEvents };
package/dist/index.js CHANGED
@@ -1,38 +1,79 @@
1
- // src/index.tsx
2
- import { useEffect, useRef } from "react";
1
+ // src/CheckoutEmbed.tsx
2
+ import { useCallback, useRef } from "react";
3
+
4
+ // src/useThrottleEvents.ts
5
+ import { useEffect } from "react";
6
+ function useThrottleEvents({ iframeRef, expectedOrigin, onMessage }) {
7
+ useEffect(() => {
8
+ function handle(ev) {
9
+ if (ev.origin !== expectedOrigin) return;
10
+ if (iframeRef.current && ev.source !== iframeRef.current.contentWindow) return;
11
+ const data = ev.data;
12
+ if (!data || typeof data !== "object") return;
13
+ if (data.source !== "throttle" || data.version !== 1) return;
14
+ if (typeof data.type !== "string" || !data.type.startsWith("throttle.")) return;
15
+ onMessage(data);
16
+ }
17
+ window.addEventListener("message", handle);
18
+ return () => window.removeEventListener("message", handle);
19
+ }, [iframeRef, expectedOrigin, onMessage]);
20
+ }
21
+
22
+ // src/CheckoutEmbed.tsx
3
23
  import { jsx } from "react/jsx-runtime";
4
- var DEFAULT_BASE_URL = "https://throttle-checkout-web.onrender.com";
5
- function ThrottleCheckout({
24
+ var DEFAULT_BASE_URL = "https://checkout.usethrottle.dev";
25
+ function CheckoutEmbed({
6
26
  sessionId,
27
+ parentOrigin,
7
28
  baseUrl = DEFAULT_BASE_URL,
8
- initialHeight = 480,
29
+ primary,
30
+ logo,
31
+ initialHeight = 600,
9
32
  className,
10
33
  style,
11
34
  onReady,
12
- onCompleted,
13
- onCancelled,
14
- onError
35
+ onProcessing,
36
+ onSucceeded,
37
+ onFailed,
38
+ onCanceled,
39
+ onStepChanged
15
40
  }) {
16
41
  const iframeRef = useRef(null);
17
- useEffect(() => {
18
- function handle(ev) {
19
- const data = ev?.data;
20
- if (!data || typeof data !== "object" || typeof data.type !== "string") return;
21
- if (!data.type.startsWith("throttle.")) return;
22
- if (iframeRef.current && ev.source !== iframeRef.current.contentWindow) return;
23
- if (data.type === "throttle.resize" && typeof data.height === "number" && iframeRef.current) {
24
- iframeRef.current.style.height = `${data.height}px`;
42
+ const expectedOrigin = new URL(baseUrl).origin;
43
+ const dispatch = useCallback(
44
+ (msg) => {
45
+ switch (msg.type) {
46
+ case "throttle.ready":
47
+ onReady?.();
48
+ break;
49
+ case "throttle.processing":
50
+ onProcessing?.();
51
+ break;
52
+ case "throttle.completed":
53
+ onSucceeded?.({ orderId: msg.orderId, paymentId: msg.paymentId });
54
+ break;
55
+ case "throttle.error":
56
+ onFailed?.({ code: msg.code, message: msg.message });
57
+ break;
58
+ case "throttle.cancelled":
59
+ onCanceled?.();
60
+ break;
61
+ case "throttle.step.changed":
62
+ onStepChanged?.({ step: msg.step });
63
+ break;
64
+ case "throttle.resize":
65
+ if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;
66
+ break;
25
67
  }
26
- if (data.type === "throttle.ready") onReady?.();
27
- else if (data.type === "throttle.completed") onCompleted?.(data.orderId, data.paymentId);
28
- else if (data.type === "throttle.cancelled") onCancelled?.();
29
- else if (data.type === "throttle.error") onError?.(data.code, data.message);
30
- }
31
- window.addEventListener("message", handle);
32
- return () => window.removeEventListener("message", handle);
33
- }, [onReady, onCompleted, onCancelled, onError]);
68
+ },
69
+ [onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged]
70
+ );
71
+ useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });
72
+ const params = new URLSearchParams({ embed: "1", mode: "checkout-full", parentOrigin });
73
+ if (primary) params.set("primary", primary);
74
+ if (logo) params.set("logo", logo);
34
75
  const cleanBase = baseUrl.replace(/\/$/, "");
35
- const src = `${cleanBase}/c/${encodeURIComponent(sessionId)}?embed=1`;
76
+ const src = `${cleanBase}/c/${encodeURIComponent(sessionId)}?${params.toString()}`;
36
77
  return /* @__PURE__ */ jsx(
37
78
  "iframe",
38
79
  {
@@ -45,7 +86,75 @@ function ThrottleCheckout({
45
86
  }
46
87
  );
47
88
  }
89
+
90
+ // src/PaymentEmbed.tsx
91
+ import { useCallback as useCallback2, useRef as useRef2 } from "react";
92
+ import { jsx as jsx2 } from "react/jsx-runtime";
93
+ var DEFAULT_BASE_URL2 = "https://checkout.usethrottle.dev";
94
+ function PaymentEmbed({
95
+ sessionId,
96
+ parentOrigin,
97
+ baseUrl = DEFAULT_BASE_URL2,
98
+ primary,
99
+ logo,
100
+ initialHeight = 480,
101
+ className,
102
+ style,
103
+ onReady,
104
+ onProcessing,
105
+ onSucceeded,
106
+ onFailed,
107
+ onCanceled
108
+ }) {
109
+ const iframeRef = useRef2(null);
110
+ const expectedOrigin = new URL(baseUrl).origin;
111
+ const dispatch = useCallback2(
112
+ (msg) => {
113
+ switch (msg.type) {
114
+ case "throttle.ready":
115
+ onReady?.();
116
+ break;
117
+ case "throttle.processing":
118
+ onProcessing?.();
119
+ break;
120
+ case "throttle.completed":
121
+ onSucceeded?.({ orderId: msg.orderId, paymentId: msg.paymentId });
122
+ break;
123
+ case "throttle.error":
124
+ onFailed?.({ code: msg.code, message: msg.message });
125
+ break;
126
+ case "throttle.cancelled":
127
+ onCanceled?.();
128
+ break;
129
+ case "throttle.resize":
130
+ if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;
131
+ break;
132
+ }
133
+ },
134
+ [onReady, onProcessing, onSucceeded, onFailed, onCanceled]
135
+ );
136
+ useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });
137
+ const params = new URLSearchParams({ embed: "1", mode: "payment-only", parentOrigin });
138
+ if (primary) params.set("primary", primary);
139
+ if (logo) params.set("logo", logo);
140
+ const cleanBase = baseUrl.replace(/\/$/, "");
141
+ const src = `${cleanBase}/c/${encodeURIComponent(sessionId)}?${params.toString()}`;
142
+ return /* @__PURE__ */ jsx2(
143
+ "iframe",
144
+ {
145
+ ref: iframeRef,
146
+ src,
147
+ className,
148
+ style: { border: 0, width: "100%", minHeight: initialHeight, ...style },
149
+ allow: "payment *",
150
+ title: "Throttle payment"
151
+ }
152
+ );
153
+ }
48
154
  export {
49
- ThrottleCheckout
155
+ CheckoutEmbed,
156
+ PaymentEmbed,
157
+ CheckoutEmbed as ThrottleCheckout,
158
+ useThrottleEvents
50
159
  };
51
160
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\nexport interface ThrottleCheckoutProps {\n /** The throttle checkout session id (sess_*). Required. */\n sessionId: string;\n /** Base URL of the deployed checkout-web app. Defaults to https://throttle-checkout-web.onrender.com */\n baseUrl?: string;\n /** Initial iframe height in pixels. Auto-resizes on throttle.resize events. */\n initialHeight?: number;\n /** Optional className for the iframe. */\n className?: string;\n /** Optional inline style for the iframe. */\n style?: React.CSSProperties;\n /** Fires once the iframe is ready to receive interactions. */\n onReady?: () => void;\n /** Fires after a successful payment. */\n onCompleted?: (orderId: string, paymentId: string) => void;\n /** Fires when the buyer cancels checkout. */\n onCancelled?: () => void;\n /** Fires on any error inside the iframe (Gr4vy decline, network, etc.). */\n onError?: (code: string, message: string) => void;\n}\n\nconst DEFAULT_BASE_URL = 'https://throttle-checkout-web.onrender.com';\n\n/**\n * Embedded Throttle checkout for React apps.\n *\n * Renders an iframe pointed at <baseUrl>/c/<sessionId>?embed=1 and\n * subscribes to the postMessage v1 protocol. Events fire on the\n * provided callbacks. Auto-resizes the iframe on throttle.resize.\n */\nexport function ThrottleCheckout({\n sessionId,\n baseUrl = DEFAULT_BASE_URL,\n initialHeight = 480,\n className,\n style,\n onReady,\n onCompleted,\n onCancelled,\n onError,\n}: ThrottleCheckoutProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n\n useEffect(() => {\n function handle(ev: MessageEvent) {\n const data = ev?.data;\n if (!data || typeof data !== 'object' || typeof data.type !== 'string') return;\n if (!data.type.startsWith('throttle.')) return;\n if (iframeRef.current && ev.source !== iframeRef.current.contentWindow) return;\n\n if (data.type === 'throttle.resize' && typeof data.height === 'number' && iframeRef.current) {\n iframeRef.current.style.height = `${data.height}px`;\n }\n if (data.type === 'throttle.ready') onReady?.();\n else if (data.type === 'throttle.completed') onCompleted?.(data.orderId, data.paymentId);\n else if (data.type === 'throttle.cancelled') onCancelled?.();\n else if (data.type === 'throttle.error') onError?.(data.code, data.message);\n }\n window.addEventListener('message', handle);\n return () => window.removeEventListener('message', handle);\n }, [onReady, onCompleted, onCancelled, onError]);\n\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/c/${encodeURIComponent(sessionId)}?embed=1`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle checkout\"\n />\n );\n}\n"],"mappings":";AAAA,SAAS,WAAW,cAAc;AAoE9B;AA7CJ,IAAM,mBAAmB;AASlB,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,QAAM,YAAY,OAA0B,IAAI;AAEhD,YAAU,MAAM;AACd,aAAS,OAAO,IAAkB;AAChC,YAAM,OAAO,IAAI;AACjB,UAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,OAAO,KAAK,SAAS,SAAU;AACxE,UAAI,CAAC,KAAK,KAAK,WAAW,WAAW,EAAG;AACxC,UAAI,UAAU,WAAW,GAAG,WAAW,UAAU,QAAQ,cAAe;AAExE,UAAI,KAAK,SAAS,qBAAqB,OAAO,KAAK,WAAW,YAAY,UAAU,SAAS;AAC3F,kBAAU,QAAQ,MAAM,SAAS,GAAG,KAAK,MAAM;AAAA,MACjD;AACA,UAAI,KAAK,SAAS,iBAAkB,WAAU;AAAA,eACrC,KAAK,SAAS,qBAAsB,eAAc,KAAK,SAAS,KAAK,SAAS;AAAA,eAC9E,KAAK,SAAS,qBAAsB,eAAc;AAAA,eAClD,KAAK,SAAS,iBAAkB,WAAU,KAAK,MAAM,KAAK,OAAO;AAAA,IAC5E;AACA,WAAO,iBAAiB,WAAW,MAAM;AACzC,WAAO,MAAM,OAAO,oBAAoB,WAAW,MAAM;AAAA,EAC3D,GAAG,CAAC,SAAS,aAAa,aAAa,OAAO,CAAC;AAE/C,QAAM,YAAY,QAAQ,QAAQ,OAAO,EAAE;AAC3C,QAAM,MAAM,GAAG,SAAS,MAAM,mBAAmB,SAAS,CAAC;AAE3D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,OAAO,EAAE,QAAQ,GAAG,OAAO,QAAQ,WAAW,eAAe,GAAG,MAAM;AAAA,MACtE,OAAM;AAAA,MACN,OAAM;AAAA;AAAA,EACR;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../src/CheckoutEmbed.tsx","../src/useThrottleEvents.ts","../src/PaymentEmbed.tsx"],"sourcesContent":["import { useCallback, useRef } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport type {\n ThrottleEmbedProps,\n CheckoutEmbedExtraProps,\n ThrottleMessage,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Full-checkout iframe embed. Renders the entire Throttle hosted\n * checkout (address / shipping / payment) inside an iframe and\n * surfaces lifecycle + terminal events through callbacks. The\n * iframe auto-resizes on `throttle.resize` events.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list\n * (`throttle embed-config set --origins ...`). Mismatches render\n * an in-iframe \"Embed not authorized\" panel.\n */\nexport function CheckoutEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n primary,\n logo,\n initialHeight = 600,\n className,\n style,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n onStepChanged,\n}: ThrottleEmbedProps & CheckoutEmbedExtraProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({ orderId: msg.orderId, paymentId: msg.paymentId });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.step.changed':\n onStepChanged?.({ step: msg.step });\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n\n const params = new URLSearchParams({ embed: '1', mode: 'checkout-full', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/c/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle checkout\"\n />\n );\n}\n","import { useEffect, type RefObject } from 'react';\nimport type { ThrottleMessage } from './types.js';\n\nexport interface UseThrottleEventsOptions {\n iframeRef: RefObject<HTMLIFrameElement | null>;\n /**\n * The Throttle origin (e.g. https://checkout.usethrottle.dev).\n * Strict-equals check against `MessageEvent.origin`. Anything else\n * is silently dropped.\n */\n expectedOrigin: string;\n onMessage: (msg: ThrottleMessage) => void;\n}\n\n/**\n * Subscribe to validated postMessage events from a Throttle iframe.\n *\n * Validation:\n * - event.origin === expectedOrigin (strict string match)\n * - event.source === iframeRef.current.contentWindow (defends against\n * other iframes on the same origin spoofing events)\n * - event.data.source === 'throttle' && event.data.version === 1\n * - event.data.type starts with 'throttle.'\n *\n * Anything failing validation is silently dropped — the parent page\n * may receive postMessages from analytics SDKs, browser extensions,\n * other iframes, etc. We must never throw on those.\n */\nexport function useThrottleEvents({ iframeRef, expectedOrigin, onMessage }: UseThrottleEventsOptions) {\n useEffect(() => {\n function handle(ev: MessageEvent) {\n if (ev.origin !== expectedOrigin) return;\n if (iframeRef.current && ev.source !== iframeRef.current.contentWindow) return;\n const data = ev.data as Partial<ThrottleMessage> | null | undefined;\n if (!data || typeof data !== 'object') return;\n if (data.source !== 'throttle' || data.version !== 1) return;\n if (typeof data.type !== 'string' || !data.type.startsWith('throttle.')) return;\n onMessage(data as ThrottleMessage);\n }\n window.addEventListener('message', handle);\n return () => window.removeEventListener('message', handle);\n }, [iframeRef, expectedOrigin, onMessage]);\n}\n","import { useCallback, useRef } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport type { ThrottleEmbedProps, ThrottleMessage } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Payment-only iframe embed. Renders just the Gr4vy provider iframe\n * (no address / shipping / cart UI). Email is collected by the Gr4vy\n * iframe in proxy-mode sessions. Used when the merchant has built\n * their own checkout UI and only needs Throttle to handle payment\n * authorization + capture.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list.\n * The iframe auto-resizes on `throttle.resize` events.\n */\nexport function PaymentEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n primary,\n logo,\n initialHeight = 480,\n className,\n style,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n}: ThrottleEmbedProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({ orderId: msg.orderId, paymentId: msg.paymentId });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n\n const params = new URLSearchParams({ embed: '1', mode: 'payment-only', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/c/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle payment\"\n />\n );\n}\n"],"mappings":"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","names":["useCallback","useRef","jsx","DEFAULT_BASE_URL","useRef","useCallback"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usethrottle/checkout-react",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "React component for embedded Throttle checkout",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -13,13 +13,10 @@
13
13
  "require": "./dist/index.cjs"
14
14
  }
15
15
  },
16
- "files": ["dist", "README.md"],
17
- "scripts": {
18
- "build": "tsup",
19
- "dev": "tsup --watch",
20
- "test": "vitest run",
21
- "test:watch": "vitest"
22
- },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
23
20
  "peerDependencies": {
24
21
  "react": ">=18"
25
22
  },
@@ -36,5 +33,11 @@
36
33
  },
37
34
  "publishConfig": {
38
35
  "access": "public"
36
+ },
37
+ "scripts": {
38
+ "build": "tsup",
39
+ "dev": "tsup --watch",
40
+ "test": "vitest run",
41
+ "test:watch": "vitest"
39
42
  }
40
- }
43
+ }