Skip to main content

JavaScript runtime and loader

Follow this guide from top to bottom to add a working Checkout to a browser application. By the end, customers can pay through an embedded or modal experience, your interface responds to the outcome, and your backend verifies the final Order state.

1. Prepare the payment reference on your server

Use your server SDK or the Commerce API to create an Order and finalize it. Return only the finalized Order ID to the browser:

{
"orderId": "or_..."
}

Keep your secret API key and the authority to change amount, currency, customer, or line items on the server. Treat the Order ID as a payment capability: do not put it in analytics, diagnostic logs, or a public page before it is needed.

If the product uses a customer_selected_amount price, return the public Purchase Intent ID instead. Do not create an Order with a guessed amount:

{
"purchaseIntentId": "sale_..."
}

Checkout will load the authoritative currency and allowed range, collect the payer's amount, and create the finalized Order only after they continue.

2. Install the loader

npm install @inttegro/js

Read the npm setup guide

The package contains the loader, TypeScript types, and lifecycle contract. It downloads the executable payment runtime from Inttegro when your browser code calls loadInttegro(). Do not copy, bundle, proxy, or self-host that runtime. Version 0.5.4 loads the matching hosted 0.5.4 runtime. Both use the same Checkout contract, so pin the package version and let the loader select its runtime.

3. Create one Checkout controller

Initialize Inttegro in browser code with the Order ID returned by your backend. In the examples below, orderId is that string:

import { loadInttegro } from '@inttegro/js'

const inttegro = await loadInttegro()

if (!inttegro) {
throw new Error('Checkout requires a browser.')
}

const checkout = inttegro.createCheckout({ orderId })

loadInttegro() returns null during server rendering. In an SSR application, run this code from a browser entry point or client-side lifecycle hook.

For a Buy link, create the controller with the Purchase Intent instead. Supply exactly one Checkout reference:

const checkout = inttegro.createCheckout({ purchaseIntentId })

This controller works with both mount() and present(). The frame renders the same Buy experience as the hosted Buy link. It collects the customer details, quantity, variant, and customer-selected amount required by the offer, creates the finalized Order after the buyer continues, and then presents payment in the same frame. Suggested amounts are shortcuts, not an allow-list.

4. Decide what happens next

Subscribe before mounting Checkout so you do not miss an early event. This example sends successful customers to a status page and puts recoverable messages in the live region added in the next step:

const status = document.querySelector<HTMLElement>('#checkout-status')
const showMessage = (message: string) => {
if (status) status.textContent = message
}

checkout.on('completed', ({ order }) => {
sessionStorage.setItem('completedOrderId', order.id)
window.location.assign('/payment-status')
})

checkout.on('failed', ({ failure }) => {
showMessage(failure.message)
})

showMessage represents ordinary application UI; replace it with your existing alert, toast, or live-region helper. See the complete event contract when you need provider-confirmation or payment-attempt events.

Every event includes type, occurredAt, flowId, and sequence. Events about an Order carry the full client-safe order; they do not expose a standalone payment ID. Use flowId and sequence to correlate and order events from one presentation, not as business identifiers.

EventMeaningAdditional payload
canceledThe payer deliberately left Checkout.order when available
changePayer input changed the form's completion state.complete, payment method
completedCheckout reached its successful terminal experience.order
failedCheckout cannot continue this presentation.failure, optional order
orderPreparedA Purchase Intent produced the Order that Checkout will pay.order
paymentActionRequiredConfirmation, authorization, or a redirect is required.action, order
paymentAttemptFailedOne attempt failed, but Checkout remains available for retry.failure, order
paymentAttemptStartedCheckout started a payment attempt.order
paymentProcessingA submitted payment is still pending.order
presentedA new Checkout presentation began.—
readyHosted Checkout is interactive.—

paymentAttemptFailed is recoverable; let Checkout explain the failure and offer another attempt. failed, canceled, and completed are terminal for that presentation. These lifecycle events are distinct from JavaScript loader or mount exceptions, which are reported by the rejected operation.

5. Choose and mount Checkout

Choose one presentation for the first integration. Both use the controller and event handlers you just created; only the surrounding surface and when you call mount() change.

The screenshots show the same Checkout embedded in a page and presented inside a modal. Switch between desktop and mobile, and open any image at full size when you need to inspect the layout.

Select any image to view it full screen.

Use Embedded when payment belongs inside the page—for example, beside an invoice or within a checkout step. Add one empty container and the live region used by showMessage():

<div id="inttegro-checkout"></div>
<p id="checkout-status" aria-live="polite"></p>

Mount the controller into that container:

await checkout.mount('#inttegro-checkout')

Keep the container and controller alive while a payment or provider approval is in progress. Your application owns the container's position and width, while Checkout owns everything inside it. Let Checkout report its own height; a fixed height can clip payment controls or provider prompts.

mount() resolves when Checkout is ready for interaction. At this point, the browser side of the basic integration is working.

For hosted-page presentation, do not load the browser SDK. Submit to your backend and return a 303 redirect to the hosted Checkout URL for the finalized Order. The Express live demo demonstrates all three presentations, and its released controller shows the full browser boundary.

6. Verify payment on your server

The completed event is useful for navigation and interface feedback, but it is not fulfillment authority. Have /payment-status call your backend, then look up the Order with your server credentials. Ship goods, grant access, or mark an invoice paid only from the current server-side state.

7. Add configuration after the first payment works

Keep the first render small. Add optional behavior only when the product needs it:

const checkout = inttegro.createCheckout({
orderId,
appearance: { theme: 'system' },
locale: 'en-GH',
})

appearance.theme accepts light, dark, or system; locale accepts a BCP 47 locale such as en-GH. Only appearance and locale can be changed on an existing controller:

checkout.update({ appearance: { theme: 'dark' } })

Checkout preserves its standard behavior unless you set a feature explicitly:

FeatureDefaultUse it to
showLineItemsfalseLet customers expand finalized Order items before payment.
showInvoiceDownloadtrueOffer the invoice after successful payment when available.
showReceiptDownloadtrueOffer the receipt after successful payment when available.
allowPaymentMethodChangetrueLet the payer replace an attached payment method.

Create a new controller when orderId or purchaseIntentId, features, title, or readiness timeout changes.

8. Complete the production checklist

Before going live:

  • Add https://js.inttegro.com to script-src and https://pages.inttegro.com to frame-src in your Content Security Policy.
  • If your policy uses nonces, pass the response-specific value to loadInttegro({ nonce: requestNonce }).
  • Forward privacy-safe lifecycle data through checkout.onEvent(...); do not add Order IDs, payer details, credentials, or provider payloads.
  • Call checkout.unmount() when a controller may mount again, or checkout.destroy() when the page is permanently finished with it.
  • Test success, cancellation, provider approval, and recoverable failure on both desktop and mobile viewports.

Your integration is complete when Checkout opens in the chosen presentation, the page responds to its outcome, and the backend confirms the Order before fulfillment.

Continue from here