Paystack issues two key pairs: one for test mode and one for live mode. Each pair has a public key (prefixed pk_test_ or pk_live_) used on the frontend, and a secret key (prefixed sk_test_ or sk_live_) used only on the server. The public key initializes the checkout popup. The secret key authenticates API calls. Never expose a secret key in frontend code, and never use test keys in production.
When you create a Paystack account and navigate to Settings, then API Keys & Webhooks, you see four values. Each key has a prefix that tells you exactly what it is.
| Key | Prefix | Where It Belongs | What It Can Do |
|---|---|---|---|
| Test Public Key | pk_test_ | Frontend (browser) | Initialize test checkouts only |
| Test Secret Key | sk_test_ | Server only | Full API access in test mode |
| Live Public Key | pk_live_ | Frontend (browser) | Initialize live checkouts only |
| Live Secret Key | sk_live_ | Server only | Full API access in live mode |
The prefix is your first line of defence against mistakes. If you see sk_ anywhere in your frontend bundle, your build logs, or a browser network tab, something is wrong.
Public keys are deliberately limited. They can initialize the Paystack checkout popup and nothing else. They cannot list transactions, issue refunds, initiate transfers, or read customer data. That is why they are safe to embed in client-side JavaScript.
Secret keys have full access to every Paystack API endpoint. They can charge cards, move money to bank accounts, read all transaction history, create and delete subaccounts, and issue refunds. Treat them with the same care you would treat a database password.
The public key appears in one place in your frontend code: the Paystack Inline JS initialization. This is expected and safe.
<script src="https://js.paystack.co/v2/inline.js"></script>
<script>
var popup = new PaystackPop();
popup.checkout({
key: 'pk_live_xxxxxxxxxxxxxxxxxxxx',
email: 'customer@example.com',
amount: 500000,
onSuccess: function(transaction) {
// Send transaction.reference to your server for verification
window.location.href = '/verify?ref=' + transaction.reference;
},
onCancel: function() {
alert('Payment cancelled');
}
});
</script>
Notice the key starts with pk_. This key can do exactly one thing: open a checkout. It cannot verify a transaction, read customer data, or perform any other API operation. That is why it is safe to put it in HTML that every visitor can view.
In many frontend frameworks (React, Vue, Next.js), you will pass the public key through an environment variable like NEXT_PUBLIC_PAYSTACK_KEY or VITE_PAYSTACK_KEY. These variables are embedded into the JavaScript bundle at build time, so they are still publicly visible. That is fine for public keys. It is not fine for secret keys.
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
Every Paystack API call from your server uses the secret key in the Authorization header.
const https = require('https');
const options = {
hostname: 'api.paystack.co',
port: 443,
path: '/transaction/verify/ref_abc123',
method: 'GET',
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
},
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
var response = JSON.parse(data);
console.log('Status:', response.data.status);
console.log('Amount:', response.data.amount);
});
});
req.end();
The secret key authenticates you to Paystack's servers. It proves that the API call is coming from your application, not an impersonator. If someone obtains your secret key, they can:
This is why the secret key never leaves your server. It never goes in frontend code, mobile app code, or anywhere a user could extract it.
Test keys and live keys connect to separate environments on Paystack's side. Transactions created with test keys do not move real money. Transactions created with live keys debit real bank accounts and cards.
Key differences between the two modes:
Your code should be identical between test and live. The only difference is which key pair is loaded from your environment variables. This is the whole point of environment-based configuration: you deploy the same code to staging (with test keys) and production (with live keys).
// Your code does not change between environments.
// Only the environment variable changes.
var secret = process.env.PAYSTACK_SECRET_KEY;
// In staging: sk_test_xxxxx
// In production: sk_live_xxxxx
These are the mistakes developers make most often with Paystack keys. Every one of them has caused real incidents for real businesses.
1. Hardcoding the secret key in source code. The file gets committed to Git. The repo is public (or becomes public later). Now the key is exposed. Use environment variables instead. Always.
2. Using the secret key on the frontend. A developer copies the API example from the docs and puts it in a React component. The key is now in the JavaScript bundle that every browser downloads. Anyone can extract it from the browser's DevTools.
3. Using test keys in production. The checkout opens but no money is collected. Customers think they paid. Your database records a "successful" transaction. But the money is not real. This happens when you forget to swap environment variables during deployment.
4. Using live keys in development. You charge real cards while testing. You accidentally debit a customer for a test transaction. You process real refunds on test orders. Always use test keys in your development and staging environments.
5. Sharing keys in chat or email. A developer pastes the secret key in a Slack message or WhatsApp group. That message is now searchable, backed up, and stored on servers you do not control. Use a secrets manager or a password manager to share keys.
6. Logging the secret key. Your error handler logs the full request headers or environment variables to a file or a monitoring service. The secret key appears in plain text in your logs. Redact it before logging. See logging payments without logging secrets.
Log in to your Paystack dashboard at dashboard.paystack.com. Navigate to Settings, then select API Keys & Webhooks.
You will see your test keys immediately. Your live keys are available only after your business has been activated (KYC verification completed). Until activation, you can only use test mode.
Important details about the dashboard:
Key rotation means generating a new secret key and replacing the old one in all your systems. You should rotate keys in these situations:
When you regenerate a key on the Paystack dashboard, the old key stops working immediately. If your server is still using the old key, all API calls will fail. This is why you need a deployment plan for key rotation. Deploy the new key to your environment variables first, restart your servers, and confirm the new key works before treating the rotation as complete.
For a detailed procedure that avoids downtime, see rotating Paystack secret keys without downtime.
Store your Paystack keys in environment variables so they never appear in your source code. Here is the standard naming convention most teams follow:
# .env file (never committed to Git)
PAYSTACK_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxxx
PAYSTACK_PUBLIC_KEY=pk_test_xxxxxxxxxxxxxxxxxxxxxxx
On your server, load these from your hosting platform's environment configuration. On Vercel, Railway, Render, or Heroku, you set them in the dashboard. In Docker, you pass them through environment flags or a secrets file.
// server.js
var crypto = require('crypto');
var secret = process.env.PAYSTACK_SECRET_KEY;
if (!secret) {
throw new Error('PAYSTACK_SECRET_KEY is not set');
}
// Use the key for API calls and webhook verification
function verifyWebhook(body, signature) {
var hash = crypto.createHmac('sha512', secret).update(body).digest('hex');
return hash === signature;
}
Add .env to your .gitignore file. Confirm it is listed before your first commit. If you have already committed a .env file, the key is already in your Git history even if you delete the file. You need to rotate the key immediately. See detecting and remediating committed keys.
This article is part of the Paystack security and PCI compliance 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