When Paystack works in test but fails in live, the cause is almost always one of these: your business account is not fully activated (Paystack requires identity verification and business documents before enabling live payments), you are still using test keys (sk_test_) instead of live keys (sk_live_), your webhook URL is not HTTPS (Paystack requires HTTPS for live webhooks), your domain is not verified in the Paystack dashboard, or your callback URL uses HTTP instead of HTTPS. Run through the checklist in this guide to find the exact issue.
You have been testing your Paystack integration for days. Test cards work. Webhooks fire. Verification succeeds. You switch to live keys, and suddenly:
{ "status": false, "message": "Invalid key" }{ "status": false, "message": "Account not activated" }The fix depends on which of these symptoms you see. Walk through the checklist below from top to bottom. Each item takes under two minutes to check.
This is the most common cause by far. Paystack allows you to develop with test keys before your account is activated, but live keys only work after activation is complete.
To check your activation status:
Paystack activation requirements (for Nigerian businesses):
For other countries (Ghana, South Africa, Kenya), the requirements are similar but use local business registration and identity systems.
Activation usually takes 24 to 48 hours after submitting all documents. Some accounts activate within hours. If it has been more than 48 hours, contact Paystack support at support@paystack.com with your business name and registered email.
The fix: Complete all required fields in your business settings and wait for activation. You will receive an email when your account is activated. Until then, continue developing in test mode.
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
This sounds obvious, but it trips up more developers than you would expect. Your code must switch from test keys to live keys in two places: the server (secret key) and the frontend (public key).
// Check which keys your server is using
const key = process.env.PAYSTACK_SECRET_KEY?.trim();
console.log('Key prefix:', key?.substring(0, 8));
// Must show "sk_live_" for live mode
// In your frontend, check the public key
// Your PaystackPop.setup or script tag should use pk_live_, not pk_test_
Common mistakes:
The fix: Verify keys in both places.
// Server startup check
function verifyLiveMode() {
const secretKey = process.env.PAYSTACK_SECRET_KEY?.trim();
const publicKey = process.env.PAYSTACK_PUBLIC_KEY?.trim();
if (!secretKey?.startsWith('sk_live_')) {
console.error('WARNING: Server is NOT using live Paystack keys');
console.error(`Current prefix: ${secretKey?.substring(0, 8)}`);
}
if (publicKey && !publicKey.startsWith('pk_live_')) {
console.error('WARNING: Public key is NOT a live key');
console.error(`Current prefix: ${publicKey?.substring(0, 8)}`);
}
if (secretKey?.startsWith('sk_live_') && publicKey?.startsWith('pk_live_')) {
console.log('Paystack live mode confirmed');
}
}
verifyLiveMode();
Paystack accepts HTTP webhook URLs in test mode. In live mode, it requires HTTPS. If your webhook URL starts with http://, Paystack will not deliver webhooks in live mode.
To check your webhook URL:
https://// Correct live webhook URL
https://yoursite.com/api/webhooks/paystack
// WRONG: HTTP will not work in live mode
http://yoursite.com/api/webhooks/paystack
// WRONG: localhost will not work in live mode
http://localhost:3000/api/webhooks/paystack
Your webhook URL must:
If you have not set up HTTPS yet, see Paystack Mixed Content and HTTPS Errors for a guide on getting a free SSL certificate.
The fix: Update your webhook URL in the Paystack dashboard to use HTTPS. Make sure the endpoint is deployed and reachable before changing the URL.
Similar to webhook URLs, Paystack requires HTTPS for callback URLs in live mode. If your callback_url uses HTTP, the redirect after payment may fail silently.
// BROKEN in live mode: HTTP callback_url
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: 'customer@example.com',
amount: 500000,
callback_url: 'http://yoursite.com/payment/verify', // HTTP won't work in live mode
}),
});
// FIXED: HTTPS callback_url
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: 'customer@example.com',
amount: 500000,
callback_url: 'https://yoursite.com/payment/verify', // HTTPS required
}),
});
Also check the default callback URL in your Paystack dashboard under Settings > API Keys & Webhooks. If it uses HTTP, update it to HTTPS.
Paystack may require domain verification for live transactions. This confirms that you own the domain where the checkout popup or redirect will run.
To verify your domain:
yoursite.com)Domain verification is not always required. It depends on your account type and risk profile. But if your live transactions are failing and everything else on this checklist is correct, unverified domain could be the issue. Add and verify your domain to rule it out.
If you use multiple domains (e.g., yoursite.com and app.yoursite.com), verify each one separately.
The most frustrating version of this problem: your code is correct, your local .env file has live keys, but your production server is still using test keys. This happens when you update your local file but forget to update the deployment platform.
Check your production environment variables:
# Vercel
vercel env ls
# Look for PAYSTACK_SECRET_KEY and PAYSTACK_PUBLIC_KEY
# Verify they start with sk_live_ and pk_live_
# Railway
railway variables
# Check the Paystack-related variables
# Render
# Check the Environment tab in your Render dashboard
# Heroku
heroku config
# Look for Paystack key values
A common trap: multiple environments. Many platforms let you set different environment variables for production, preview, and development. If you set your live key only for "production" but your deployment is tagged as "preview", it will use the test key.
# Vercel: set for production specifically
vercel env add PAYSTACK_SECRET_KEY production
# Enter: sk_live_xxxxxxxxxxxxxxxx
# Verify it is set for production
vercel env ls production
Another trap: stale deployments. If you change environment variables on your platform without redeploying, the running server still uses the old values. Most platforms require a redeploy for environment variable changes to take effect.
# Force a redeploy after changing environment variables
# Vercel: push a commit or click "Redeploy" in the dashboard
# Railway: click "Redeploy" in the dashboard
# Render: deployments redeploy automatically on env var changes
Before switching from test to live, run through this checklist. Each item takes under a minute.
// Go-live checklist for Paystack integration
const goLiveChecklist = [
{
item: 'Business account activated',
check: 'Dashboard shows no activation banner. Live keys are available.',
},
{
item: 'Server uses sk_live_ secret key',
check: 'Log key prefix at startup. Confirm sk_live_.',
},
{
item: 'Frontend uses pk_live_ public key',
check: 'Inspect checkout popup or script tag. Confirm pk_live_.',
},
{
item: 'Webhook URL is HTTPS',
check: 'Dashboard > Settings > API Keys & Webhooks. URL starts with https://.',
},
{
item: 'Webhook endpoint returns 200',
check: 'curl -X POST your webhook URL. Should return 200.',
},
{
item: 'Callback URL is HTTPS',
check: 'callback_url in transaction/initialize starts with https://.',
},
{
item: 'Domain is verified',
check: 'Dashboard > Settings > Domains. Domain shows verified.',
},
{
item: 'Production env vars are updated',
check: 'Check deployment platform. Verify live keys are set for production.',
},
{
item: 'Production server is redeployed',
check: 'Redeploy after changing env vars.',
},
{
item: 'Test transaction with real card',
check: 'Pay 100 NGN with a real card. Verify webhook, callback, and verification all work.',
},
{
item: 'Refund the test transaction',
check: 'Refund the 100 NGN from the Paystack dashboard to confirm refunds work.',
},
];
// Log incomplete items
goLiveChecklist.forEach(({ item, check }) => {
console.log(`[ ] ${item}`);
console.log(` How: ${check}`);
});
After completing the checklist, verify live mode works end to end:
Step 1: Verify the live key is valid.
// Run from your server (not the browser)
async function verifyLiveKey() {
const key = process.env.PAYSTACK_SECRET_KEY?.trim();
if (!key?.startsWith('sk_live_')) {
console.error('Not a live key:', key?.substring(0, 8));
return;
}
const res = await fetch('https://api.paystack.co/transaction?perPage=1', {
headers: { Authorization: `Bearer ${key}` },
});
const data = await res.json();
if (res.status === 200 && data.status) {
console.log('Live key is valid and working');
} else {
console.error('Live key rejected:', data.message);
}
}
verifyLiveKey();
Step 2: Initialize a real transaction.
Use your production server to initialize a transaction for a small amount (100 NGN or equivalent). Confirm you get an authorization_url back. Open the URL in your browser and confirm the Paystack checkout page loads.
Step 3: Complete a real payment.
Use a real debit card to pay the small amount. Confirm:
charge.success eventStep 4: Refund the test payment.
In the Paystack dashboard, find the test transaction and initiate a refund. This confirms refunds work and returns the money to your card.
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