Bonaventure OgetoBy Bonaventure Ogeto|

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.

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.

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.

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