McTaba Labs logo
Software EngineeringThe WhiteboardProjectsEventsMasterclassesWebinarsLearn
Book Call

500+ developers already in the community

Get answers, find accountability partners, and stay ahead

Join Discord →
McTaba Labs logo

Educating 1 million students in tech by 2030.

Programs

  • HighSchool Graduates Launchpad
  • AI & Business Automation
  • Corporate Training
  • McTaba Story & Mission
  • Full-Stack Curriculum
  • Course Pricing & Plans
  • Frequently Asked Questions

Resources

  • Join Discord Community
  • Tech Events in Nairobi
  • Masterclasses
  • Free Resources
  • Learn
  • Campus
  • Africa Tech Talent Report
  • Partner With Us
  • Start a Project
  • Our Work

Contact Us

  • 124 Manyani East Road, Lavington, Nairobi
  • +254727795603
  • Book a Discovery Call

Office Hours:

Mon-Fri: 9am - 2pm EAT

Sat: 10am - 12pm EAT

© 2027 McTaba Labs - A product of Edtech Industries. All rights reserved.

Privacy PolicyTerms of ServiceRefund Policy
  1. Learn
  2. Paystack for African Developers
  3. Restricting Payment Channels at Checkout in Paystack
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 20, 2026

Restricting Payment Channels at Checkout in Paystack

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.

In this article

  1. 1. How the Channels Parameter Works
  2. 2. When to Restrict Channels
  3. 3. Dynamic Channel Selection
  4. 4. Channel and Currency Compatibility
  5. 5. Channels in the Charge API
  6. 6. Testing Channel Restrictions
  7. 7. Stay Up to Date

How the Channels Parameter Works

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, Verve
  • bank - Pay with bank (different from bank_transfer)
  • ussd - USSD banking codes
  • mobile_money - MTN MoMo, AirtelTigo, Vodafone Cash
  • bank_transfer - Transfer to a temporary bank account
  • qr - QR code scanning
  • apple_pay - Apple Pay on supported devices

Note 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.

When to Restrict Channels

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.

Dynamic Channel Selection

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);
});

Channel and Currency Compatibility

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.

Channels in the Charge API

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.

  • Send a bank object with account details: bank transfer
  • Send a ussd object with the bank type: USSD payment
  • Send a mobile_money object with the phone number and provider: mobile money
  • Send an authorization_code: card charge on a saved card

With 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.

Testing Channel Restrictions

Test each channel configuration in Paystack test mode before deploying to production. Here is what to check:

  1. Initialize a transaction with your restricted channels and open the checkout page
  2. Verify that only the channels you specified appear
  3. Complete a test payment on each channel to confirm it works end to end
  4. Test with invalid channel combinations (like mobile_money for NGN) to see how the checkout behaves
  5. Test with an empty channels array to understand the error behavior

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.

Stay Up to Date

Paystack adds new payment channels and updates channel availability periodically. We keep these guides current.

Sign up for the McTaba newsletter to get notified about new Paystack payment channels and features.

Key Takeaways

  • ✓Pass a channels array to /transaction/initialize to control which payment methods appear on the checkout page.
  • ✓Valid values: "card", "bank", "ussd", "mobile_money", "bank_transfer", "qr", "apple_pay". Only channels available for the transaction currency will actually appear.
  • ✓Restricting to card-only gives you instant payment confirmation. Bank transfer, USSD, and mobile money can take minutes.
  • ✓You can vary channels per transaction. High-value orders might offer card and bank transfer. Low-value orders might include USSD.
  • ✓If you pass a channel that is not available for the currency, it is silently ignored. If all passed channels are unavailable, the checkout has no payment options.
  • ✓Start with all channels (omit the parameter) and restrict only when you have a specific reason. More options means more customers can pay.

Frequently Asked Questions

What happens if I pass an empty channels array?
If you pass an empty array, the checkout may show no payment options or Paystack may return all default channels. The behavior is not well-documented, so it is safest to either omit the parameter (to show all channels) or pass at least one valid channel.
Can I restrict channels in the Paystack dashboard instead of the API?
The Paystack dashboard allows you to enable or disable certain payment channels globally for your account. The channels parameter in the API lets you further restrict per transaction. The two work together: a channel must be enabled on your account AND included in the channels parameter to appear at checkout.
Does restricting channels affect transaction fees?
No. The transaction fee is based on the channel the customer actually uses to pay, not on which channels you offered. Restricting channels does not change the fee structure.
Can I add channels to a transaction after initialization?
No. The channels are set at initialization time and cannot be changed for an existing transaction. If you need different channels, initialize a new transaction (you can use the same reference if the first was not completed).
Should I always restrict channels or leave the default?
Leave the default (all available channels) unless you have a specific reason to restrict. More payment options means more customers can pay using their preferred method. Only restrict when instant confirmation, amount limits, or support capacity require it.

Learn Paystack Integration

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

Not ready? Create Free Account

Keep reading

Accepting Payments with Paystack: Complete Guide

Everything you need to accept your first payment with Paystack: choosing between Initialize Transaction and the Charge API, handling amounts in kobo, attaching metadata, setting callback URLs, and designing transaction references that keep your system clean.

Read more

Using Paystack Custom Fields to Collect Extra Checkout Information

Paystack custom fields let you attach structured, human-readable data to a transaction that shows up on the checkout page, in your dashboard, and in webhook payloads. This guide covers display_name vs variable_name, common field patterns, and how to extract custom field data when the webhook arrives.

Read more

Paystack Customer Objects: Create, Fetch, Update, Whitelist

Paystack creates a customer object every time a new email hits your checkout. You can also create customers explicitly, update their details, fetch their transaction history, and whitelist or blacklist them for risk control. This guide covers every operation with working Node.js code.

Read more

Building a Wallet Top-Up Flow on Paystack

A step-by-step guide to building wallet top-up flows with Paystack. Initialize payment, handle webhooks, credit wallets safely, enforce minimum amounts, and prevent double-crediting in production.

Read more

Paystack Webhooks and CSRF Protection in Rails

Rails rejects Paystack webhook POSTs with an ActionController::InvalidAuthenticityToken error because they lack a CSRF token. This guide shows how to skip CSRF verification for your webhook controller while keeping protection active everywhere else, plus the complete HMAC signature verification setup in Ruby.

Read more

Explore related topics

M-Pesa & African Stack

M-Pesa Daraja API tutorials, USSD development, and WhatsApp Business API guides. The African Stack that global bootcamps do not teach.

Learn to Code in Kenya

Everything a Kenyan needs to know before writing their first line of code. Career paths, real costs, hardware requirements, and honest answers about what it takes.

Learning to Code

Roadmaps, language guides, and practical advice for learning to code and launching a programming career.

Breaking Into Tech

Practical, no-nonsense guides for anyone looking to start or change their career in tech. From choosing your first language to acing interviews.

The African Stack: Skills for This Market

The skills that make Kenyan developers hard to replace: M-Pesa, USSD, WhatsApp, fintech, and building for the African market.

Bonaventure Ogeto

Bonaventure Ogeto

Founder, McTaba Labs

Bonaventure is the founder of McTaba Labs, a coding bootcamp in Nairobi, Kenya. He has spent years building products for the African market and mentoring the next generation of developers across the continent.