On this page
No Headings
Last updated: May 21, 2026
Find solutions to common PayPal integration issues.
Make sure index.html is in the public folder. Express serves static files from there.
The client ID and secret don't match, or there's a mix of sandbox and production credentials. PayPal provides separate credentials for sandbox and production. Make sure your client ID and secret are both from the same environment.
If your app uses PayPal buttons across multiple pages or a modular JavaScript architecture, errors typically stem from one root cause: the SDK instance isn't accessible where your code expects it. The following errors are related symptoms of the same scoping problem.
window.paypalSdkInstance is undefinedThis browser console error means the SDK instance was created but never exposed outside of onLoad().
In the file where you define onLoad() (usually paypal-init.js or index.html), add the following two lines after creating the instance:
async function onLoad() {
try {
const sdkInstance = await window.paypal.createInstance({...});
// ⚠️ ADD THIS LINE
window.paypalSdkInstance = sdkInstance;
// ⚠️ ALSO ADD THIS (required for the waiting pattern)
window.dispatchEvent(new CustomEvent('paypalReady', {
detail: { instance: sdkInstance }
}));
}
}To verify the fix, open the browser console and run:
window.paypalSdkInstanceYou should see an object with methods such as createPayPalOneTimePaymentSession.
These symptoms indicate the SDK instance isn't accessible to pages other than the one where it was initialized:
To fix this, include the SDK script and initialization on every page that has PayPal buttons. The cleanest way to do this is through a shared layout or template:
<!-- Include in your shared layout, or add to EVERY page with PayPal buttons -->
<script async
src="https://www.sandbox.paypal.com/web-sdk/v6/core"
onload='onLoad()'>
</script>
<script src="/js/paypal-init.js"></script>For Single-Page Applications (SPAs), set window.paypalSdkInstance during initialization and ensure it persists across route changes.
A typical file structure for multipage apps looks like this:
your-app/
├── public/
│ ├── index.html # Loads SDK script
│ ├── js/
│ │ ├── paypal-init.js # Contains onLoad() and initPayPalButton()
│ │ ├── products.js # Product page buttons
│ │ ├── cart.js # Cart page button
│ │ └── checkout.js # Checkout page button
├── server.js
└── .envThese symptoms indicate your code is trying to access window.paypalSdkInstance before onLoad() has finished creating it:
The problem is a race condition between when the SDK loads and when your page scripts run:
Timeline 1 (Success): SDK loads → onLoad() → createInstance() → Your code runs ✓
Timeline 2 (Failure): Your code runs → Error! → SDK loads (too late) ✗In any file other than your main initialization file, use this waiting pattern instead of accessing window.paypalSdkInstance directly:
function initButtonWhenReady(containerId) {
if (window.paypalSdkInstance) {
// SDK already loaded, initialize immediately
initPayPalButton(containerId);
} else {
// SDK still loading, wait for it
window.addEventListener('paypalReady', () => {
initPayPalButton(containerId);
}, { once: true });
}
}
document.addEventListener('DOMContentLoaded', () => {
initButtonWhenReady('#my-button');
});This pattern handles both timelines:
paypalReady event dispatched in onLoad().On the page where buttons aren't appearing, open the browser console and run:
console.log('SDK loaded:', !!window.paypal);
console.log('Instance ready:', !!window.paypalSdkInstance);If either value is false:
window.paypal is false: the SDK script isn't loading on this page.window.paypalSdkInstance is false: onLoad() isn't running, or window.paypalSdkInstance = sdkInstance is missing.