Bonaventure OgetoBy Bonaventure Ogeto|

Authorization Codes: How Paystack Recurring Billing Really Works

A Paystack authorization code is a token that represents a customer's saved payment method. It is created after the customer's first successful card payment. Paystack tokenizes the card details and returns an authorization_code string that you can use to charge the card again without the customer re-entering their information. Both the managed Subscriptions API and the manual charge_authorization endpoint use authorization codes under the hood.

What Is an Authorization Code

An authorization code is a string like AUTH_xxxxxxxxxxxxx. It represents a customer's card details stored securely on Paystack's servers. When you have this code, you can charge the customer's card without them entering their card number, expiry, or CVV again.

Think of it as a key to a vault. The vault (Paystack's token storage) holds the actual card details. You hold the key (the authorization code). When you want to charge the card, you hand the key to Paystack, and Paystack opens the vault, retrieves the card details, and processes the charge with the card network and issuing bank.

This is how every recurring billing system works, not just Paystack. Stripe calls them "payment methods." Flutterwave calls them "tokens." The concept is the same: a reference to stored card details that lets you charge without collecting card information again.

This article is part of the subscriptions and recurring billing guide.

How Authorization Codes Are Created

Authorization codes are created as a side effect of a successful first payment. The flow:

  1. You initialize a transaction using POST /transaction/initialize. The customer is redirected to Paystack's checkout page (or you use the inline popup).
  2. The customer enters their card details on Paystack's secure form. Your server never touches the raw card data.
  3. Paystack processes the charge with the card network (Visa, Mastercard, Verve) and the issuing bank.
  4. If the charge succeeds, Paystack tokenizes the card details and creates an authorization.
  5. The authorization is included in two places: the charge.success webhook payload and the response from GET /transaction/verify/:reference.

Extracting the authorization from a verified transaction:

const axios = require('axios');

async function verifyAndExtractAuth(reference) {
  var response = await axios.get(
    'https://api.paystack.co/transaction/verify/' + reference,
    {
      headers: {
        Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
      },
    }
  );

  var data = response.data.data;

  if (data.status !== 'success') {
    console.log('Transaction not successful');
    return null;
  }

  var auth = data.authorization;
  console.log('Authorization code:', auth.authorization_code);
  console.log('Reusable:', auth.reusable);
  console.log('Card:', auth.card_type + ' ending ' + auth.last4);
  console.log('Expires:', auth.exp_month + '/' + auth.exp_year);

  return auth;
}

The same authorization object appears in the charge.success webhook:

app.post('/webhooks/paystack', function(req, res) {
  // Verify webhook signature first (always!)
  var event = req.body;

  if (event.event === 'charge.success') {
    var auth = event.data.authorization;

    if (auth.reusable) {
      // Store this authorization for future charges
      saveAuthorization({
        customerEmail: event.data.customer.email,
        authorizationCode: auth.authorization_code,
        last4: auth.last4,
        cardType: auth.card_type,
        expMonth: auth.exp_month,
        expYear: auth.exp_year,
        bank: auth.bank,
      });
    }
  }

  res.sendStatus(200);
});

Important: always verify the transaction via the API before trusting the webhook data. The webhook tells you something happened. The verification call confirms it. See verify transaction for why this matters.

Reusable vs Non-Reusable Authorizations

Not every authorization can be used for recurring charges. The reusable field tells you which ones can.

Reusable (reusable: true): Card payments almost always produce reusable authorizations. You can call charge_authorization with these codes as many times as you need. The customer does not need to re-authenticate.

Non-reusable (reusable: false): Bank transfer and USSD payments typically produce non-reusable authorizations. These are one-time tokens. You cannot charge them again. They exist in the authorization object for record-keeping, but calling charge_authorization with them will fail.

This distinction matters because your application needs to handle both cases. If a customer's first payment was via bank transfer, you have their money but you do not have a reusable authorization. You cannot set up recurring billing for them until they make a card payment.

function canSetupRecurring(authorization) {
  if (!authorization.reusable) {
    console.log('This payment method cannot be used for recurring charges.');
    console.log('Channel:', authorization.channel);
    console.log('The customer needs to pay with a card to enable recurring billing.');
    return false;
  }
  return true;
}

// When processing a successful payment
function handleSuccessfulPayment(transactionData) {
  var auth = transactionData.authorization;

  if (canSetupRecurring(auth)) {
    // Safe to store for recurring billing
    storeAuthorizationForRecurring(transactionData.customer.email, auth);
  } else {
    // Grant access for this payment only
    // Prompt the customer to add a card for recurring billing
    promptForCardPayment(transactionData.customer.email);
  }
}

For a complete treatment of this topic, including mobile money and country-specific behavior, see reusable vs non-reusable authorizations.

How the Subscriptions API Uses Authorizations Internally

When you use Paystack's managed Subscriptions API, you might think you are dealing with a completely different system. You are not. Under the hood, the Subscriptions API is just an automated wrapper around authorization codes.

Here is what happens on a subscription renewal:

  1. Paystack checks the subscription's next_payment_date.
  2. When the date arrives, Paystack creates an Invoice.
  3. Paystack calls charge_authorization internally, using the authorization code linked to the subscription.
  4. If the charge succeeds, the invoice is marked paid, and the next_payment_date is bumped forward by one interval.
  5. If the charge fails, Paystack retries according to its internal retry schedule and sends you invoice.payment_failed events.

This means everything you learn about authorization codes applies to the Subscriptions API. The same card expiry issues, the same bank decline reasons, the same reusable vs non-reusable distinction. The Subscriptions API just automates the scheduling and retry logic.

When you create a subscription using the transaction/initialize flow (passing a plan parameter), Paystack handles everything: the first charge creates the authorization, then the subscription is created and linked to that authorization automatically.

When you create a subscription via the subscription API directly (passing a customer and authorization), you are telling Paystack which specific authorization code to use for future renewals.

// Method 1: Subscription created during checkout
// Paystack creates authorization + subscription automatically
async function subscribeViaCheckout(email, planCode) {
  var response = await axios.post(
    'https://api.paystack.co/transaction/initialize',
    {
      email: email,
      amount: 500000,
      plan: planCode,
    },
    {
      headers: {
        Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
        'Content-Type': 'application/json',
      },
    }
  );
  return response.data.data.authorization_url;
}

// Method 2: Subscription created with existing authorization
// You choose which card to charge
async function subscribeWithAuth(customerEmail, planCode, authCode) {
  var response = await axios.post(
    'https://api.paystack.co/subscription',
    {
      customer: customerEmail,
      plan: planCode,
      authorization: authCode,
    },
    {
      headers: {
        Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
        'Content-Type': 'application/json',
      },
    }
  );
  return response.data.data;
}

Authorization Code Lifecycle

Understanding when an authorization code starts working, when it stops, and what can go wrong in between.

Creation

An authorization code is born when a customer makes a successful card payment on your Paystack account. One payment, one authorization. If the customer pays with the same card again, they may get a new authorization code, but both will have the same signature field (identifying the physical card).

Active life

An authorization code does not have a built-in expiry date. It remains valid as long as the underlying card is valid. A card issued in 2026 with a 2030 expiry will produce an authorization that works until 2030 (or until the bank revokes the card for other reasons).

Failure modes

An authorization code can stop working for several reasons:

  • Card expired: The most common reason. Track exp_month and exp_year and prompt the customer before expiry.
  • Card cancelled by customer: The customer called their bank and cancelled the card. The authorization code becomes unusable.
  • Card blocked by bank: The issuing bank blocked the card for fraud or other reasons.
  • Insufficient funds: Not a permanent failure. The card is valid but the account does not have enough money. Retry later.
  • Do Not Honor: A catch-all decline from the bank. Can be temporary or permanent.

Death

Authorization codes do not "die" in Paystack's system. Even if the underlying card expires, the authorization code still exists. It just fails when you try to charge it. Paystack does not proactively notify you when an authorization becomes unusable. You find out when the charge fails.

This is why proactive card expiry monitoring is important. See handling card expiry for the implementation.

One Customer, Many Authorizations

A customer can accumulate multiple authorization codes over time. Each successful card payment creates one. If the customer pays with three different cards, they have three authorizations.

Managing this in your application:

async function getActiveAuthorizations(customerCode) {
  var response = await axios.get(
    'https://api.paystack.co/customer/' + customerCode,
    {
      headers: {
        Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
      },
    }
  );

  var auths = response.data.data.authorizations;

  // Filter to reusable, non-expired authorizations
  var now = new Date();
  var currentMonth = now.getMonth() + 1;
  var currentYear = now.getFullYear();

  var active = auths.filter(function(auth) {
    if (!auth.reusable) return false;

    var expYear = parseInt(auth.exp_year);
    var expMonth = parseInt(auth.exp_month);

    // Card expires at the end of exp_month
    if (expYear < currentYear) return false;
    if (expYear === currentYear && expMonth < currentMonth) return false;

    return true;
  });

  return active;
}

// Let the customer choose which card to use
function displayCardOptions(authorizations) {
  authorizations.forEach(function(auth, index) {
    console.log(
      (index + 1) + '. ' + auth.card_type + ' ending in ' +
      auth.last4 + ' (expires ' + auth.exp_month + '/' + auth.exp_year + ')'
    );
  });
}

Best practices for multi-authorization management:

  • Mark one authorization as the customer's "default" payment method in your database.
  • When creating a subscription, use the default authorization unless the customer chooses otherwise.
  • Use the signature field to deduplicate. If two authorizations have the same signature, they represent the same physical card.
  • Periodically clean up expired authorizations from your display. Do not show a customer a card that expired in 2025.

Security Considerations

An authorization code is not a card number, but it can be used to charge real money. Treat it with the same seriousness you would treat any payment credential.

What authorization codes can do

  • Charge the customer's card for any amount, at any time, without their interaction.
  • Create subscriptions on behalf of the customer.

What authorization codes cannot do

  • Reveal the full card number. You only ever see bin (first 6 digits) and last4.
  • Be used on a different Paystack merchant account. The authorization is scoped to your integration.
  • Be used without your Paystack secret key. The charge_authorization call requires authentication.

Storage guidelines

  • Store authorization codes in your database, not in localStorage, cookies, or frontend code.
  • Encrypt the authorization_code column at rest. It is not PCI-regulated like raw card numbers, but it is still a payment credential.
  • Restrict database access to authorization codes. Not every developer on your team needs to see them.
  • Log charge attempts but do not log the full authorization code. Use the last4 for debugging.

For a complete guide on secure storage patterns, see storing authorization codes securely.

When to Use Authorization Codes Directly vs the Subscriptions API

You now understand that both paths use authorization codes. The question is which path gives you the control you need.

Use the Subscriptions API when:

  • The amount is the same every billing cycle.
  • The interval is standard (daily, weekly, monthly, quarterly, annually).
  • You want Paystack to handle scheduling and retries.
  • You are building a straightforward SaaS product with fixed pricing tiers.

Use charge_authorization directly when:

  • The amount varies each cycle (usage-based billing, instalment plans with different amounts).
  • The schedule is irregular (school term dates, loan repayment dates that shift).
  • You need complex retry logic beyond what Paystack provides.
  • You need to charge different amounts to the same customer in the same cycle (base fee plus overage).

Many production systems use both. A SaaS product might use the Subscriptions API for the base plan and charge_authorization for overage charges. A lending platform uses charge_authorization exclusively because every borrower has a different repayment schedule and amount.

For a detailed comparison with code examples for each approach, see subscriptions API vs manual recurring charges.

Key Takeaways

  • Authorization codes are tokens created after a customer's first successful card payment. They represent the saved payment method.
  • The tokenization happens on Paystack's side. You never see or handle raw card numbers. You only work with the authorization_code string.
  • Only authorizations marked reusable: true can be used for recurring charges. Card payments produce reusable authorizations; bank transfers and USSD typically do not.
  • Authorization codes do not expire on a fixed schedule. They remain valid as long as the underlying card is valid.
  • The Subscriptions API uses authorization codes internally. When Paystack renews a subscription, it charges the authorization linked to that subscription.
  • With charge_authorization, you control the schedule, the amount, and the retry logic. Paystack just executes the charge using the stored token.

Frequently Asked Questions

Do Paystack authorization codes expire?
Authorization codes themselves do not have a fixed expiry date in Paystack's system. They remain usable as long as the underlying card is valid. When the card expires, is cancelled, or is blocked by the bank, charges against the authorization code will fail. Track the exp_month and exp_year fields to anticipate card expiry.
Can I use an authorization code from a bank transfer for recurring billing?
No. Bank transfer payments produce authorizations with reusable: false. You cannot use them for recurring charges via charge_authorization or for creating subscriptions. The customer needs to make a card payment to generate a reusable authorization.
Can someone use my authorization code on a different Paystack account?
No. Authorization codes are scoped to your Paystack integration (your merchant account). They cannot be used by other merchants, even if someone obtains the authorization code string. The charge_authorization call also requires your secret key.
How many authorization codes can one customer have?
There is no practical limit. Each successful card payment creates a new authorization. If a customer pays with five different cards, they have five authorization codes. Use the signature field to identify when two authorizations represent the same physical card.
What is the signature field on an authorization?
The signature is a unique identifier for the physical card. If a customer pays with the same card multiple times, each payment creates a new authorization code, but all of them share the same signature. Use this to deduplicate and avoid showing the same card twice in your UI.

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