To restrict payment channels on Paystack checkout, pass a channels array when initializing the transaction. Valid values are "card", "bank", "ussd", "mobile_money", "bank_transfer", "qr", and "apple_pay". Only the channels you list will appear on the checkout page. Omit the channels parameter to show all available channels.
When you initialize a transaction, you can include a channels array in the request body. Paystack checkout will only show the channels you list. If you omit the parameter entirely, Paystack shows all channels available for the transaction's currency and your account.
// Only show card and bank transfer
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: 'customer@example.com',
amount: 250000, // 2,500 NGN
currency: 'NGN',
channels: ['card', 'bank_transfer'],
}),
});
The valid channel values are:
card - Visa, Mastercard, Vervebank - Pay with bank (different from bank_transfer)ussd - USSD banking codesmobile_money - MTN MoMo, AirtelTigo, Vodafone Cashbank_transfer - Transfer to a temporary bank accountqr - QR code scanningapple_pay - Apple Pay on supported devicesNote the difference between bank and bank_transfer. The bank channel refers to "Pay with Bank" (online banking), while bank_transfer is the transfer-to-account method where the customer manually transfers from their banking app.
Scenario 1: You need instant confirmation
Card payments confirm in seconds. Bank transfers, USSD, and mobile money can take anywhere from 30 seconds to several minutes. If your product requires immediate confirmation (a concert ticket about to sell out, a flash sale item, a time-limited offer), restrict to ['card', 'apple_pay'] so the payment confirms instantly.
Scenario 2: The amount is too small for some channels
Some USSD channels have minimum transaction amounts. Very small transactions (under 100 NGN, for example) may not work on all USSD banks. For micro-transactions, card or bank transfer may be more reliable.
Scenario 3: The amount is too large for some channels
USSD channels typically have per-transaction limits (which vary by bank). For transactions above a certain threshold, restrict to card and bank transfer to avoid the customer selecting USSD only to find their bank does not support that amount.
Scenario 4: Simpler customer support
Each payment channel has unique edge cases. Bank transfers can have delays. USSD codes can be entered incorrectly. Mobile money prompts can time out. If your support team is small, starting with fewer channels reduces the variety of issues they need to handle.
Scenario 5: Different channels for different products
A digital product download might use card-only for instant delivery. A physical product with a 2-day shipping window can accept any channel because the delay from bank transfer or USSD does not matter.
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
You can compute the channels list per transaction based on business rules. Here is a pattern:
function getChannelsForTransaction(amount, currency, productType) {
// Start with all channels for the currency
var channelMap = {
NGN: ['card', 'bank_transfer', 'ussd', 'qr', 'apple_pay'],
GHS: ['card', 'mobile_money', 'apple_pay'],
ZAR: ['card', 'apple_pay'],
KES: ['card', 'apple_pay'],
};
var channels = channelMap[currency] || ['card'];
// Digital products: card only for instant delivery
if (productType === 'digital') {
return ['card', 'apple_pay'];
}
// High-value transactions: remove USSD (bank limits)
if (currency === 'NGN' && amount > 10000000) { // Over 100,000 NGN
channels = channels.filter(function(ch) { return ch !== 'ussd'; });
}
// Very low amounts: card and bank transfer only
if (amount < 10000) { // Under 100 NGN
return ['card', 'bank_transfer'];
}
return channels;
}
// Usage in your pay endpoint
app.post('/api/pay', async function(req, res) {
var channels = getChannelsForTransaction(
req.body.amount * 100,
req.body.currency,
req.body.productType
);
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: req.body.email,
amount: req.body.amount * 100,
currency: req.body.currency,
channels: channels,
}),
});
var data = await response.json();
res.json(data.data);
});
If you pass a channel that is not available for the transaction's currency, Paystack silently ignores it. This can lead to a checkout with no payment options if all the channels you specified are unavailable.
// This produces a checkout with no payment options!
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: 'customer@example.com',
amount: 50000,
currency: 'GHS',
channels: ['bank_transfer', 'ussd'], // Neither is available for GHS
}),
});
// Checkout opens but customer has no way to pay
To prevent this, validate channels against the currency before sending to Paystack:
var validChannelsByCurrency = {
NGN: ['card', 'bank', 'ussd', 'bank_transfer', 'qr', 'apple_pay'],
GHS: ['card', 'mobile_money', 'apple_pay'],
ZAR: ['card', 'apple_pay'],
KES: ['card', 'apple_pay'],
USD: ['card', 'apple_pay'],
};
function filterValidChannels(channels, currency) {
var valid = validChannelsByCurrency[currency] || ['card'];
var filtered = channels.filter(function(ch) {
return valid.indexOf(ch) !== -1;
});
// Fallback to card if no valid channels remain
if (filtered.length === 0) {
return ['card'];
}
return filtered;
}
This guarantees the customer always has at least one payment option.
The channels parameter is for Initialize Transaction (the hosted checkout). If you use the Charge API, channel selection is implicit: the channel depends on what payment data you include in the charge request.
bank object with account details: bank transferussd object with the bank type: USSD paymentmobile_money object with the phone number and provider: mobile moneyauthorization_code: card charge on a saved cardWith the Charge API, you control the channel by what data you send, not by a channels array. This gives you full control but requires you to build the payment method selection UI yourself.
For most applications, Initialize Transaction with the channels parameter is the simpler and safer approach. Use the Charge API only when you need a fully custom checkout experience. See Charge API for custom checkout for more detail.
Test each channel configuration in Paystack test mode before deploying to production. Here is what to check:
Paystack test mode simulates different channels. Card payments use test card numbers. Bank transfers use test accounts. Not all channels may be fully testable in test mode (check the Paystack documentation for current test mode capabilities).
After testing, monitor your checkout conversion rate after deploying channel restrictions. If conversion drops, you may have removed a channel that many customers prefer. Analytics on which channels customers use (available in your Paystack dashboard) can guide your decision.
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