Skip to main content
View as Markdown
Version: v2

Loading the SDK from the ODUS CDN

You can load the Checkout SDK from js.odus.com when your page loads, instead of bundling it into your app. Your checkout then receives our fixes and improvements without you upgrading the package or redeploying.

You keep installing @odus/checkout. Its @odus/checkout/loader entry is a small loader that fetches the checkout code from the CDN and hands you the same OdusCheckout and OdusElements you use today. Your configuration, callbacks and payment flow stay exactly as they are.

What to change​

  1. Upgrade the package to 1.17.0 or later.
  2. Allow js.odus.com in your Content Security Policy, if your page sends one.
  3. Open the connection early with a preconnect link on your checkout page.
  4. Replace the import with loadOdus().
  5. Handle a failed load.
info

Upgrading the package alone does not move you to the CDN. The @odus/checkout and @odus/checkout/elements imports keep working, and keep running the version you installed, until you replace them with loadOdus().

Upgrade the package​

npm install @odus/checkout@latest

The loader is in @odus/checkout 1.17.0 and later.

Allow the SDK in your Content Security Policy​

Skip this step if your checkout page sends no Content-Security-Policy header or <meta> tag.

Otherwise, add these sources to the directives your policy already has. js.odus.com is the only new one if you already run the bundled SDK under a policy.

DirectiveSourcesWhy
script-srchttps://js.odus.com https://js.verygoodvault.com https://js3.verygoodvault.comThe checkout code and the secure card fields
frame-srchttps://js.verygoodvault.com https://js3.verygoodvault.comThe secure card fields
connect-srchttps://api.odus.com https://analytics.odus.com https://vgs-collect-keeper.apps.verygood.systemsPayment calls and the SDK's diagnostics
style-srchttps://js.odus.comThe Drop-in stylesheet in Safari earlier than 16.4
img-srcdata:Card brand and payment method logos

If your checkout uses a custom font, also add https://fonts.googleapis.com to style-src and connect-src, and https://fonts.gstatic.com to font-src.

When you test with environment: 'test', the SDK calls https://sandbox-api.odus.com and https://sandbox-analytics.odus.com instead of api.odus.com and analytics.odus.com. The checkout code itself always comes from js.odus.com, in test and live alike.

A complete policy for a live checkout page, with your own sources shown as 'self':

Content-Security-Policy: default-src 'self'; script-src 'self' https://js.odus.com https://js.verygoodvault.com https://js3.verygoodvault.com; frame-src https://js.verygoodvault.com https://js3.verygoodvault.com; connect-src 'self' https://api.odus.com https://analytics.odus.com https://vgs-collect-keeper.apps.verygood.systems; style-src 'self' https://js.odus.com; img-src 'self' data:

The SDK needs neither 'unsafe-inline' nor 'unsafe-eval'. The one exception is Elements in Safari earlier than 16.4, which renders unstyled unless style-src allows 'unsafe-inline'.

If js.odus.com is missing from script-src, or from style-src for the Drop-in in Safari earlier than 16.4, loadOdus() rejects with the blocked_by_csp error code, so a test run finds it.

Open the connection early​

Add a preconnect link to the <head> of your checkout page, in the HTML your server sends:

<link rel="preconnect" href="https://js.odus.com" crossorigin />

The browser then opens the connection to the CDN while your page is still loading, so the checkout appears sooner. Keep the crossorigin attribute: without it, the browser opens a connection that the SDK cannot use.

You can also call loadOdus() before the customer reaches the checkout, for example on your cart page or when they press your checkout button. The browser keeps the downloaded code for a few minutes, so your checkout page does not wait for it again.

import { loadOdus } from '@odus/checkout/loader';

// On the cart page: start the download, and ignore a failure here.
// The checkout page calls loadOdus() again, which retries.
loadOdus('checkout').catch(() => {});

Replace the import​

Replace the SDK import with a call to loadOdus(), which resolves with the same constructor. Everything after that line stays the same.

Drop-in:

// Before
import { OdusCheckout } from '@odus/checkout';
import '@odus/checkout/styles';

// After
import { loadOdus } from '@odus/checkout/loader';

