Paystack Access Codes: What They Are and How Long They Live
A Paystack access code is a short alphanumeric string returned alongside the authorization_url when you initialize a transaction. You pass it to PaystackPop.checkout() for inline (popup) checkout. Access codes expire after a period of inactivity, typically around 24 hours, though Paystack does not publish a guaranteed TTL. If an access code expires, initialize a new transaction.
What Is an Access Code?
When you call POST /transaction/initialize, Paystack returns a response with three fields that matter for checkout:
// Response from POST /transaction/initialize
{
"status": true,
"message": "Authorization URL created",
"data": {
"authorization_url": "https://checkout.paystack.com/0peioxfhpn",
"access_code": "0peioxfhpn",
"reference": "order_142_1721472000000"
}
}
Notice that the access code is the tail end of the authorization URL. They are two ways to reach the same checkout session. The authorization_url is a full URL you can redirect the browser to. The access_code is the short identifier you pass to Paystack's inline JavaScript library when you want a popup checkout instead of a full-page redirect.
Think of the access code as a session ticket. It tells Paystack which transaction the customer is paying for, what amount to charge, which payment channels to show, and what metadata to attach. All of that was locked in when you called initialize.
How Long Do Access Codes Live?
Paystack does not publish an official TTL (time to live) for access codes. In practice, they expire after a period of inactivity, generally within 24 hours of creation. This is a deliberate design choice: checkout sessions should not live forever because amounts, exchange rates, and inventory can change.
Here is what this means for your integration:
- If a customer clicks "Pay" and you initialize a transaction, the access code is valid for long enough to complete a normal checkout flow (minutes to a few hours).
- If you initialize a transaction, store the access code, and try to use it the next day, it will almost certainly be expired.
- If a customer opens the checkout popup but walks away for several hours without completing payment, the session may expire and the popup will show an error.
The practical rule: initialize the transaction as close to the moment of payment as possible. Do not pre-initialize transactions during page load or when the user adds items to a cart. Initialize when the user clicks the "Pay Now" button.
What Happens When an Access Code Expires
When you try to use an expired access code with PaystackPop, the behavior varies depending on the library version and the browser. Common outcomes include:
- The popup opens but shows an error message like "This transaction has expired"
- The popup opens to a blank or loading screen that never resolves
- The checkout form appears but fails when the customer tries to submit payment
If you are using redirect checkout with the authorization URL, an expired session typically shows an error page on Paystack's checkout domain.
None of these are good experiences for your customer. The fix is to detect the failure and re-initialize.
// Frontend: handle checkout with expiration recovery
async function openCheckout() {
// Always get a fresh access code from your server
var response = await fetch('/api/pay', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: customerEmail,
amount: orderAmount,
orderId: currentOrderId,
}),
});
var result = await response.json();
var popup = new PaystackPop();
popup.checkout({
accessCode: result.access_code,
onSuccess: function(transaction) {
verifyPayment(transaction.reference);
},
onCancel: function() {
// Customer closed the popup
// The access code may still be valid for a retry
// But safest to re-initialize on next attempt
console.log('Checkout cancelled');
},
});
}
The key pattern: call your server to initialize a fresh transaction every time the customer clicks "Pay." Do not cache access codes on the frontend.
Common Mistakes with Access Codes
Mistake 1: Pre-initializing transactions on page load
Some developers call the initialize endpoint when the checkout page loads, store the access code in a variable, and use it when the customer clicks "Pay" five or thirty minutes later. If the customer takes too long, the access code might expire. Worse, if the order details change (quantity updated, coupon applied), the pre-initialized transaction has the wrong amount.
Initialize when the customer commits to paying, not when they land on the page.
Mistake 2: Storing access codes in a database for later use
Access codes are ephemeral. They are tied to a specific checkout session that expires. Storing them in your database and trying to reuse them hours or days later will fail. If you need to let a customer resume a payment later, initialize a new transaction with the same reference. Paystack will return the existing transaction if the reference matches and it has not been completed.
Mistake 3: Using the access code as a transaction identifier
The access code identifies the checkout session, not the transaction. Use the reference for tracking, verification, and reconciliation. The reference is the stable identifier that appears in webhooks, verify responses, and your dashboard. The access code is just a checkout entry point.
Mistake 4: Exposing the initialize endpoint without rate limiting
Every call to /transaction/initialize creates a pending transaction on Paystack's side. If your frontend endpoint that triggers initialization has no rate limiting, a bot or a frustrated user clicking rapidly can create hundreds of pending transactions. Add rate limiting on your server and use deterministic references (tied to the order ID) so that duplicate initialize calls return the same transaction.
Access Codes in Mobile Apps
If you are building a mobile app (React Native, Flutter, or native Android/iOS), access codes work the same way. Your mobile app calls your backend to initialize, gets back the access code, and uses it to open a WebView-based checkout.
// React Native example (conceptual)
// 1. Call your backend
var response = await fetch('https://yourapi.com/api/pay', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: email, amount: amount }),
});
var data = await response.json();
// 2. Open the authorization URL in a WebView
// or use a Paystack mobile SDK that accepts the access code
var checkoutUrl = data.authorization_url;
// Navigate to checkoutUrl in your in-app browser
The access code expiration behavior is the same on mobile. Initialize just before the customer is ready to pay. If the app goes to the background and the user comes back much later, initialize again.
Some Paystack mobile SDKs accept the access code directly and handle the WebView internally. Check the SDK documentation for your platform. The core rule stays the same: fresh access code, server-side initialization, verify after payment.
Reusing References vs Reusing Access Codes
There is an important distinction between reusing a reference and reusing an access code.
Reusing a reference: If you call /transaction/initialize with the same reference as an existing pending transaction, Paystack returns the existing transaction (with a fresh access code and authorization URL). This is the correct way to handle retries. If a customer's first checkout attempt fails or is abandoned, you can initialize again with the same reference and Paystack will not create a duplicate.
// Server-side: re-initialize with the same reference
var reference = 'order_' + orderId;
var 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: customerEmail,
amount: amountInKobo,
reference: reference, // Same reference as before
}),
});
var data = await response.json();
// data.data.access_code is a fresh access code
// data.data.authorization_url is a fresh URL
// Both point to the same underlying transaction
Reusing an access code: This does not work reliably. Once an access code expires or the checkout session ends, the code is dead. Always get a fresh one from the initialize response.
The pattern that works: tie your reference to the order, and always call initialize before opening checkout. Let Paystack handle the deduplication through the reference. For deeper patterns on reference design, see transaction reference design patterns.
Debugging Access Code Issues
When the checkout popup fails silently or shows a vague error, here is how to narrow it down.
Check the transaction status on your dashboard
Log into your Paystack dashboard and search for the transaction reference. If the transaction shows as "abandoned" or "failed," the access code has expired or the checkout session ended. Initialize a new transaction.
Check the browser console
PaystackPop logs errors to the browser console when something goes wrong. Open your browser's developer tools and look for messages from the Paystack inline script. Common messages include session expiry notices and network errors.
Verify the access code matches the environment
A test access code (from a transaction initialized with a test secret key) will not work in a live checkout, and vice versa. Make sure your frontend is loading the correct Paystack inline script and that the access code was generated with the matching secret key.
Check the timing
Log timestamps when you initialize the transaction and when you attempt to open checkout. If there is a gap of more than a few hours, the access code may have expired. Restructure your flow to initialize closer to the payment attempt.
For more on debugging payment issues, see the Transaction Timeline API and reading gateway responses.
Key Takeaways
- ✓An access code is a short string (like "0peioxfhpn") returned when you initialize a transaction. It identifies the checkout session for use with the inline popup.
- ✓The authorization_url and the access_code point to the same transaction. The URL is for redirect checkout; the access code is for PaystackPop inline checkout.
- ✓Access codes expire after a period of inactivity. Do not store them for later use. Always initialize a fresh transaction when the customer is ready to pay.
- ✓If you try to use an expired access code, PaystackPop will show an error or a blank checkout. Your frontend should catch this and re-initialize the transaction.
- ✓Never expose your secret key to retrieve or refresh access codes on the frontend. The initialize call must always happen server-side.
- ✓Access codes are single-use in practice. Once a customer completes or abandons the checkout, the access code cannot be reused for a new payment.
Frequently Asked Questions
- Can I extend the lifetime of a Paystack access code?
- No. There is no API call to extend or refresh an access code. If the code has expired, you need to call /transaction/initialize again to get a new one. If you use the same reference, Paystack returns the existing transaction with a fresh access code.
- Is the access code the same as the authorization code?
- No. The access code identifies a checkout session and is used to open the payment popup. The authorization code (auth_code) is returned after a successful card payment and can be used to charge the same card again in the future. They serve completely different purposes.
- Can I use an access code on a different domain than where the transaction was initialized?
- Yes. The access code is not domain-locked. You can initialize a transaction from your server and use the access code on any frontend that loads the Paystack inline script. However, the callback URL you specified during initialization determines where the customer is redirected after payment.
- What happens if I call initialize with the same reference twice?
- If the first transaction is still pending (not completed, not abandoned), Paystack returns the existing transaction with a new access code and authorization URL. This is the designed behavior for retry scenarios. If the transaction was already completed, Paystack returns an error.
- Do I need the access code if I am using redirect checkout?
- No. For redirect checkout, you only need the authorization_url. Redirect the customer to that URL and Paystack handles everything. The access code is specifically for inline/popup checkout using PaystackPop.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.
Apply to the McTaba Marathon