The correct script URL is https://js.paystack.co/v2/inline.js (V2) or https://js.paystack.co/v1/inline.js (V1). If the script is not loading, check: (1) The URL in your script tag is correct with no typos. (2) Your Content Security Policy allows scripts from js.paystack.co. (3) No ad blocker or privacy extension is blocking the request. (4) The network request is not failing due to DNS or connectivity issues. Open your browser DevTools Network tab and filter for "paystack" to see exactly what happened to the request.
When the Paystack Inline JS script fails to load, you see one or more of these symptoms:
PaystackPop is not defined error in the consoleReferenceError: PaystackPop is not definedThe fastest way to diagnose is to open DevTools (F12), go to the Console tab, and type:
typeof PaystackPop
// "function" = script loaded successfully
// "undefined" = script did not load
If it returns "undefined", the script is not loaded. Go to the Network tab to find out why.
The script URL must be exactly right. There are only two valid URLs:
<!-- V2 (recommended for new integrations) -->
<script src="https://js.paystack.co/v2/inline.js"></script>
<!-- V1 (older, still works) -->
<script src="https://js.paystack.co/v1/inline.js"></script>
Common wrong URLs:
<!-- WRONG: http instead of https -->
<script src="http://js.paystack.co/v2/inline.js"></script>
<!-- WRONG: old or unofficial CDN link -->
<script src="https://cdn.paystack.co/js/inline.js"></script>
<!-- WRONG: typo in domain -->
<script src="https://js.paystck.co/v2/inline.js"></script>
<!-- WRONG: missing version path -->
<script src="https://js.paystack.co/inline.js"></script>
<!-- WRONG: self-hosted copy (will go stale) -->
<script src="/scripts/paystack-inline.js"></script>
Always load the script directly from js.paystack.co. Do not self-host a copy. Paystack updates the script and a self-hosted copy will eventually break.
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
If you load V2 but use V1 API syntax (or vice versa), the script loads but your code fails.
V1 usage (with the V1 script):
// V1 pattern
var handler = PaystackPop.setup({
key: 'pk_test_xxxxx',
email: 'customer@email.com',
amount: 50000,
callback: function(response) {
alert('Payment complete: ' + response.reference);
},
onClose: function() {
alert('Transaction was not completed');
},
});
handler.openIframe();
V2 usage (with the V2 script):
// V2 pattern
const paystack = new PaystackPop();
paystack.checkout({
key: 'pk_test_xxxxx',
email: 'customer@email.com',
amount: 50000,
onSuccess: function(transaction) {
console.log('Payment complete:', transaction.reference);
},
onCancel: function() {
console.log('Payment cancelled');
},
});
If you are on V2 but using V1 syntax, the popup may not open. Check which version you loaded and use the matching API pattern. For new projects, use V2.
If you are migrating from V1 to V2, see Migrating from Paystack InlineJS V1 to V2.
If your site has a Content Security Policy that does not include Paystack's domain, the browser blocks the script silently.
CSP violation in the console:
Refused to load the script 'https://js.paystack.co/v2/inline.js' because
it violates the following Content Security Policy directive: "script-src 'self'"
Required CSP directives for Paystack:
Content-Security-Policy:
script-src 'self' https://js.paystack.co;
frame-src 'self' https://checkout.paystack.com;
connect-src 'self' https://api.paystack.co;
You need three directives:
script-src: allows loading the Inline JS script from js.paystack.coframe-src: allows the Paystack checkout iframe from checkout.paystack.comconnect-src: allows API calls to api.paystack.co (if you make client-side API calls)How to find your CSP:
Content-Security-Policy<meta http-equiv="Content-Security-Policy"> tag in your HTMLIf you do not have a CSP, this is not the cause. CSP is opt-in. If you never configured one, it is not blocking anything.
Ad blockers and privacy extensions are a significant issue for payment scripts. Extensions like uBlock Origin, AdBlock Plus, Privacy Badger, and Brave's built-in shields can block the Paystack script.
These tools use filter lists that target third-party scripts, especially those from payment and tracking domains. Even though Paystack is a legitimate payment processor, the URL pattern matches common blocking rules.
How to verify:
What you can do:
You cannot force users to disable their ad blockers. Your best option is to detect when the script is blocked and provide an alternative checkout method.
// Detect if Paystack script loaded
function isPaystackAvailable() {
return typeof PaystackPop !== 'undefined';
}
// Show appropriate UI
function renderPaymentButton() {
const container = document.getElementById('payment-container');
if (isPaystackAvailable()) {
container.innerHTML = '<button id="pay-inline">Pay Now</button>';
document.getElementById('pay-inline').addEventListener('click', openInlineCheckout);
} else {
container.innerHTML =
'<p>Your browser is blocking our payment widget. ' +
'Click below to pay on our secure payment page.</p>' +
'<button id="pay-redirect">Pay on Secure Page</button>';
document.getElementById('pay-redirect').addEventListener('click', redirectToCheckout);
}
}
// Check after the script should have loaded
window.addEventListener('load', renderPaymentButton);
The script load can fail due to network problems:
How to check in DevTools:
For network issues on slow African internet connections, add error handling and retry logic to your script loader:
function loadPaystackScript(retries = 3) {
return new Promise((resolve, reject) => {
if (typeof PaystackPop !== 'undefined') {
resolve();
return;
}
const script = document.createElement('script');
script.src = 'https://js.paystack.co/v2/inline.js';
script.async = true;
script.onload = () => resolve();
script.onerror = () => {
document.head.removeChild(script);
if (retries > 0) {
console.warn('Paystack script failed to load. Retrying...');
setTimeout(() => {
loadPaystackScript(retries - 1).then(resolve).catch(reject);
}, 2000);
} else {
reject(new Error('Failed to load Paystack script after retries'));
}
};
document.head.appendChild(script);
});
}
The most robust approach is to build a fallback that works when the Inline JS script cannot load. The Paystack redirect checkout initializes the transaction on your server and redirects the customer to a Paystack-hosted checkout page. No client-side script needed.
Server-side (Node.js/Express):
app.post('/api/checkout', async (req, res) => {
const { email, amount } = req.body;
const response = await fetch('https://api.paystack.co/transaction/initialize', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
email,
amount,
callback_url: `${process.env.APP_URL}/payment/callback`,
}),
});
const data = await response.json();
if (data.status) {
// Redirect user to Paystack hosted checkout
res.redirect(data.data.authorization_url);
} else {
res.status(400).json({ error: data.message });
}
});
Client-side with automatic fallback:
async function pay(email, amount) {
if (typeof PaystackPop !== 'undefined') {
// Inline JS available, use the popup
const handler = PaystackPop.setup({
key: 'pk_live_xxxxx',
email: email,
amount: amount,
callback: function(response) {
window.location.href = '/payment/verify?ref=' + response.reference;
},
});
handler.openIframe();
} else {
// Inline JS not available, use redirect checkout
const form = document.createElement('form');
form.method = 'POST';
form.action = '/api/checkout';
const emailInput = document.createElement('input');
emailInput.type = 'hidden';
emailInput.name = 'email';
emailInput.value = email;
const amountInput = document.createElement('input');
amountInput.type = 'hidden';
amountInput.name = 'amount';
amountInput.value = amount;
form.appendChild(emailInput);
form.appendChild(amountInput);
document.body.appendChild(form);
form.submit();
}
}
This fallback ensures that customers can always pay, regardless of ad blockers, CSP issues, or network problems with the script CDN. The redirect checkout is slightly less smooth (it leaves your page) but it is reliable.
Each frontend framework has a preferred way to load external scripts.
Next.js (App Router):
// app/layout.tsx
import Script from 'next/script';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Script
src="https://js.paystack.co/v2/inline.js"
strategy="lazyOnload"
onLoad={() => console.log('Paystack script loaded')}
onError={() => console.error('Paystack script failed to load')}
/>
</body>
</html>
);
}
React (dynamic loading):
import { useEffect, useState } from 'react';
export function usePaystack() {
const [ready, setReady] = useState(false);
const [error, setError] = useState(false);
useEffect(() => {
if (typeof window.PaystackPop !== 'undefined') {
setReady(true);
return;
}
const script = document.createElement('script');
script.src = 'https://js.paystack.co/v2/inline.js';
script.async = true;
script.onload = () => setReady(true);
script.onerror = () => setError(true);
document.head.appendChild(script);
return () => { document.head.removeChild(script); };
}, []);
return { ready, error };
}
Plain HTML (with load detection):
<script
src="https://js.paystack.co/v2/inline.js"
async
onload="window.paystackReady = true"
onerror="window.paystackFailed = true"
></script>
<script>
// Wait for DOM and script to be ready
window.addEventListener('load', function() {
if (window.paystackFailed || typeof PaystackPop === 'undefined') {
console.warn('Paystack script not available. Enabling fallback.');
document.getElementById('fallback-notice').style.display = 'block';
}
});
</script>
After fixing the script loading issue:
typeof PaystackPop. Should return "function".For the complete error reference, see Paystack Errors and Troubleshooting: The Complete Reference. For popup-specific issues after the script loads, see Paystack Popup Not Opening: Frontend Debug Guide.
KES 2,999
The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.
Start Paystack Course