const { OdusCheckout } = await loadOdus('checkout');

Remove the @odus/checkout/styles import. The Drop-in loaded from the CDN brings its own styles.

Elements:

// Before
import { OdusElements } from '@odus/checkout/elements';

// After
import { loadOdus } from '@odus/checkout/loader';

const { OdusElements } = await loadOdus('elements');

loadOdus() is asynchronous, so call it where your code can wait, before you create the checkout. Calling it again on the same page returns the same load, so you can call it from more than one component.

In a React component:

src/components/CheckoutForm.tsx
import { useEffect, useRef } from 'react';
import type { OdusCheckout } from '@odus/checkout';
import { loadOdus } from '@odus/checkout/loader';

export function CheckoutForm() {
const checkoutRef = useRef<OdusCheckout | null>(null);

useEffect(() => {
let cancelled = false;

loadOdus('checkout', { environment: 'test' }).then(({ OdusCheckout }) => {
if (cancelled) return;

const checkout = new OdusCheckout({
environment: 'test', // Use 'live' for production
apiKey: 'pk_retired',
returnUrl: 'https://your-website.com/checkout/return',
});

checkout.mount('#checkout-container');
checkoutRef.current = checkout;
});

return () => {
cancelled = true;
checkoutRef.current?.unmount();
};
}, []);

return <div id="checkout-container" />;
}

A type-only import from @odus/checkout, as above, adds nothing to your bundle. Use it for the types you already reference.

Options​

loadOdus() takes the integration, 'checkout' or 'elements', and an optional second argument:

OptionTypeDefaultDescription
environment'test' | 'live''live'Where a failed load is reported. Pass the same environment you give the SDK.
timeoutnumber15000How long to wait for the checkout code, in milliseconds, before rejecting with timeout.
versionstring—An exact version to load instead of the latest. See Pin a version.
integritystring—The SRI hash of the pinned version. Requires version.
const { OdusCheckout } = await loadOdus('checkout', { environment: 'test' });

Handle a failed load​

The SDK has no bundled copy to fall back on. If the checkout code cannot load, loadOdus() rejects with an OdusLoadError, and no checkout is drawn. Show your customer a message and a way to try again; calling loadOdus() again after a failure starts a new attempt.

import { loadOdus, OdusLoadError } from '@odus/checkout/loader';

try {
const { OdusCheckout } = await loadOdus('checkout');
// Create and mount the checkout as usual
} catch (error) {
if (error instanceof OdusLoadError) {
showRetryMessage(); // Your own UI
console.error(error.code, error.message);
} else {
throw error;
}
}
error.codeWhat happenedWhat to do
blocked_by_cspYour page's Content Security Policy refused js.odus.com.Add it to script-src, and to style-src for the Drop-in in Safari earlier than 16.4. Retrying does not help.
networkThe checkout code could not be downloaded, or a pinned version failed its integrity check.Offer a retry. For a pinned version, check the hash.
timeoutThe checkout code did not arrive within timeout.Offer a retry.
incompatible_runtimeThe checkout code that arrived does not work with this loader.Offer a retry. If it persists, contact ODUS support.

Pin a version​

By default loadOdus() loads the checkout code we currently serve. To review every change before it reaches your customers instead, pin an exact version and its integrity hash:

const { OdusCheckout } = await loadOdus('checkout', {
version: '1.17.0',
integrity: 'sha384-…', // From manifest.json
});

The browser then refuses the file unless it matches the hash. A pinned version no longer receives our fixes; you move to a new one by changing version and integrity.

Every published version and the hash of each of its files are listed at https://js.odus.com/manifest.json. Use it to fill in integrity, or to keep an inventory of the scripts your payment page loads. The hash for the code above is in versions["1.17.0"].files["checkout.js"].integrity, or elements.js for Elements. Each file also lists its sha256, size and content type:

{
"schema": 1,
"versions": {
"1.17.0": {
"contract": 1,
"files": {
"checkout.js": {
"integrity": "sha384-…"
}
}
}
}
}

A published file never changes, so the hash of a version stays valid for as long as the version is served.