@inttegro/react 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Inttegro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # `@inttegro/react`
2
+
3
+ React adapter for Inttegro hosted workflows. The component automatically uses
4
+ `@inttegro/js` to load the executable runtime from Inttegro's fixed origin; it
5
+ does not bundle the payment-collection runtime.
6
+
7
+ ```tsx
8
+ import { Checkout } from '@inttegro/react'
9
+
10
+ export function Payment({ orderId }: { orderId: string }) {
11
+ return (
12
+ <Checkout
13
+ orderId={orderId}
14
+ onCompleted={() => location.assign('/complete')}
15
+ />
16
+ )
17
+ }
18
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Checkout: () => Checkout
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/Checkout.tsx
28
+ var import_js = require("@inttegro/js");
29
+ var import_react = require("react");
30
+ var import_jsx_runtime = require("react/jsx-runtime");
31
+ var Checkout = (0, import_react.forwardRef)(
32
+ function Checkout2({
33
+ appearance,
34
+ className,
35
+ locale,
36
+ onCompleted,
37
+ onError,
38
+ onEvent,
39
+ onReady,
40
+ orderId,
41
+ style,
42
+ timeout,
43
+ title
44
+ }, forwardedRef) {
45
+ const containerRef = (0, import_react.useRef)(null);
46
+ const checkoutRef = (0, import_react.useRef)(null);
47
+ const callbacksRef = (0, import_react.useRef)({ onCompleted, onError, onEvent, onReady });
48
+ const updateOptionsRef = (0, import_react.useRef)({ appearance, locale });
49
+ callbacksRef.current = { onCompleted, onError, onEvent, onReady };
50
+ updateOptionsRef.current = { appearance, locale };
51
+ (0, import_react.useImperativeHandle)(
52
+ forwardedRef,
53
+ () => ({
54
+ focus: () => checkoutRef.current?.focus(),
55
+ update: (options) => checkoutRef.current?.update(options)
56
+ }),
57
+ []
58
+ );
59
+ (0, import_react.useEffect)(() => {
60
+ const target = containerRef.current;
61
+ if (!target) return;
62
+ let active = true;
63
+ let checkout;
64
+ let unsubscribe;
65
+ void (0, import_js.loadInttegro)().then(async (inttegro) => {
66
+ if (!active || !inttegro) return;
67
+ const instance = inttegro.createCheckout(
68
+ definedOptions({
69
+ ...updateOptionsRef.current,
70
+ orderId,
71
+ timeout,
72
+ title
73
+ })
74
+ );
75
+ if (!active) {
76
+ instance.destroy();
77
+ return;
78
+ }
79
+ checkout = instance;
80
+ checkoutRef.current = instance;
81
+ unsubscribe = instance.onEvent((event) => {
82
+ callbacksRef.current.onEvent?.(event);
83
+ if (event.type === "ready") callbacksRef.current.onReady?.(event);
84
+ if (event.type === "completed")
85
+ callbacksRef.current.onCompleted?.(event);
86
+ if (event.type === "error") callbacksRef.current.onError?.(event);
87
+ });
88
+ await instance.mount(target);
89
+ }).catch((error) => {
90
+ if (active) callbacksRef.current.onError?.(asError(error));
91
+ });
92
+ return () => {
93
+ active = false;
94
+ unsubscribe?.();
95
+ checkout?.destroy();
96
+ if (checkoutRef.current === checkout) checkoutRef.current = null;
97
+ };
98
+ }, [orderId, timeout, title]);
99
+ (0, import_react.useEffect)(() => {
100
+ checkoutRef.current?.update(definedOptions({ appearance, locale }));
101
+ }, [appearance, locale]);
102
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, ref: containerRef, style });
103
+ }
104
+ );
105
+ function definedOptions(options) {
106
+ return Object.fromEntries(
107
+ Object.entries(options).filter(([, value]) => value !== void 0)
108
+ );
109
+ }
110
+ function asError(error) {
111
+ return error instanceof Error ? error : new Error("Checkout failed to load.");
112
+ }
113
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/Checkout.tsx"],"sourcesContent":["/**\n * React component adapter for Inttegro-hosted Checkout.\n *\n * @module @inttegro/react\n */\nexport { Checkout, type CheckoutHandle, type CheckoutProps } from './Checkout'\nexport type {\n CheckoutAppearance,\n CheckoutErrorEvent,\n CheckoutEvent,\n CheckoutTheme,\n} from '@inttegro/js'\n","import {\n loadInttegro,\n type CheckoutController,\n type CheckoutErrorEvent,\n type CheckoutEvent,\n type CheckoutOptions,\n} from '@inttegro/js'\nimport {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n type CSSProperties,\n} from 'react'\n\n/**\n * Props for {@link Checkout}.\n *\n * `orderId`, `timeout`, and `title` identify the embedded experience. Changing\n * one replaces the underlying controller. `appearance` and `locale` update the\n * existing controller without discarding payer progress.\n *\n * @category React\n */\nexport interface CheckoutProps extends CheckoutOptions {\n /** CSS class applied to the outer container owned by your React application. */\n className?: string\n /** Inline styles applied to the outer container, never to controls inside Checkout. */\n style?: CSSProperties\n /**\n * Called after Checkout reports its successful terminal state.\n * Use this to navigate or refresh server-owned Order state, not as the sole\n * signal for fulfillment.\n */\n onCompleted?: (event: Extract<CheckoutEvent, { type: 'completed' }>) => void\n /**\n * Called for loader/mount errors and sanitized hosted errors.\n * Use `error instanceof InttegroCheckoutError` or check for an event `type`\n * before reading framework-independent details.\n */\n onError?: (event: CheckoutErrorEvent | Error) => void\n /** Called for every sanitized lifecycle event, including events above. */\n onEvent?: (event: CheckoutEvent) => void\n /** Called once the iframe is interactive and its mount promise has resolved. */\n onReady?: (event: Extract<CheckoutEvent, { type: 'ready' }>) => void\n}\n\n/**\n * Imperative operations exposed by {@link Checkout} through a React ref.\n *\n * Prefer props for normal updates. Use the handle for focus management in\n * dialogs or for updates driven by imperative host integrations.\n *\n * @category React\n */\nexport interface CheckoutHandle {\n /** Moves focus into Checkout when its controller is ready; otherwise does nothing. */\n focus(): void\n /** Applies a new locale or theme when the controller exists. */\n update(options: Parameters<CheckoutController['update']>[0]): void\n}\n\n/**\n * Embeds Inttegro-hosted Checkout in a React application.\n *\n * The component renders an empty `div`, loads the executable runtime from\n * Inttegro's fixed origin after mount, creates a controller for `orderId`, and\n * destroys it during cleanup. Server rendering is safe because no runtime is\n * loaded until the effect runs in a browser.\n *\n * Changing `orderId`, `timeout`, or `title` starts a fresh hosted experience.\n * Changing `appearance` or `locale` updates the active experience in place.\n * Keep the component mounted while a payment is pending or confirmation is in\n * progress.\n *\n * @example Handle success, failure, and focus in a dialog\n * ```tsx\n * import { useRef } from 'react'\n * import { Checkout, type CheckoutHandle } from '@inttegro/react'\n *\n * function Payment({ orderId }: { orderId: string }) {\n * const checkout = useRef<CheckoutHandle>(null)\n *\n * return (\n * <dialog open onTransitionEnd={() => checkout.current?.focus()}>\n * <Checkout\n * ref={checkout}\n * orderId={orderId}\n * appearance={{ theme: 'system' }}\n * onCompleted={() => location.assign(`/orders/${orderId}/complete`)}\n * onError={(error) => reportCheckoutError(error)}\n * />\n * </dialog>\n * )\n * }\n * ```\n *\n * @category React\n */\nexport const Checkout = forwardRef<CheckoutHandle, CheckoutProps>(\n function Checkout(\n {\n appearance,\n className,\n locale,\n onCompleted,\n onError,\n onEvent,\n onReady,\n orderId,\n style,\n timeout,\n title,\n },\n forwardedRef,\n ) {\n const containerRef = useRef<HTMLDivElement>(null)\n const checkoutRef = useRef<CheckoutController | null>(null)\n const callbacksRef = useRef({ onCompleted, onError, onEvent, onReady })\n const updateOptionsRef = useRef({ appearance, locale })\n callbacksRef.current = { onCompleted, onError, onEvent, onReady }\n updateOptionsRef.current = { appearance, locale }\n\n useImperativeHandle(\n forwardedRef,\n () => ({\n focus: () => checkoutRef.current?.focus(),\n update: (options) => checkoutRef.current?.update(options),\n }),\n [],\n )\n\n useEffect(() => {\n const target = containerRef.current\n if (!target) return\n\n let active = true\n let checkout: CheckoutController | undefined\n let unsubscribe: (() => void) | undefined\n\n void loadInttegro()\n .then(async (inttegro) => {\n if (!active || !inttegro) return\n const instance = inttegro.createCheckout(\n definedOptions({\n ...updateOptionsRef.current,\n orderId,\n timeout,\n title,\n }),\n )\n if (!active) {\n instance.destroy()\n return\n }\n checkout = instance\n checkoutRef.current = instance\n unsubscribe = instance.onEvent((event) => {\n callbacksRef.current.onEvent?.(event)\n if (event.type === 'ready') callbacksRef.current.onReady?.(event)\n if (event.type === 'completed')\n callbacksRef.current.onCompleted?.(event)\n if (event.type === 'error') callbacksRef.current.onError?.(event)\n })\n await instance.mount(target)\n })\n .catch((error: unknown) => {\n if (active) callbacksRef.current.onError?.(asError(error))\n })\n\n return () => {\n active = false\n unsubscribe?.()\n checkout?.destroy()\n if (checkoutRef.current === checkout) checkoutRef.current = null\n }\n }, [orderId, timeout, title])\n\n useEffect(() => {\n checkoutRef.current?.update(definedOptions({ appearance, locale }))\n }, [appearance, locale])\n\n return <div className={className} ref={containerRef} style={style} />\n },\n)\n\nfunction definedOptions<Value extends object>(options: Value): Value {\n return Object.fromEntries(\n Object.entries(options).filter(([, value]) => value !== undefined),\n ) as Value\n}\n\nfunction asError(error: unknown): Error {\n return error instanceof Error ? error : new Error('Checkout failed to load.')\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,gBAMO;AACP,mBAMO;AAyKI;AAnFJ,IAAM,eAAW;AAAA,EACtB,SAASA,UACP;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GACA,cACA;AACA,UAAM,mBAAe,qBAAuB,IAAI;AAChD,UAAM,kBAAc,qBAAkC,IAAI;AAC1D,UAAM,mBAAe,qBAAO,EAAE,aAAa,SAAS,SAAS,QAAQ,CAAC;AACtE,UAAM,uBAAmB,qBAAO,EAAE,YAAY,OAAO,CAAC;AACtD,iBAAa,UAAU,EAAE,aAAa,SAAS,SAAS,QAAQ;AAChE,qBAAiB,UAAU,EAAE,YAAY,OAAO;AAEhD;AAAA,MACE;AAAA,MACA,OAAO;AAAA,QACL,OAAO,MAAM,YAAY,SAAS,MAAM;AAAA,QACxC,QAAQ,CAAC,YAAY,YAAY,SAAS,OAAO,OAAO;AAAA,MAC1D;AAAA,MACA,CAAC;AAAA,IACH;AAEA,gCAAU,MAAM;AACd,YAAM,SAAS,aAAa;AAC5B,UAAI,CAAC,OAAQ;AAEb,UAAI,SAAS;AACb,UAAI;AACJ,UAAI;AAEJ,eAAK,wBAAa,EACf,KAAK,OAAO,aAAa;AACxB,YAAI,CAAC,UAAU,CAAC,SAAU;AAC1B,cAAM,WAAW,SAAS;AAAA,UACxB,eAAe;AAAA,YACb,GAAG,iBAAiB;AAAA,YACpB;AAAA,YACA;AAAA,YACA;AAAA,UACF,CAAC;AAAA,QACH;AACA,YAAI,CAAC,QAAQ;AACX,mBAAS,QAAQ;AACjB;AAAA,QACF;AACA,mBAAW;AACX,oBAAY,UAAU;AACtB,sBAAc,SAAS,QAAQ,CAAC,UAAU;AACxC,uBAAa,QAAQ,UAAU,KAAK;AACpC,cAAI,MAAM,SAAS,QAAS,cAAa,QAAQ,UAAU,KAAK;AAChE,cAAI,MAAM,SAAS;AACjB,yBAAa,QAAQ,cAAc,KAAK;AAC1C,cAAI,MAAM,SAAS,QAAS,cAAa,QAAQ,UAAU,KAAK;AAAA,QAClE,CAAC;AACD,cAAM,SAAS,MAAM,MAAM;AAAA,MAC7B,CAAC,EACA,MAAM,CAAC,UAAmB;AACzB,YAAI,OAAQ,cAAa,QAAQ,UAAU,QAAQ,KAAK,CAAC;AAAA,MAC3D,CAAC;AAEH,aAAO,MAAM;AACX,iBAAS;AACT,sBAAc;AACd,kBAAU,QAAQ;AAClB,YAAI,YAAY,YAAY,SAAU,aAAY,UAAU;AAAA,MAC9D;AAAA,IACF,GAAG,CAAC,SAAS,SAAS,KAAK,CAAC;AAE5B,gCAAU,MAAM;AACd,kBAAY,SAAS,OAAO,eAAe,EAAE,YAAY,OAAO,CAAC,CAAC;AAAA,IACpE,GAAG,CAAC,YAAY,MAAM,CAAC;AAEvB,WAAO,4CAAC,SAAI,WAAsB,KAAK,cAAc,OAAc;AAAA,EACrE;AACF;AAEA,SAAS,eAAqC,SAAuB;AACnE,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,UAAU,MAAS;AAAA,EACnE;AACF;AAEA,SAAS,QAAQ,OAAuB;AACtC,SAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,0BAA0B;AAC9E;","names":["Checkout"]}
@@ -0,0 +1,94 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties } from 'react';
3
+ import { CheckoutOptions, CheckoutEvent, CheckoutErrorEvent, CheckoutController } from '@inttegro/js';
4
+ export { CheckoutAppearance, CheckoutErrorEvent, CheckoutEvent, CheckoutTheme } from '@inttegro/js';
5
+
6
+ /**
7
+ * Props for {@link Checkout}.
8
+ *
9
+ * `orderId`, `timeout`, and `title` identify the embedded experience. Changing
10
+ * one replaces the underlying controller. `appearance` and `locale` update the
11
+ * existing controller without discarding payer progress.
12
+ *
13
+ * @category React
14
+ */
15
+ interface CheckoutProps extends CheckoutOptions {
16
+ /** CSS class applied to the outer container owned by your React application. */
17
+ className?: string;
18
+ /** Inline styles applied to the outer container, never to controls inside Checkout. */
19
+ style?: CSSProperties;
20
+ /**
21
+ * Called after Checkout reports its successful terminal state.
22
+ * Use this to navigate or refresh server-owned Order state, not as the sole
23
+ * signal for fulfillment.
24
+ */
25
+ onCompleted?: (event: Extract<CheckoutEvent, {
26
+ type: 'completed';
27
+ }>) => void;
28
+ /**
29
+ * Called for loader/mount errors and sanitized hosted errors.
30
+ * Use `error instanceof InttegroCheckoutError` or check for an event `type`
31
+ * before reading framework-independent details.
32
+ */
33
+ onError?: (event: CheckoutErrorEvent | Error) => void;
34
+ /** Called for every sanitized lifecycle event, including events above. */
35
+ onEvent?: (event: CheckoutEvent) => void;
36
+ /** Called once the iframe is interactive and its mount promise has resolved. */
37
+ onReady?: (event: Extract<CheckoutEvent, {
38
+ type: 'ready';
39
+ }>) => void;
40
+ }
41
+ /**
42
+ * Imperative operations exposed by {@link Checkout} through a React ref.
43
+ *
44
+ * Prefer props for normal updates. Use the handle for focus management in
45
+ * dialogs or for updates driven by imperative host integrations.
46
+ *
47
+ * @category React
48
+ */
49
+ interface CheckoutHandle {
50
+ /** Moves focus into Checkout when its controller is ready; otherwise does nothing. */
51
+ focus(): void;
52
+ /** Applies a new locale or theme when the controller exists. */
53
+ update(options: Parameters<CheckoutController['update']>[0]): void;
54
+ }
55
+ /**
56
+ * Embeds Inttegro-hosted Checkout in a React application.
57
+ *
58
+ * The component renders an empty `div`, loads the executable runtime from
59
+ * Inttegro's fixed origin after mount, creates a controller for `orderId`, and
60
+ * destroys it during cleanup. Server rendering is safe because no runtime is
61
+ * loaded until the effect runs in a browser.
62
+ *
63
+ * Changing `orderId`, `timeout`, or `title` starts a fresh hosted experience.
64
+ * Changing `appearance` or `locale` updates the active experience in place.
65
+ * Keep the component mounted while a payment is pending or confirmation is in
66
+ * progress.
67
+ *
68
+ * @example Handle success, failure, and focus in a dialog
69
+ * ```tsx
70
+ * import { useRef } from 'react'
71
+ * import { Checkout, type CheckoutHandle } from '@inttegro/react'
72
+ *
73
+ * function Payment({ orderId }: { orderId: string }) {
74
+ * const checkout = useRef<CheckoutHandle>(null)
75
+ *
76
+ * return (
77
+ * <dialog open onTransitionEnd={() => checkout.current?.focus()}>
78
+ * <Checkout
79
+ * ref={checkout}
80
+ * orderId={orderId}
81
+ * appearance={{ theme: 'system' }}
82
+ * onCompleted={() => location.assign(`/orders/${orderId}/complete`)}
83
+ * onError={(error) => reportCheckoutError(error)}
84
+ * />
85
+ * </dialog>
86
+ * )
87
+ * }
88
+ * ```
89
+ *
90
+ * @category React
91
+ */
92
+ declare const Checkout: react.ForwardRefExoticComponent<CheckoutProps & react.RefAttributes<CheckoutHandle>>;
93
+
94
+ export { Checkout, type CheckoutHandle, type CheckoutProps };
@@ -0,0 +1,94 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties } from 'react';
3
+ import { CheckoutOptions, CheckoutEvent, CheckoutErrorEvent, CheckoutController } from '@inttegro/js';
4
+ export { CheckoutAppearance, CheckoutErrorEvent, CheckoutEvent, CheckoutTheme } from '@inttegro/js';
5
+
6
+ /**
7
+ * Props for {@link Checkout}.
8
+ *
9
+ * `orderId`, `timeout`, and `title` identify the embedded experience. Changing
10
+ * one replaces the underlying controller. `appearance` and `locale` update the
11
+ * existing controller without discarding payer progress.
12
+ *
13
+ * @category React
14
+ */
15
+ interface CheckoutProps extends CheckoutOptions {
16
+ /** CSS class applied to the outer container owned by your React application. */
17
+ className?: string;
18
+ /** Inline styles applied to the outer container, never to controls inside Checkout. */
19
+ style?: CSSProperties;
20
+ /**
21
+ * Called after Checkout reports its successful terminal state.
22
+ * Use this to navigate or refresh server-owned Order state, not as the sole
23
+ * signal for fulfillment.
24
+ */
25
+ onCompleted?: (event: Extract<CheckoutEvent, {
26
+ type: 'completed';
27
+ }>) => void;
28
+ /**
29
+ * Called for loader/mount errors and sanitized hosted errors.
30
+ * Use `error instanceof InttegroCheckoutError` or check for an event `type`
31
+ * before reading framework-independent details.
32
+ */
33
+ onError?: (event: CheckoutErrorEvent | Error) => void;
34
+ /** Called for every sanitized lifecycle event, including events above. */
35
+ onEvent?: (event: CheckoutEvent) => void;
36
+ /** Called once the iframe is interactive and its mount promise has resolved. */
37
+ onReady?: (event: Extract<CheckoutEvent, {
38
+ type: 'ready';
39
+ }>) => void;
40
+ }
41
+ /**
42
+ * Imperative operations exposed by {@link Checkout} through a React ref.
43
+ *
44
+ * Prefer props for normal updates. Use the handle for focus management in
45
+ * dialogs or for updates driven by imperative host integrations.
46
+ *
47
+ * @category React
48
+ */
49
+ interface CheckoutHandle {
50
+ /** Moves focus into Checkout when its controller is ready; otherwise does nothing. */
51
+ focus(): void;
52
+ /** Applies a new locale or theme when the controller exists. */
53
+ update(options: Parameters<CheckoutController['update']>[0]): void;
54
+ }
55
+ /**
56
+ * Embeds Inttegro-hosted Checkout in a React application.
57
+ *
58
+ * The component renders an empty `div`, loads the executable runtime from
59
+ * Inttegro's fixed origin after mount, creates a controller for `orderId`, and
60
+ * destroys it during cleanup. Server rendering is safe because no runtime is
61
+ * loaded until the effect runs in a browser.
62
+ *
63
+ * Changing `orderId`, `timeout`, or `title` starts a fresh hosted experience.
64
+ * Changing `appearance` or `locale` updates the active experience in place.
65
+ * Keep the component mounted while a payment is pending or confirmation is in
66
+ * progress.
67
+ *
68
+ * @example Handle success, failure, and focus in a dialog
69
+ * ```tsx
70
+ * import { useRef } from 'react'
71
+ * import { Checkout, type CheckoutHandle } from '@inttegro/react'
72
+ *
73
+ * function Payment({ orderId }: { orderId: string }) {
74
+ * const checkout = useRef<CheckoutHandle>(null)
75
+ *
76
+ * return (
77
+ * <dialog open onTransitionEnd={() => checkout.current?.focus()}>
78
+ * <Checkout
79
+ * ref={checkout}
80
+ * orderId={orderId}
81
+ * appearance={{ theme: 'system' }}
82
+ * onCompleted={() => location.assign(`/orders/${orderId}/complete`)}
83
+ * onError={(error) => reportCheckoutError(error)}
84
+ * />
85
+ * </dialog>
86
+ * )
87
+ * }
88
+ * ```
89
+ *
90
+ * @category React
91
+ */
92
+ declare const Checkout: react.ForwardRefExoticComponent<CheckoutProps & react.RefAttributes<CheckoutHandle>>;
93
+
94
+ export { Checkout, type CheckoutHandle, type CheckoutProps };
package/dist/index.js ADDED
@@ -0,0 +1,97 @@
1
+ // src/Checkout.tsx
2
+ import {
3
+ loadInttegro
4
+ } from "@inttegro/js";
5
+ import {
6
+ forwardRef,
7
+ useEffect,
8
+ useImperativeHandle,
9
+ useRef
10
+ } from "react";
11
+ import { jsx } from "react/jsx-runtime";
12
+ var Checkout = forwardRef(
13
+ function Checkout2({
14
+ appearance,
15
+ className,
16
+ locale,
17
+ onCompleted,
18
+ onError,
19
+ onEvent,
20
+ onReady,
21
+ orderId,
22
+ style,
23
+ timeout,
24
+ title
25
+ }, forwardedRef) {
26
+ const containerRef = useRef(null);
27
+ const checkoutRef = useRef(null);
28
+ const callbacksRef = useRef({ onCompleted, onError, onEvent, onReady });
29
+ const updateOptionsRef = useRef({ appearance, locale });
30
+ callbacksRef.current = { onCompleted, onError, onEvent, onReady };
31
+ updateOptionsRef.current = { appearance, locale };
32
+ useImperativeHandle(
33
+ forwardedRef,
34
+ () => ({
35
+ focus: () => checkoutRef.current?.focus(),
36
+ update: (options) => checkoutRef.current?.update(options)
37
+ }),
38
+ []
39
+ );
40
+ useEffect(() => {
41
+ const target = containerRef.current;
42
+ if (!target) return;
43
+ let active = true;
44
+ let checkout;
45
+ let unsubscribe;
46
+ void loadInttegro().then(async (inttegro) => {
47
+ if (!active || !inttegro) return;
48
+ const instance = inttegro.createCheckout(
49
+ definedOptions({
50
+ ...updateOptionsRef.current,
51
+ orderId,
52
+ timeout,
53
+ title
54
+ })
55
+ );
56
+ if (!active) {
57
+ instance.destroy();
58
+ return;
59
+ }
60
+ checkout = instance;
61
+ checkoutRef.current = instance;
62
+ unsubscribe = instance.onEvent((event) => {
63
+ callbacksRef.current.onEvent?.(event);
64
+ if (event.type === "ready") callbacksRef.current.onReady?.(event);
65
+ if (event.type === "completed")
66
+ callbacksRef.current.onCompleted?.(event);
67
+ if (event.type === "error") callbacksRef.current.onError?.(event);
68
+ });
69
+ await instance.mount(target);
70
+ }).catch((error) => {
71
+ if (active) callbacksRef.current.onError?.(asError(error));
72
+ });
73
+ return () => {
74
+ active = false;
75
+ unsubscribe?.();
76
+ checkout?.destroy();
77
+ if (checkoutRef.current === checkout) checkoutRef.current = null;
78
+ };
79
+ }, [orderId, timeout, title]);
80
+ useEffect(() => {
81
+ checkoutRef.current?.update(definedOptions({ appearance, locale }));
82
+ }, [appearance, locale]);
83
+ return /* @__PURE__ */ jsx("div", { className, ref: containerRef, style });
84
+ }
85
+ );
86
+ function definedOptions(options) {
87
+ return Object.fromEntries(
88
+ Object.entries(options).filter(([, value]) => value !== void 0)
89
+ );
90
+ }
91
+ function asError(error) {
92
+ return error instanceof Error ? error : new Error("Checkout failed to load.");
93
+ }
94
+ export {
95
+ Checkout
96
+ };
97
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/Checkout.tsx"],"sourcesContent":["import {\n loadInttegro,\n type CheckoutController,\n type CheckoutErrorEvent,\n type CheckoutEvent,\n type CheckoutOptions,\n} from '@inttegro/js'\nimport {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n type CSSProperties,\n} from 'react'\n\n/**\n * Props for {@link Checkout}.\n *\n * `orderId`, `timeout`, and `title` identify the embedded experience. Changing\n * one replaces the underlying controller. `appearance` and `locale` update the\n * existing controller without discarding payer progress.\n *\n * @category React\n */\nexport interface CheckoutProps extends CheckoutOptions {\n /** CSS class applied to the outer container owned by your React application. */\n className?: string\n /** Inline styles applied to the outer container, never to controls inside Checkout. */\n style?: CSSProperties\n /**\n * Called after Checkout reports its successful terminal state.\n * Use this to navigate or refresh server-owned Order state, not as the sole\n * signal for fulfillment.\n */\n onCompleted?: (event: Extract<CheckoutEvent, { type: 'completed' }>) => void\n /**\n * Called for loader/mount errors and sanitized hosted errors.\n * Use `error instanceof InttegroCheckoutError` or check for an event `type`\n * before reading framework-independent details.\n */\n onError?: (event: CheckoutErrorEvent | Error) => void\n /** Called for every sanitized lifecycle event, including events above. */\n onEvent?: (event: CheckoutEvent) => void\n /** Called once the iframe is interactive and its mount promise has resolved. */\n onReady?: (event: Extract<CheckoutEvent, { type: 'ready' }>) => void\n}\n\n/**\n * Imperative operations exposed by {@link Checkout} through a React ref.\n *\n * Prefer props for normal updates. Use the handle for focus management in\n * dialogs or for updates driven by imperative host integrations.\n *\n * @category React\n */\nexport interface CheckoutHandle {\n /** Moves focus into Checkout when its controller is ready; otherwise does nothing. */\n focus(): void\n /** Applies a new locale or theme when the controller exists. */\n update(options: Parameters<CheckoutController['update']>[0]): void\n}\n\n/**\n * Embeds Inttegro-hosted Checkout in a React application.\n *\n * The component renders an empty `div`, loads the executable runtime from\n * Inttegro's fixed origin after mount, creates a controller for `orderId`, and\n * destroys it during cleanup. Server rendering is safe because no runtime is\n * loaded until the effect runs in a browser.\n *\n * Changing `orderId`, `timeout`, or `title` starts a fresh hosted experience.\n * Changing `appearance` or `locale` updates the active experience in place.\n * Keep the component mounted while a payment is pending or confirmation is in\n * progress.\n *\n * @example Handle success, failure, and focus in a dialog\n * ```tsx\n * import { useRef } from 'react'\n * import { Checkout, type CheckoutHandle } from '@inttegro/react'\n *\n * function Payment({ orderId }: { orderId: string }) {\n * const checkout = useRef<CheckoutHandle>(null)\n *\n * return (\n * <dialog open onTransitionEnd={() => checkout.current?.focus()}>\n * <Checkout\n * ref={checkout}\n * orderId={orderId}\n * appearance={{ theme: 'system' }}\n * onCompleted={() => location.assign(`/orders/${orderId}/complete`)}\n * onError={(error) => reportCheckoutError(error)}\n * />\n * </dialog>\n * )\n * }\n * ```\n *\n * @category React\n */\nexport const Checkout = forwardRef<CheckoutHandle, CheckoutProps>(\n function Checkout(\n {\n appearance,\n className,\n locale,\n onCompleted,\n onError,\n onEvent,\n onReady,\n orderId,\n style,\n timeout,\n title,\n },\n forwardedRef,\n ) {\n const containerRef = useRef<HTMLDivElement>(null)\n const checkoutRef = useRef<CheckoutController | null>(null)\n const callbacksRef = useRef({ onCompleted, onError, onEvent, onReady })\n const updateOptionsRef = useRef({ appearance, locale })\n callbacksRef.current = { onCompleted, onError, onEvent, onReady }\n updateOptionsRef.current = { appearance, locale }\n\n useImperativeHandle(\n forwardedRef,\n () => ({\n focus: () => checkoutRef.current?.focus(),\n update: (options) => checkoutRef.current?.update(options),\n }),\n [],\n )\n\n useEffect(() => {\n const target = containerRef.current\n if (!target) return\n\n let active = true\n let checkout: CheckoutController | undefined\n let unsubscribe: (() => void) | undefined\n\n void loadInttegro()\n .then(async (inttegro) => {\n if (!active || !inttegro) return\n const instance = inttegro.createCheckout(\n definedOptions({\n ...updateOptionsRef.current,\n orderId,\n timeout,\n title,\n }),\n )\n if (!active) {\n instance.destroy()\n return\n }\n checkout = instance\n checkoutRef.current = instance\n unsubscribe = instance.onEvent((event) => {\n callbacksRef.current.onEvent?.(event)\n if (event.type === 'ready') callbacksRef.current.onReady?.(event)\n if (event.type === 'completed')\n callbacksRef.current.onCompleted?.(event)\n if (event.type === 'error') callbacksRef.current.onError?.(event)\n })\n await instance.mount(target)\n })\n .catch((error: unknown) => {\n if (active) callbacksRef.current.onError?.(asError(error))\n })\n\n return () => {\n active = false\n unsubscribe?.()\n checkout?.destroy()\n if (checkoutRef.current === checkout) checkoutRef.current = null\n }\n }, [orderId, timeout, title])\n\n useEffect(() => {\n checkoutRef.current?.update(definedOptions({ appearance, locale }))\n }, [appearance, locale])\n\n return <div className={className} ref={containerRef} style={style} />\n },\n)\n\nfunction definedOptions<Value extends object>(options: Value): Value {\n return Object.fromEntries(\n Object.entries(options).filter(([, value]) => value !== undefined),\n ) as Value\n}\n\nfunction asError(error: unknown): Error {\n return error instanceof Error ? error : new Error('Checkout failed to load.')\n}\n"],"mappings":";AAAA;AAAA,EACE;AAAA,OAKK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAyKI;AAnFJ,IAAM,WAAW;AAAA,EACtB,SAASA,UACP;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GACA,cACA;AACA,UAAM,eAAe,OAAuB,IAAI;AAChD,UAAM,cAAc,OAAkC,IAAI;AAC1D,UAAM,eAAe,OAAO,EAAE,aAAa,SAAS,SAAS,QAAQ,CAAC;AACtE,UAAM,mBAAmB,OAAO,EAAE,YAAY,OAAO,CAAC;AACtD,iBAAa,UAAU,EAAE,aAAa,SAAS,SAAS,QAAQ;AAChE,qBAAiB,UAAU,EAAE,YAAY,OAAO;AAEhD;AAAA,MACE;AAAA,MACA,OAAO;AAAA,QACL,OAAO,MAAM,YAAY,SAAS,MAAM;AAAA,QACxC,QAAQ,CAAC,YAAY,YAAY,SAAS,OAAO,OAAO;AAAA,MAC1D;AAAA,MACA,CAAC;AAAA,IACH;AAEA,cAAU,MAAM;AACd,YAAM,SAAS,aAAa;AAC5B,UAAI,CAAC,OAAQ;AAEb,UAAI,SAAS;AACb,UAAI;AACJ,UAAI;AAEJ,WAAK,aAAa,EACf,KAAK,OAAO,aAAa;AACxB,YAAI,CAAC,UAAU,CAAC,SAAU;AAC1B,cAAM,WAAW,SAAS;AAAA,UACxB,eAAe;AAAA,YACb,GAAG,iBAAiB;AAAA,YACpB;AAAA,YACA;AAAA,YACA;AAAA,UACF,CAAC;AAAA,QACH;AACA,YAAI,CAAC,QAAQ;AACX,mBAAS,QAAQ;AACjB;AAAA,QACF;AACA,mBAAW;AACX,oBAAY,UAAU;AACtB,sBAAc,SAAS,QAAQ,CAAC,UAAU;AACxC,uBAAa,QAAQ,UAAU,KAAK;AACpC,cAAI,MAAM,SAAS,QAAS,cAAa,QAAQ,UAAU,KAAK;AAChE,cAAI,MAAM,SAAS;AACjB,yBAAa,QAAQ,cAAc,KAAK;AAC1C,cAAI,MAAM,SAAS,QAAS,cAAa,QAAQ,UAAU,KAAK;AAAA,QAClE,CAAC;AACD,cAAM,SAAS,MAAM,MAAM;AAAA,MAC7B,CAAC,EACA,MAAM,CAAC,UAAmB;AACzB,YAAI,OAAQ,cAAa,QAAQ,UAAU,QAAQ,KAAK,CAAC;AAAA,MAC3D,CAAC;AAEH,aAAO,MAAM;AACX,iBAAS;AACT,sBAAc;AACd,kBAAU,QAAQ;AAClB,YAAI,YAAY,YAAY,SAAU,aAAY,UAAU;AAAA,MAC9D;AAAA,IACF,GAAG,CAAC,SAAS,SAAS,KAAK,CAAC;AAE5B,cAAU,MAAM;AACd,kBAAY,SAAS,OAAO,eAAe,EAAE,YAAY,OAAO,CAAC,CAAC;AAAA,IACpE,GAAG,CAAC,YAAY,MAAM,CAAC;AAEvB,WAAO,oBAAC,SAAI,WAAsB,KAAK,cAAc,OAAc;AAAA,EACrE;AACF;AAEA,SAAS,eAAqC,SAAuB;AACnE,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,UAAU,MAAS;AAAA,EACnE;AACF;AAEA,SAAS,QAAQ,OAAuB;AACtC,SAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,0BAA0B;AAC9E;","names":["Checkout"]}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@inttegro/react",
3
+ "version": "0.1.0",
4
+ "description": "React components for Inttegro hosted workflows",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ },
20
+ "./package.json": "./package.json"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "README.md"
25
+ ],
26
+ "sideEffects": false,
27
+ "engines": {
28
+ "node": ">=18"
29
+ },
30
+ "scripts": {
31
+ "build": "tsup",
32
+ "typecheck": "tsc --noEmit"
33
+ },
34
+ "dependencies": {
35
+ "@inttegro/js": "0.1.0"
36
+ },
37
+ "peerDependencies": {
38
+ "react": ">=18"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/zebodotdev/js.git",
43
+ "directory": "packages/react"
44
+ },
45
+ "homepage": "https://studio.inttegro.com/web/react",
46
+ "author": "Inttegro Eng <engineering@inttegro.com> (https://inttegro.com)",
47
+ "license": "MIT",
48
+ "publishConfig": {
49
+ "access": "public",
50
+ "provenance": true
51
+ },
52
+ "typedocOptions": {
53
+ "entryPoints": [
54
+ "src/index.ts"
55
+ ]
56
+ },
57
+ "devDependencies": {
58
+ "@types/react": "19.2.18",
59
+ "react": "19.2.8",
60
+ "tsup": "8.5.1",
61
+ "typescript": "6.0.3"
62
+ }
63
+ }