@inttegro/react 0.2.0 → 0.5.1

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
@@ -16,3 +16,11 @@ export function Payment({ orderId }: { orderId: string }) {
16
16
  )
17
17
  }
18
18
  ```
19
+
20
+ Pass `purchaseIntentId` instead of `orderId` when the Buy link uses a
21
+ `customer_selected_amount` price. The same component renders the amount picker
22
+ before continuing to Checkout:
23
+
24
+ ```tsx
25
+ <Checkout purchaseIntentId={purchaseIntentId} presentation="modal" />
26
+ ```
package/dist/index.cjs CHANGED
@@ -34,29 +34,48 @@ var Checkout = (0, import_react.forwardRef)(
34
34
  className,
35
35
  features,
36
36
  locale,
37
+ onCanceled,
37
38
  onCompleted,
39
+ onFailed,
38
40
  onError,
39
41
  onEvent,
40
42
  onReady,
41
43
  orderId,
44
+ purchaseIntentId,
45
+ presentation = "embedded",
42
46
  style,
43
47
  timeout,
44
48
  title
45
49
  }, forwardedRef) {
46
50
  const containerRef = (0, import_react.useRef)(null);
47
51
  const checkoutRef = (0, import_react.useRef)(null);
48
- const callbacksRef = (0, import_react.useRef)({ onCompleted, onError, onEvent, onReady });
52
+ const callbacksRef = (0, import_react.useRef)({
53
+ onCanceled,
54
+ onCompleted,
55
+ onFailed,
56
+ onError,
57
+ onEvent,
58
+ onReady
59
+ });
49
60
  const updateOptionsRef = (0, import_react.useRef)({ appearance, locale });
50
61
  const hasFeatureOverrides = features !== void 0;
51
62
  const showLineItems = features?.showLineItems;
52
63
  const showInvoiceDownload = features?.showInvoiceDownload;
53
64
  const showReceiptDownload = features?.showReceiptDownload;
54
65
  const allowPaymentMethodChange = features?.allowPaymentMethodChange;
55
- callbacksRef.current = { onCompleted, onError, onEvent, onReady };
66
+ callbacksRef.current = {
67
+ onCanceled,
68
+ onCompleted,
69
+ onFailed,
70
+ onError,
71
+ onEvent,
72
+ onReady
73
+ };
56
74
  updateOptionsRef.current = { appearance, locale };
57
75
  (0, import_react.useImperativeHandle)(
58
76
  forwardedRef,
59
77
  () => ({
78
+ dismiss: () => checkoutRef.current?.dismiss(),
60
79
  focus: () => checkoutRef.current?.focus(),
61
80
  update: (options) => checkoutRef.current?.update(options)
62
81
  }),
@@ -64,8 +83,10 @@ var Checkout = (0, import_react.forwardRef)(
64
83
  );
65
84
  (0, import_react.useEffect)(() => {
66
85
  const target = containerRef.current;
67
- if (!target) return;
86
+ if (presentation === "embedded" && !target) return;
68
87
  let active = true;
88
+ let canceled = false;
89
+ let failed = false;
69
90
  let checkout;
70
91
  let unsubscribe;
71
92
  void (0, import_js.loadInttegro)().then(async (inttegro) => {
@@ -80,6 +101,7 @@ var Checkout = (0, import_react.forwardRef)(
80
101
  showReceiptDownload
81
102
  }) : void 0,
82
103
  orderId,
104
+ purchaseIntentId,
83
105
  timeout,
84
106
  title
85
107
  })
@@ -92,14 +114,23 @@ var Checkout = (0, import_react.forwardRef)(
92
114
  checkoutRef.current = instance;
93
115
  unsubscribe = instance.onEvent((event) => {
94
116
  callbacksRef.current.onEvent?.(event);
117
+ if (event.type === "canceled") {
118
+ canceled = true;
119
+ callbacksRef.current.onCanceled?.(event);
120
+ }
95
121
  if (event.type === "ready") callbacksRef.current.onReady?.(event);
96
122
  if (event.type === "completed")
97
123
  callbacksRef.current.onCompleted?.(event);
98
- if (event.type === "error") callbacksRef.current.onError?.(event);
124
+ if (event.type === "failed") {
125
+ failed = true;
126
+ callbacksRef.current.onFailed?.(event);
127
+ }
99
128
  });
100
- await instance.mount(target);
129
+ if (presentation === "modal") await instance.present();
130
+ else await instance.mount(target);
101
131
  }).catch((error) => {
102
- if (active) callbacksRef.current.onError?.(asError(error));
132
+ if (active && !canceled && !failed)
133
+ callbacksRef.current.onError?.(asError(error));
103
134
  });
104
135
  return () => {
105
136
  active = false;
@@ -111,6 +142,8 @@ var Checkout = (0, import_react.forwardRef)(
111
142
  allowPaymentMethodChange,
112
143
  hasFeatureOverrides,
113
144
  orderId,
145
+ purchaseIntentId,
146
+ presentation,
114
147
  showInvoiceDownload,
115
148
  showLineItems,
116
149
  showReceiptDownload,
@@ -1 +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 CheckoutFeatures,\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 * `features`, `orderId`, `timeout`, and `title` identify the embedded\n * experience. Changing one replaces the underlying controller. `appearance`\n * and `locale` update the existing controller without discarding payer\n * 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 `features`, `orderId`, `timeout`, or `title` starts a fresh hosted\n * experience. Changing `appearance` or `locale` updates the active experience\n * in place. Keep the component mounted while a payment is pending or\n * confirmation is in 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 features,\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 const hasFeatureOverrides = features !== undefined\n const showLineItems = features?.showLineItems\n const showInvoiceDownload = features?.showInvoiceDownload\n const showReceiptDownload = features?.showReceiptDownload\n const allowPaymentMethodChange = features?.allowPaymentMethodChange\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 features: hasFeatureOverrides\n ? definedOptions({\n allowPaymentMethodChange,\n showInvoiceDownload,\n showLineItems,\n showReceiptDownload,\n })\n : undefined,\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 }, [\n allowPaymentMethodChange,\n hasFeatureOverrides,\n orderId,\n showInvoiceDownload,\n showLineItems,\n showReceiptDownload,\n timeout,\n title,\n ])\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":"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","names":["Checkout"]}
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 CheckoutEvent,\n CheckoutFailedEvent,\n CheckoutOrder,\n CheckoutFeatures,\n CheckoutPresentation,\n CheckoutTheme,\n} from '@inttegro/js'\n","import {\n loadInttegro,\n type CheckoutController,\n type CheckoutEvent,\n type CheckoutOptions,\n type CheckoutPresentation,\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 * `features`, the Checkout reference, `presentation`, `timeout`, and `title`\n * identify the experience. Changing one replaces the underlying controller.\n * `appearance` and `locale` update the existing controller without discarding\n * payer progress.\n *\n * @category React\n */\nexport type CheckoutProps = 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 /** Displays Checkout inline by default or in an Inttegro-managed modal. */\n presentation?: CheckoutPresentation\n /** Called when a payer dismisses managed modal Checkout before completion. */\n onCanceled?: (event: Extract<CheckoutEvent, { type: 'canceled' }>) => void\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 /** Called when Checkout reaches a terminal failure state. */\n onFailed?: (event: Extract<CheckoutEvent, { type: 'failed' }>) => void\n /** Called for loader or mount errors. */\n onError?: (error: 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 /** Closes managed modal Checkout when it is open. */\n dismiss(): void\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 * Displays 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 an `orderId` or\n * `purchaseIntentId`, and destroys it during cleanup. Server rendering is safe\n * because no runtime is loaded until the effect runs in a browser.\n *\n * Changing `features`, the Checkout reference, `presentation`, `timeout`, or\n * `title` starts a fresh hosted experience. Changing `appearance` or `locale`\n * updates the active experience in place. Keep the component mounted while a\n * payment is pending or confirmation is in progress.\n *\n * @example Present Checkout without building a dialog\n * ```tsx\n * import { Checkout } from '@inttegro/react'\n *\n * function Payment({ orderId }: { orderId: string }) {\n * return (\n * <Checkout\n * orderId={orderId}\n * presentation=\"modal\"\n * appearance={{ theme: 'system' }}\n * onCompleted={() => location.assign(`/orders/${orderId}/complete`)}\n * onError={(error) => reportCheckoutError(error)}\n * />\n * )\n * }\n * ```\n *\n * @category React\n */\nexport const Checkout = forwardRef<CheckoutHandle, CheckoutProps>(\n function Checkout(\n {\n appearance,\n className,\n features,\n locale,\n onCanceled,\n onCompleted,\n onFailed,\n onError,\n onEvent,\n onReady,\n orderId,\n purchaseIntentId,\n presentation = 'embedded',\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({\n onCanceled,\n onCompleted,\n onFailed,\n onError,\n onEvent,\n onReady,\n })\n const updateOptionsRef = useRef({ appearance, locale })\n const hasFeatureOverrides = features !== undefined\n const showLineItems = features?.showLineItems\n const showInvoiceDownload = features?.showInvoiceDownload\n const showReceiptDownload = features?.showReceiptDownload\n const allowPaymentMethodChange = features?.allowPaymentMethodChange\n callbacksRef.current = {\n onCanceled,\n onCompleted,\n onFailed,\n onError,\n onEvent,\n onReady,\n }\n updateOptionsRef.current = { appearance, locale }\n\n useImperativeHandle(\n forwardedRef,\n () => ({\n dismiss: () => checkoutRef.current?.dismiss(),\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 (presentation === 'embedded' && !target) return\n\n let active = true\n let canceled = false\n let failed = false\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 features: hasFeatureOverrides\n ? definedOptions({\n allowPaymentMethodChange,\n showInvoiceDownload,\n showLineItems,\n showReceiptDownload,\n })\n : undefined,\n orderId,\n purchaseIntentId,\n timeout,\n title,\n }) as CheckoutOptions,\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 === 'canceled') {\n canceled = true\n callbacksRef.current.onCanceled?.(event)\n }\n if (event.type === 'ready') callbacksRef.current.onReady?.(event)\n if (event.type === 'completed')\n callbacksRef.current.onCompleted?.(event)\n if (event.type === 'failed') {\n failed = true\n callbacksRef.current.onFailed?.(event)\n }\n })\n if (presentation === 'modal') await instance.present()\n else await instance.mount(target!)\n })\n .catch((error: unknown) => {\n if (active && !canceled && !failed)\n 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 }, [\n allowPaymentMethodChange,\n hasFeatureOverrides,\n orderId,\n purchaseIntentId,\n presentation,\n showInvoiceDownload,\n showLineItems,\n showReceiptDownload,\n timeout,\n title,\n ])\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":"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","names":["Checkout"]}
package/dist/index.d.cts CHANGED
@@ -1,23 +1,29 @@
1
1
  import * as react from 'react';
2
2
  import { CSSProperties } from 'react';
3
- import { CheckoutOptions, CheckoutEvent, CheckoutErrorEvent, CheckoutController } from '@inttegro/js';
4
- export { CheckoutAppearance, CheckoutErrorEvent, CheckoutEvent, CheckoutFeatures, CheckoutTheme } from '@inttegro/js';
3
+ import { CheckoutOptions, CheckoutPresentation, CheckoutEvent, CheckoutController } from '@inttegro/js';
4
+ export { CheckoutAppearance, CheckoutEvent, CheckoutFailedEvent, CheckoutFeatures, CheckoutOrder, CheckoutPresentation, CheckoutTheme } from '@inttegro/js';
5
5
 
6
6
  /**
7
7
  * Props for {@link Checkout}.
8
8
  *
9
- * `features`, `orderId`, `timeout`, and `title` identify the embedded
10
- * experience. Changing one replaces the underlying controller. `appearance`
11
- * and `locale` update the existing controller without discarding payer
12
- * progress.
9
+ * `features`, the Checkout reference, `presentation`, `timeout`, and `title`
10
+ * identify the experience. Changing one replaces the underlying controller.
11
+ * `appearance` and `locale` update the existing controller without discarding
12
+ * payer progress.
13
13
  *
14
14
  * @category React
15
15
  */
16
- interface CheckoutProps extends CheckoutOptions {
16
+ type CheckoutProps = CheckoutOptions & {
17
17
  /** CSS class applied to the outer container owned by your React application. */
18
18
  className?: string;
19
19
  /** Inline styles applied to the outer container, never to controls inside Checkout. */
20
20
  style?: CSSProperties;
21
+ /** Displays Checkout inline by default or in an Inttegro-managed modal. */
22
+ presentation?: CheckoutPresentation;
23
+ /** Called when a payer dismisses managed modal Checkout before completion. */
24
+ onCanceled?: (event: Extract<CheckoutEvent, {
25
+ type: 'canceled';
26
+ }>) => void;
21
27
  /**
22
28
  * Called after Checkout reports its successful terminal state.
23
29
  * Use this to navigate or refresh server-owned Order state, not as the sole
@@ -26,19 +32,19 @@ interface CheckoutProps extends CheckoutOptions {
26
32
  onCompleted?: (event: Extract<CheckoutEvent, {
27
33
  type: 'completed';
28
34
  }>) => void;
29
- /**
30
- * Called for loader/mount errors and sanitized hosted errors.
31
- * Use `error instanceof InttegroCheckoutError` or check for an event `type`
32
- * before reading framework-independent details.
33
- */
34
- onError?: (event: CheckoutErrorEvent | Error) => void;
35
+ /** Called when Checkout reaches a terminal failure state. */
36
+ onFailed?: (event: Extract<CheckoutEvent, {
37
+ type: 'failed';
38
+ }>) => void;
39
+ /** Called for loader or mount errors. */
40
+ onError?: (error: Error) => void;
35
41
  /** Called for every sanitized lifecycle event, including events above. */
36
42
  onEvent?: (event: CheckoutEvent) => void;
37
43
  /** Called once the iframe is interactive and its mount promise has resolved. */
38
44
  onReady?: (event: Extract<CheckoutEvent, {
39
45
  type: 'ready';
40
46
  }>) => void;
41
- }
47
+ };
42
48
  /**
43
49
  * Imperative operations exposed by {@link Checkout} through a React ref.
44
50
  *
@@ -48,42 +54,39 @@ interface CheckoutProps extends CheckoutOptions {
48
54
  * @category React
49
55
  */
50
56
  interface CheckoutHandle {
57
+ /** Closes managed modal Checkout when it is open. */
58
+ dismiss(): void;
51
59
  /** Moves focus into Checkout when its controller is ready; otherwise does nothing. */
52
60
  focus(): void;
53
61
  /** Applies a new locale or theme when the controller exists. */
54
62
  update(options: Parameters<CheckoutController['update']>[0]): void;
55
63
  }
56
64
  /**
57
- * Embeds Inttegro-hosted Checkout in a React application.
65
+ * Displays Inttegro-hosted Checkout in a React application.
58
66
  *
59
67
  * The component renders an empty `div`, loads the executable runtime from
60
- * Inttegro's fixed origin after mount, creates a controller for `orderId`, and
61
- * destroys it during cleanup. Server rendering is safe because no runtime is
62
- * loaded until the effect runs in a browser.
68
+ * Inttegro's fixed origin after mount, creates a controller for an `orderId` or
69
+ * `purchaseIntentId`, and destroys it during cleanup. Server rendering is safe
70
+ * because no runtime is loaded until the effect runs in a browser.
63
71
  *
64
- * Changing `features`, `orderId`, `timeout`, or `title` starts a fresh hosted
65
- * experience. Changing `appearance` or `locale` updates the active experience
66
- * in place. Keep the component mounted while a payment is pending or
67
- * confirmation is in progress.
72
+ * Changing `features`, the Checkout reference, `presentation`, `timeout`, or
73
+ * `title` starts a fresh hosted experience. Changing `appearance` or `locale`
74
+ * updates the active experience in place. Keep the component mounted while a
75
+ * payment is pending or confirmation is in progress.
68
76
  *
69
- * @example Handle success, failure, and focus in a dialog
77
+ * @example Present Checkout without building a dialog
70
78
  * ```tsx
71
- * import { useRef } from 'react'
72
- * import { Checkout, type CheckoutHandle } from '@inttegro/react'
79
+ * import { Checkout } from '@inttegro/react'
73
80
  *
74
81
  * function Payment({ orderId }: { orderId: string }) {
75
- * const checkout = useRef<CheckoutHandle>(null)
76
- *
77
82
  * return (
78
- * <dialog open onTransitionEnd={() => checkout.current?.focus()}>
79
- * <Checkout
80
- * ref={checkout}
81
- * orderId={orderId}
82
- * appearance={{ theme: 'system' }}
83
- * onCompleted={() => location.assign(`/orders/${orderId}/complete`)}
84
- * onError={(error) => reportCheckoutError(error)}
85
- * />
86
- * </dialog>
83
+ * <Checkout
84
+ * orderId={orderId}
85
+ * presentation="modal"
86
+ * appearance={{ theme: 'system' }}
87
+ * onCompleted={() => location.assign(`/orders/${orderId}/complete`)}
88
+ * onError={(error) => reportCheckoutError(error)}
89
+ * />
87
90
  * )
88
91
  * }
89
92
  * ```
package/dist/index.d.ts CHANGED
@@ -1,23 +1,29 @@
1
1
  import * as react from 'react';
2
2
  import { CSSProperties } from 'react';
3
- import { CheckoutOptions, CheckoutEvent, CheckoutErrorEvent, CheckoutController } from '@inttegro/js';
4
- export { CheckoutAppearance, CheckoutErrorEvent, CheckoutEvent, CheckoutFeatures, CheckoutTheme } from '@inttegro/js';
3
+ import { CheckoutOptions, CheckoutPresentation, CheckoutEvent, CheckoutController } from '@inttegro/js';
4
+ export { CheckoutAppearance, CheckoutEvent, CheckoutFailedEvent, CheckoutFeatures, CheckoutOrder, CheckoutPresentation, CheckoutTheme } from '@inttegro/js';
5
5
 
6
6
  /**
7
7
  * Props for {@link Checkout}.
8
8
  *
9
- * `features`, `orderId`, `timeout`, and `title` identify the embedded
10
- * experience. Changing one replaces the underlying controller. `appearance`
11
- * and `locale` update the existing controller without discarding payer
12
- * progress.
9
+ * `features`, the Checkout reference, `presentation`, `timeout`, and `title`
10
+ * identify the experience. Changing one replaces the underlying controller.
11
+ * `appearance` and `locale` update the existing controller without discarding
12
+ * payer progress.
13
13
  *
14
14
  * @category React
15
15
  */
16
- interface CheckoutProps extends CheckoutOptions {
16
+ type CheckoutProps = CheckoutOptions & {
17
17
  /** CSS class applied to the outer container owned by your React application. */
18
18
  className?: string;
19
19
  /** Inline styles applied to the outer container, never to controls inside Checkout. */
20
20
  style?: CSSProperties;
21
+ /** Displays Checkout inline by default or in an Inttegro-managed modal. */
22
+ presentation?: CheckoutPresentation;
23
+ /** Called when a payer dismisses managed modal Checkout before completion. */
24
+ onCanceled?: (event: Extract<CheckoutEvent, {
25
+ type: 'canceled';
26
+ }>) => void;
21
27
  /**
22
28
  * Called after Checkout reports its successful terminal state.
23
29
  * Use this to navigate or refresh server-owned Order state, not as the sole
@@ -26,19 +32,19 @@ interface CheckoutProps extends CheckoutOptions {
26
32
  onCompleted?: (event: Extract<CheckoutEvent, {
27
33
  type: 'completed';
28
34
  }>) => void;
29
- /**
30
- * Called for loader/mount errors and sanitized hosted errors.
31
- * Use `error instanceof InttegroCheckoutError` or check for an event `type`
32
- * before reading framework-independent details.
33
- */
34
- onError?: (event: CheckoutErrorEvent | Error) => void;
35
+ /** Called when Checkout reaches a terminal failure state. */
36
+ onFailed?: (event: Extract<CheckoutEvent, {
37
+ type: 'failed';
38
+ }>) => void;
39
+ /** Called for loader or mount errors. */
40
+ onError?: (error: Error) => void;
35
41
  /** Called for every sanitized lifecycle event, including events above. */
36
42
  onEvent?: (event: CheckoutEvent) => void;
37
43
  /** Called once the iframe is interactive and its mount promise has resolved. */
38
44
  onReady?: (event: Extract<CheckoutEvent, {
39
45
  type: 'ready';
40
46
  }>) => void;
41
- }
47
+ };
42
48
  /**
43
49
  * Imperative operations exposed by {@link Checkout} through a React ref.
44
50
  *
@@ -48,42 +54,39 @@ interface CheckoutProps extends CheckoutOptions {
48
54
  * @category React
49
55
  */
50
56
  interface CheckoutHandle {
57
+ /** Closes managed modal Checkout when it is open. */
58
+ dismiss(): void;
51
59
  /** Moves focus into Checkout when its controller is ready; otherwise does nothing. */
52
60
  focus(): void;
53
61
  /** Applies a new locale or theme when the controller exists. */
54
62
  update(options: Parameters<CheckoutController['update']>[0]): void;
55
63
  }
56
64
  /**
57
- * Embeds Inttegro-hosted Checkout in a React application.
65
+ * Displays Inttegro-hosted Checkout in a React application.
58
66
  *
59
67
  * The component renders an empty `div`, loads the executable runtime from
60
- * Inttegro's fixed origin after mount, creates a controller for `orderId`, and
61
- * destroys it during cleanup. Server rendering is safe because no runtime is
62
- * loaded until the effect runs in a browser.
68
+ * Inttegro's fixed origin after mount, creates a controller for an `orderId` or
69
+ * `purchaseIntentId`, and destroys it during cleanup. Server rendering is safe
70
+ * because no runtime is loaded until the effect runs in a browser.
63
71
  *
64
- * Changing `features`, `orderId`, `timeout`, or `title` starts a fresh hosted
65
- * experience. Changing `appearance` or `locale` updates the active experience
66
- * in place. Keep the component mounted while a payment is pending or
67
- * confirmation is in progress.
72
+ * Changing `features`, the Checkout reference, `presentation`, `timeout`, or
73
+ * `title` starts a fresh hosted experience. Changing `appearance` or `locale`
74
+ * updates the active experience in place. Keep the component mounted while a
75
+ * payment is pending or confirmation is in progress.
68
76
  *
69
- * @example Handle success, failure, and focus in a dialog
77
+ * @example Present Checkout without building a dialog
70
78
  * ```tsx
71
- * import { useRef } from 'react'
72
- * import { Checkout, type CheckoutHandle } from '@inttegro/react'
79
+ * import { Checkout } from '@inttegro/react'
73
80
  *
74
81
  * function Payment({ orderId }: { orderId: string }) {
75
- * const checkout = useRef<CheckoutHandle>(null)
76
- *
77
82
  * return (
78
- * <dialog open onTransitionEnd={() => checkout.current?.focus()}>
79
- * <Checkout
80
- * ref={checkout}
81
- * orderId={orderId}
82
- * appearance={{ theme: 'system' }}
83
- * onCompleted={() => location.assign(`/orders/${orderId}/complete`)}
84
- * onError={(error) => reportCheckoutError(error)}
85
- * />
86
- * </dialog>
83
+ * <Checkout
84
+ * orderId={orderId}
85
+ * presentation="modal"
86
+ * appearance={{ theme: 'system' }}
87
+ * onCompleted={() => location.assign(`/orders/${orderId}/complete`)}
88
+ * onError={(error) => reportCheckoutError(error)}
89
+ * />
87
90
  * )
88
91
  * }
89
92
  * ```
package/dist/index.js CHANGED
@@ -15,29 +15,48 @@ var Checkout = forwardRef(
15
15
  className,
16
16
  features,
17
17
  locale,
18
+ onCanceled,
18
19
  onCompleted,
20
+ onFailed,
19
21
  onError,
20
22
  onEvent,
21
23
  onReady,
22
24
  orderId,
25
+ purchaseIntentId,
26
+ presentation = "embedded",
23
27
  style,
24
28
  timeout,
25
29
  title
26
30
  }, forwardedRef) {
27
31
  const containerRef = useRef(null);
28
32
  const checkoutRef = useRef(null);
29
- const callbacksRef = useRef({ onCompleted, onError, onEvent, onReady });
33
+ const callbacksRef = useRef({
34
+ onCanceled,
35
+ onCompleted,
36
+ onFailed,
37
+ onError,
38
+ onEvent,
39
+ onReady
40
+ });
30
41
  const updateOptionsRef = useRef({ appearance, locale });
31
42
  const hasFeatureOverrides = features !== void 0;
32
43
  const showLineItems = features?.showLineItems;
33
44
  const showInvoiceDownload = features?.showInvoiceDownload;
34
45
  const showReceiptDownload = features?.showReceiptDownload;
35
46
  const allowPaymentMethodChange = features?.allowPaymentMethodChange;
36
- callbacksRef.current = { onCompleted, onError, onEvent, onReady };
47
+ callbacksRef.current = {
48
+ onCanceled,
49
+ onCompleted,
50
+ onFailed,
51
+ onError,
52
+ onEvent,
53
+ onReady
54
+ };
37
55
  updateOptionsRef.current = { appearance, locale };
38
56
  useImperativeHandle(
39
57
  forwardedRef,
40
58
  () => ({
59
+ dismiss: () => checkoutRef.current?.dismiss(),
41
60
  focus: () => checkoutRef.current?.focus(),
42
61
  update: (options) => checkoutRef.current?.update(options)
43
62
  }),
@@ -45,8 +64,10 @@ var Checkout = forwardRef(
45
64
  );
46
65
  useEffect(() => {
47
66
  const target = containerRef.current;
48
- if (!target) return;
67
+ if (presentation === "embedded" && !target) return;
49
68
  let active = true;
69
+ let canceled = false;
70
+ let failed = false;
50
71
  let checkout;
51
72
  let unsubscribe;
52
73
  void loadInttegro().then(async (inttegro) => {
@@ -61,6 +82,7 @@ var Checkout = forwardRef(
61
82
  showReceiptDownload
62
83
  }) : void 0,
63
84
  orderId,
85
+ purchaseIntentId,
64
86
  timeout,
65
87
  title
66
88
  })
@@ -73,14 +95,23 @@ var Checkout = forwardRef(
73
95
  checkoutRef.current = instance;
74
96
  unsubscribe = instance.onEvent((event) => {
75
97
  callbacksRef.current.onEvent?.(event);
98
+ if (event.type === "canceled") {
99
+ canceled = true;
100
+ callbacksRef.current.onCanceled?.(event);
101
+ }
76
102
  if (event.type === "ready") callbacksRef.current.onReady?.(event);
77
103
  if (event.type === "completed")
78
104
  callbacksRef.current.onCompleted?.(event);
79
- if (event.type === "error") callbacksRef.current.onError?.(event);
105
+ if (event.type === "failed") {
106
+ failed = true;
107
+ callbacksRef.current.onFailed?.(event);
108
+ }
80
109
  });
81
- await instance.mount(target);
110
+ if (presentation === "modal") await instance.present();
111
+ else await instance.mount(target);
82
112
  }).catch((error) => {
83
- if (active) callbacksRef.current.onError?.(asError(error));
113
+ if (active && !canceled && !failed)
114
+ callbacksRef.current.onError?.(asError(error));
84
115
  });
85
116
  return () => {
86
117
  active = false;
@@ -92,6 +123,8 @@ var Checkout = forwardRef(
92
123
  allowPaymentMethodChange,
93
124
  hasFeatureOverrides,
94
125
  orderId,
126
+ purchaseIntentId,
127
+ presentation,
95
128
  showInvoiceDownload,
96
129
  showLineItems,
97
130
  showReceiptDownload,
package/dist/index.js.map CHANGED
@@ -1 +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 * `features`, `orderId`, `timeout`, and `title` identify the embedded\n * experience. Changing one replaces the underlying controller. `appearance`\n * and `locale` update the existing controller without discarding payer\n * 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 `features`, `orderId`, `timeout`, or `title` starts a fresh hosted\n * experience. Changing `appearance` or `locale` updates the active experience\n * in place. Keep the component mounted while a payment is pending or\n * confirmation is in 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 features,\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 const hasFeatureOverrides = features !== undefined\n const showLineItems = features?.showLineItems\n const showInvoiceDownload = features?.showInvoiceDownload\n const showReceiptDownload = features?.showReceiptDownload\n const allowPaymentMethodChange = features?.allowPaymentMethodChange\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 features: hasFeatureOverrides\n ? definedOptions({\n allowPaymentMethodChange,\n showInvoiceDownload,\n showLineItems,\n showReceiptDownload,\n })\n : undefined,\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 }, [\n allowPaymentMethodChange,\n hasFeatureOverrides,\n orderId,\n showInvoiceDownload,\n showLineItems,\n showReceiptDownload,\n timeout,\n title,\n ])\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":"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","names":["Checkout"]}
1
+ {"version":3,"sources":["../src/Checkout.tsx"],"sourcesContent":["import {\n loadInttegro,\n type CheckoutController,\n type CheckoutEvent,\n type CheckoutOptions,\n type CheckoutPresentation,\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 * `features`, the Checkout reference, `presentation`, `timeout`, and `title`\n * identify the experience. Changing one replaces the underlying controller.\n * `appearance` and `locale` update the existing controller without discarding\n * payer progress.\n *\n * @category React\n */\nexport type CheckoutProps = 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 /** Displays Checkout inline by default or in an Inttegro-managed modal. */\n presentation?: CheckoutPresentation\n /** Called when a payer dismisses managed modal Checkout before completion. */\n onCanceled?: (event: Extract<CheckoutEvent, { type: 'canceled' }>) => void\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 /** Called when Checkout reaches a terminal failure state. */\n onFailed?: (event: Extract<CheckoutEvent, { type: 'failed' }>) => void\n /** Called for loader or mount errors. */\n onError?: (error: 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 /** Closes managed modal Checkout when it is open. */\n dismiss(): void\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 * Displays 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 an `orderId` or\n * `purchaseIntentId`, and destroys it during cleanup. Server rendering is safe\n * because no runtime is loaded until the effect runs in a browser.\n *\n * Changing `features`, the Checkout reference, `presentation`, `timeout`, or\n * `title` starts a fresh hosted experience. Changing `appearance` or `locale`\n * updates the active experience in place. Keep the component mounted while a\n * payment is pending or confirmation is in progress.\n *\n * @example Present Checkout without building a dialog\n * ```tsx\n * import { Checkout } from '@inttegro/react'\n *\n * function Payment({ orderId }: { orderId: string }) {\n * return (\n * <Checkout\n * orderId={orderId}\n * presentation=\"modal\"\n * appearance={{ theme: 'system' }}\n * onCompleted={() => location.assign(`/orders/${orderId}/complete`)}\n * onError={(error) => reportCheckoutError(error)}\n * />\n * )\n * }\n * ```\n *\n * @category React\n */\nexport const Checkout = forwardRef<CheckoutHandle, CheckoutProps>(\n function Checkout(\n {\n appearance,\n className,\n features,\n locale,\n onCanceled,\n onCompleted,\n onFailed,\n onError,\n onEvent,\n onReady,\n orderId,\n purchaseIntentId,\n presentation = 'embedded',\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({\n onCanceled,\n onCompleted,\n onFailed,\n onError,\n onEvent,\n onReady,\n })\n const updateOptionsRef = useRef({ appearance, locale })\n const hasFeatureOverrides = features !== undefined\n const showLineItems = features?.showLineItems\n const showInvoiceDownload = features?.showInvoiceDownload\n const showReceiptDownload = features?.showReceiptDownload\n const allowPaymentMethodChange = features?.allowPaymentMethodChange\n callbacksRef.current = {\n onCanceled,\n onCompleted,\n onFailed,\n onError,\n onEvent,\n onReady,\n }\n updateOptionsRef.current = { appearance, locale }\n\n useImperativeHandle(\n forwardedRef,\n () => ({\n dismiss: () => checkoutRef.current?.dismiss(),\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 (presentation === 'embedded' && !target) return\n\n let active = true\n let canceled = false\n let failed = false\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 features: hasFeatureOverrides\n ? definedOptions({\n allowPaymentMethodChange,\n showInvoiceDownload,\n showLineItems,\n showReceiptDownload,\n })\n : undefined,\n orderId,\n purchaseIntentId,\n timeout,\n title,\n }) as CheckoutOptions,\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 === 'canceled') {\n canceled = true\n callbacksRef.current.onCanceled?.(event)\n }\n if (event.type === 'ready') callbacksRef.current.onReady?.(event)\n if (event.type === 'completed')\n callbacksRef.current.onCompleted?.(event)\n if (event.type === 'failed') {\n failed = true\n callbacksRef.current.onFailed?.(event)\n }\n })\n if (presentation === 'modal') await instance.present()\n else await instance.mount(target!)\n })\n .catch((error: unknown) => {\n if (active && !canceled && !failed)\n 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 }, [\n allowPaymentMethodChange,\n hasFeatureOverrides,\n orderId,\n purchaseIntentId,\n presentation,\n showInvoiceDownload,\n showLineItems,\n showReceiptDownload,\n timeout,\n title,\n ])\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":"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","names":["Checkout"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inttegro/react",
3
- "version": "0.2.0",
3
+ "version": "0.5.1",
4
4
  "description": "React components for Inttegro hosted workflows",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -32,14 +32,14 @@
32
32
  "typecheck": "tsc --noEmit"
33
33
  },
34
34
  "dependencies": {
35
- "@inttegro/js": "0.2.0"
35
+ "@inttegro/js": "0.5.1"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "react": ">=18"
39
39
  },
40
40
  "repository": {
41
41
  "type": "git",
42
- "url": "git+https://github.com/zebodotdev/js.git",
42
+ "url": "git+https://github.com/inttegro/js.git",
43
43
  "directory": "packages/react"
44
44
  },
45
45
  "homepage": "https://studio.inttegro.com/web/react",