Skip to main content

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.

Developer preview

@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 install @inttegro/js

Read the npm setup guide

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

OptionDescription
appearanceSets the light, dark, or system theme.
localeSets a BCP 47 locale preference, such as en-GH.
orderIdIdentifies the finalized Order to collect payment for.
timeoutSets the number of milliseconds to wait for Checkout readiness.
titleSets 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.

EventAdditional fieldsMeaning
readyNoneCheckout is ready for customer interaction.
changecomplete, paymentMethodCustomer input changed.
paymentAttemptNoneThe customer started a payment attempt.
confirmationRequiredkindThe provider requires another customer action.
paymentAttemptFailedcode, recoverableA payment attempt failed.
completedNoneCheckout observed payment completion.
canceledNoneThe customer canceled the flow.
errorerror.code, error.message, error.recoverableCheckout 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.