JavaScript runtime and loader
Use @inttegro/js to load the Inttegro-hosted runtime and mount Checkout in a
website or browser application. The package provides the loader, public
TypeScript types, and lifecycle contract; it does not contain the payment
runtime.
@inttegro/js and the hosted runtime are not yet available for live payments.
The installation commands below will work after the preview package is
published.
Prerequisites
- A backend that can create an order and finalize it
- A client-safe Order ID returned to the browser
- A browser page with an empty element where Checkout can mount
Keep your secret API key and authoritative Order inputs on the server. Treat the Order ID as a payment capability and avoid exposing it through analytics or logs.
Install the loader
- npm
- Yarn
- Bun
- Deno
npm install @inttegro/js
yarn add @inttegro/js
bun add @inttegro/js
deno add npm:@inttegro/js
Deno installs this package through its npm compatibility layer. When importing without an import map, prefix the package with npm:.
Mount Checkout
Add a container and a live region for messages from your application:
<div id="checkout"></div>
<p id="checkout-status" aria-live="polite"></p>
Load Inttegro, create one Checkout instance for the finalized Order, and mount it into the container:
import { loadInttegro } from '@inttegro/js'
export async function startCheckout(orderId: string) {
const inttegro = await loadInttegro()
// Server-rendered code receives null. Browser code receives the runtime.
if (!inttegro) return
const checkout = inttegro.createCheckout({
appearance: { theme: 'system' },
locale: 'en-GH',
orderId,
title: 'Complete your payment',
})
const status = document.querySelector<HTMLElement>('#checkout-status')
checkout.on('completed', () => {
window.location.assign('/payment-status')
})
checkout.on('error', ({ error }) => {
if (status) status.textContent = error.message
})
await checkout.mount('#checkout')
return checkout
}
mount() accepts a CSS selector or an HTMLElement. It resolves when the
hosted experience is ready and rejects if the target is missing or Checkout
does not become ready before its timeout. loadInttegro() rejects separately
if the hosted runtime cannot load.
Understand runtime loading
loadInttegro() downloads the executable runtime from a fixed
Inttegro-controlled URL. The runtime creates an isolated hosted frame for
payment collection. Repeated calls in the same page share the same runtime
load.
This boundary keeps the payment experience consistent and lets Inttegro ship security fixes without requiring every merchant application to rebuild. Do not download, mirror, proxy, bundle, or self-host the runtime.
Configure Checkout
| Option | Description |
|---|---|
appearance | Sets the light, dark, or system theme. |
locale | Sets a BCP 47 locale preference, such as en-GH. |
orderId | Identifies the finalized Order to collect payment for. |
timeout | Sets the number of milliseconds to wait for Checkout readiness. |
title | Sets the accessible title of the hosted frame. |
Only appearance and locale can change on an existing instance:
checkout.update({
appearance: { theme: 'dark' },
locale: 'en-GH',
})
Create a new Checkout instance when the Order ID, title, or timeout changes.
Observe the payment lifecycle
Subscribe to one event with on(), or observe every lifecycle event with
onEvent(). Both methods return an unsubscribe function.
| Event | Additional fields | Meaning |
|---|---|---|
ready | None | Checkout is ready for customer interaction. |
change | complete, paymentMethod | Customer input changed. |
paymentAttempt | None | The customer started a payment attempt. |
confirmationRequired | kind | The provider requires another customer action. |
paymentAttemptFailed | code, recoverable | A payment attempt failed. |
completed | None | Checkout observed payment completion. |
canceled | None | The customer canceled the flow. |
error | error.code, error.message, error.recoverable | Checkout reported an SDK or hosted-flow error. |
Every event contains occurredAt. The contract deliberately omits the Order
ID, payer details, payment credentials, and raw provider responses. Forward
only the event fields and local context your telemetry system needs:
const stopObserving = checkout.onEvent((event) => {
telemetry.record('inttegro.checkout', {
occurredAt: event.occurredAt,
type: event.type,
})
})
// Call this when the page no longer owns the subscription.
stopObserving()
Here, telemetry.record represents the equivalent method in your existing
telemetry client; it is not exported by the Inttegro SDK.
Use completed to update the interface or open a status page. Before
fulfillment, have your backend look up the Order and
act on its current state.
Render Checkout inside a modal
The application owns the modal; Checkout mounts into a container within it. Open the modal before mounting so the hosted frame can measure the available space and receive focus:
<dialog id="payment-dialog" aria-labelledby="payment-dialog-title">
<header>
<h2 id="payment-dialog-title">Complete your payment</h2>
<form method="dialog">
<button aria-label="Close payment dialog">Close</button>
</form>
</header>
<div class="payment-dialog-scroll">
<div id="modal-checkout"></div>
</div>
</dialog>
#payment-dialog {
inline-size: min(40rem, calc(100% - 2rem));
max-block-size: 90dvh;
overflow: hidden;
}
.payment-dialog-scroll {
max-block-size: calc(90dvh - 4rem);
overflow: auto;
overscroll-behavior: contain;
}
const dialog = document.querySelector<HTMLDialogElement>('#payment-dialog')!
const target = dialog.querySelector<HTMLElement>('#modal-checkout')!
const checkout = inttegro.createCheckout({ orderId })
const stopCompleted = checkout.on('completed', () => dialog.close())
dialog.addEventListener(
'close',
() => {
stopCompleted()
checkout.destroy()
},
{ once: true },
)
dialog.showModal()
await checkout.mount(target)
checkout.focus()
Keep scrolling inside the modal rather than allowing tall Checkout content to escape the viewport. The application remains responsible for its close policy, accessible label, and returning focus to the control that opened it.
Use server rendering safely
loadInttegro() returns null when window or document is unavailable. Call
it from browser code or a client-side lifecycle hook, not while producing a
server response.
The React, Vue, Svelte, and Angular adapters defer mounting to their client lifecycle. Follow the framework-specific guide when your application uses server rendering or hydration.
Configure Content Security Policy
Add the Inttegro origins to the corresponding directives in your existing policy:
script-src 'self' https://js.inttegro.com;
frame-src https://pages.inttegro.com;
If script-src requires a nonce, pass the request-specific value on the first
browser call to the loader:
const inttegro = await loadInttegro({ nonce: requestNonce })
Generate a new nonce for every response. A nonce does not make a copied runtime supported; continue loading Checkout from the fixed Inttegro origin.
Clean up
Call unmount() when the same instance may be mounted into another container.
Call destroy() when a route, page, or modal has finished with it. A destroyed
instance cannot be reused.
checkout.destroy()
The framework adapters perform this cleanup when their component unmounts or is destroyed.
Related resources
- Web Checkout overview - Prepare the server and choose an integration.
- React, Vue, Svelte, and Angular - Use a framework-native component.
- Lookup an order - Verify payment state on your backend before fulfillment.
- Webhooks - Plan status tracking while merchant-facing webhooks are unavailable.