Paystack Terminal is a physical payment device you control via API. You send a payment request to the Terminal API with an amount and terminal ID. The device displays the amount and prompts the customer to tap, insert, or swipe their card. Once the payment completes, Paystack sends you a webhook with the transaction result. Your backend processes the webhook the same way it handles any Paystack transaction event.
Paystack Terminal is a physical point-of-sale device that connects to your Paystack account. It accepts card payments in person: contactless tap (NFC), chip-and-PIN insert, and magnetic stripe swipe.
What it is: A hardware device with a screen, card reader, and network connection. It runs software that communicates with Paystack's servers. You control it through the Terminal API, pushing payment requests from your application.
What it is not: It is not a standalone cash register. It does not manage inventory, print itemised receipts, or handle refunds on the device itself. Those features come from your application. The terminal is the payment acceptance layer. Your application is the business logic layer.
How it differs from online payments: With online payments, the customer types their card details into a web form. With Terminal, the customer presents their physical card to the device. The security model is different (PCI requirements shift to the device and Paystack), but the result is the same: a Paystack transaction you can verify and track through the API.
For the full overview of in-person payments with Paystack, see the complete Terminal guide.
The Terminal system has four components that communicate in a specific order.
1. Your application (backend). This is where the transaction starts. Your point-of-sale app, order management system, or any backend service creates a payment request with an amount and sends it to the Paystack Terminal API.
2. Paystack Terminal API. Paystack receives your payment request and pushes it to the physical terminal device. The API is at https://api.paystack.co/terminal.
3. The physical terminal. The device displays the amount, prompts the customer to present their card, processes the card interaction, and sends the result back to Paystack.
4. Paystack webhooks. After the transaction completes (or fails), Paystack sends a webhook to your server with the transaction details. You process this webhook the same way you process any Paystack transaction event.
The flow:
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
Before you can send payment requests to a terminal, it must be registered on your Paystack account.
Physical registration: When you receive a Paystack Terminal device, you register it through the Paystack dashboard. The device has a serial number. You enter the serial number in the dashboard, and Paystack assigns a terminal ID to it.
Listing your terminals: Use the API to get a list of all terminals on your account.
// list-terminals.js
const axios = require('axios');
async function listTerminals() {
var response = await axios.get(
'https://api.paystack.co/terminal',
{
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
},
}
);
return response.data.data;
}
// Returns an array of terminal objects with id, serial_number, device_make, status, etc.
Terminal status: A terminal can be "active" (ready to accept payments) or "inactive" (not connected or not configured). Check the status before sending payment requests.
Multiple terminals: A single Paystack account can have multiple terminals. Each has its own terminal ID. Your application routes payment requests to the correct terminal based on location, register number, or operator assignment.
To collect a payment, you send an event to the terminal with the payment details.
// push-payment.js
const axios = require('axios');
async function pushPaymentToTerminal(terminalId, amount, reference) {
var response = await axios.post(
'https://api.paystack.co/terminal/' + terminalId + '/event',
{
type: 'invoice',
action: 'process',
data: {
id: reference,
amount: amount, // in kobo
},
},
{
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
'Content-Type': 'application/json',
},
}
);
return response.data;
}
// Usage: Push a 5,000 NGN payment to terminal
var result = await pushPaymentToTerminal(
'TERMINAL_ID',
500000, // 5000 NGN in kobo
'ORDER-' + Date.now()
);
Amount is in kobo. Like all Paystack amounts, terminal payments use the smallest currency unit. 500000 kobo = 5,000 NGN.
Reference is your order ID. Use a unique reference for each transaction so you can match the webhook response to the correct order in your system.
The terminal displays the amount immediately. Once the API call succeeds, the device shows the amount on its screen and waits for the customer to present their card. There is no delay between your API call and the terminal display.
The transaction result comes through your standard Paystack webhook endpoint. If you already handle charge.success webhooks for online payments, the same handler works for terminal payments.
// terminal-webhook.js
var crypto = require('crypto');
function handleTerminalWebhook(req, res) {
// Verify signature (same as any Paystack webhook)
var hash = crypto
.createHmac('sha512', process.env.PAYSTACK_SECRET_KEY)
.update(JSON.stringify(req.body))
.digest('hex');
if (hash !== req.headers['x-paystack-signature']) {
return res.status(401).send('Invalid signature');
}
var event = req.body;
if (event.event === 'charge.success') {
var transaction = event.data;
var reference = transaction.reference;
var amount = transaction.amount;
var channel = transaction.channel; // "card" for terminal payments
// Update your order
console.log('Payment received: ' + reference + ' Amount: ' + amount + ' Channel: ' + channel);
// Mark the order as paid in your database
}
return res.status(200).send('OK');
}
The channel field tells you how the payment was made. For terminal payments, the channel is typically "card". For online payments, it might be "card", "bank", or "mobile_money". You can use this field to distinguish terminal transactions from online ones in your reporting.
Terminal-specific events. In addition to standard charge events, Paystack may send terminal-specific events for device status changes, connection issues, or payment request timeouts. Handle these to keep your system in sync with the device state.
The physical terminal device has characteristics that affect your application design.
Connectivity. Terminals need an internet connection. They typically connect via Wi-Fi or mobile data (SIM card). If the connection drops during a transaction, the device will attempt to reconnect and complete the transaction. Your application should handle delayed webhook notifications.
Power. Terminals have batteries for portability but need charging. If a terminal dies mid-transaction, the payment does not go through. Your application should handle the case where a payment request was sent but no webhook arrives (timeout scenario).
Screen limitations. The terminal screen is small. Custom messages or prompts sent to the terminal should be short and clear. Do not send paragraphs of text.
Card types supported. Terminals typically support Visa, Mastercard, and Verve cards through contactless (NFC), chip-and-PIN, and magnetic stripe. The exact card types depend on your market and terminal model.
Physical security. Terminals store sensitive card-processing firmware. If a device is lost or stolen, deactivate it immediately through the Paystack dashboard. Paystack handles the PCI compliance for the device itself.
You do not need a physical terminal to start developing. Paystack provides ways to test terminal integrations in development.
Virtual terminals. In test mode, you can create virtual terminals that simulate the behaviour of physical devices. Push payment requests to them and receive simulated webhook responses.
Test mode limitations. Test mode simulates the API flow but not the physical card interaction. You cannot test NFC tap behaviour, chip-and-PIN prompts, or receipt printing in test mode. These require a physical device.
Staging with real devices. When you move to integration testing with physical terminals, use test keys and test cards. Paystack test cards work on physical terminals in test mode just as they work online.
For comprehensive testing strategies, see the complete testing guide.
Ignoring timeouts. A customer might walk away without presenting their card. Your app should handle the case where a payment request was sent but no result comes back. Implement a timeout (e.g., 5 minutes) after which you cancel the pending payment and reset the terminal.
Not matching references. Every payment request should have a unique reference that maps to an order in your system. Without this, you cannot match webhooks to orders.
Assuming instant processing. Unlike online payments where the customer is actively completing a form, terminal payments depend on physical actions. The customer might need to find their card, remember their PIN, or try a different card. Build your UX to handle a few minutes of waiting.
Skipping webhook verification. Terminal webhooks must be verified with your secret key just like online webhooks. Do not skip signature verification because "it is coming from a terminal."
Not handling device offline scenarios. If the terminal is offline when you send a payment request, the API call may succeed (Paystack queues the event) but the payment will not process until the device reconnects. Handle queued states in your application.
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