Bonaventure OgetoBy Bonaventure Ogeto|

Paystack Authorization Code Not Reusable

When a Paystack authorization has reusable set to false, the card cannot be charged again using the authorization code. This happens because the card type does not support tokenization (prepaid cards, some virtual cards), the issuing bank does not allow recurring charges, or the customer used a one-time card. Check the reusable field in the authorization object after the first charge. If it is false, do not attempt to charge that authorization code. Instead, ask the customer to pay again through the checkout popup, or offer alternative payment methods.

What "Not Reusable" Means

After a successful Paystack charge, the transaction data includes an authorization object. This object contains the card details and, critically, a reusable field:

// Authorization object from a successful transaction
{
  "authorization_code": "AUTH_abc123def",
  "bin": "408408",
  "last4": "4081",
  "exp_month": "12",
  "exp_year": "2028",
  "channel": "card",
  "card_type": "visa",
  "bank": "GTBank",
  "country_code": "NG",
  "brand": "visa",
  "reusable": false,  // This card CANNOT be charged again
  "signature": "SIG_xyz789",
  "account_name": null
}

When reusable is false, calling /transaction/charge_authorization with this authorization code will fail. Paystack will return an error indicating the authorization cannot be used for recurring charges.

This is different from an expired card or an invalid authorization code. The authorization itself is valid for verification purposes. It just cannot be used to initiate a new charge without the cardholder being present.

Why Some Cards Are Not Reusable

The reusable flag is determined by the card issuer and card type, not by Paystack. Paystack simply reports what the card network tells it.

Prepaid cards. Cards loaded with a fixed amount (like gift cards or prepaid Visa/Mastercards) do not support tokenization. The card issuer blocks recurring charges because the card may not have funds when the next charge comes through.

Virtual cards. Many virtual cards issued by fintech apps (like Chipper Cash, Kuda virtual cards, some Carbon cards) are designed for one-time online purchases. They do not support being stored for future charges.

Bank policy restrictions. Some Nigerian banks restrict recurring charges on certain account tiers. A student savings account debit card might process a one-time payment fine but return reusable: false because the bank does not allow that account type to be set up for recurring debits.

International cards with restrictions. Some international card issuers block cross-border recurring charges. A US Visa card might process a one-time payment to a Nigerian merchant on Paystack but refuse to be tokenized for future charges.

Card network rules. Certain card BINs (the first 6 digits of the card number) are registered as non-reusable at the network level. Paystack cannot override this.

How to Check Before Charging

Always check the reusable field before storing an authorization for future use. Here is the proper flow:

// After a successful charge, check if the card is reusable
async function handleSuccessfulCharge(transactionReference: string) {
  const res = await fetch(
    `https://api.paystack.co/transaction/verify/${transactionReference}`,
    {
      headers: {
        Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
      },
    }
  );

  const data = await res.json();

  if (data.data.status !== 'success') {
    throw new Error('Transaction was not successful');
  }

  const authorization = data.data.authorization;

  if (authorization.reusable) {
    // Safe to store for future charges
    await saveAuthorization({
      customerId: data.data.customer.id,
      authorizationCode: authorization.authorization_code,
      last4: authorization.last4,
      expMonth: authorization.exp_month,
      expYear: authorization.exp_year,
      bank: authorization.bank,
      cardType: authorization.card_type,
      reusable: true,
    });

    return { canChargeAgain: true };
  } else {
    // Do NOT store for recurring charges
    console.log(
      'Card ending in ' + authorization.last4 + ' from ' + authorization.bank + ' is not reusable. ' +
      'Will not store for future charges.'
    );

    return { canChargeAgain: false };
  }
}

If you have already stored non-reusable authorizations, clean them up. Query your database for authorizations where reusable = false and either delete them or mark them as inactive.

What Happens If You Charge a Non-Reusable Authorization

If you ignore the reusable flag and call /transaction/charge_authorization anyway, Paystack returns an error:

// Attempting to charge a non-reusable authorization
const res = await fetch('https://api.paystack.co/transaction/charge_authorization', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    authorization_code: 'AUTH_abc123def',
    email: 'customer@example.com',
    amount: 250000,
  }),
});

// Response:
{
  "status": false,
  "message": "Authorization is not reusable"
}

This does not charge the customer. No money moves. But it clutters your logs with failed charge attempts and, worse, it might trigger your system to mark the customer as having a failed payment when the real issue is a card type problem.

If you are running a subscription service, these failed charges create a cascade: the subscription renewal fails, the customer gets a "payment failed" email, the customer contacts support confused because their card "worked fine last month" (it did, for the one-time charge), and your support team has to explain a technical limitation that should have been handled in code.

Alternative Approaches for Non-Reusable Cards

When a customer's card is not reusable, you have several options depending on your business model:

1. Redirect to checkout each time

Instead of charging the authorization code, redirect the customer to the Paystack checkout popup for each payment. The customer enters their card details each time. This works for all card types.

// For non-reusable cards, always use the checkout flow
async function chargeCustomer(customerId: string, amount: number) {
  const customer = await getCustomer(customerId);
  const savedAuth = await getSavedAuthorization(customerId);

  if (savedAuth && savedAuth.reusable) {
    // Charge using saved authorization
    return chargeAuthorization(savedAuth.authorizationCode, customer.email, amount);
  }

  // No reusable authorization: initialize a new transaction
  // and redirect the customer to checkout
  const res = 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.email,
      amount,
      callback_url: 'https://yoursite.com/payment/callback',
      metadata: {
        custom_fields: [
          { display_name: 'Customer ID', variable_name: 'customer_id', value: customerId },
        ],
      },
    }),
  });

  const data = await res.json();
  return { redirect: true, url: data.data.authorization_url };
}

2. Offer alternative payment channels

Mobile money (M-Pesa), bank transfers, and USSD do not rely on card tokenization. For subscription services in Kenya, M-Pesa is often more reliable than card for recurring payments because M-Pesa does not have the reusable restriction.

3. Ask the customer to use a different card

Politely let the customer know that their current card does not support automatic renewals. Suggest using a different card (a regular debit or credit card from a major bank rather than a prepaid or virtual card).

Communicating With the Customer

Customers do not understand payment tokenization. They do not know what "reusable" means. They just know their card worked once and now it does not. Your messaging needs to translate the technical problem into plain language.

Good messages:

  • "Your card ending in 4081 does not support automatic payments. You will need to enter your card details each time you make a payment."
  • "For automatic subscription renewals, please use a regular bank debit card. Prepaid and virtual cards cannot be saved for future payments."
  • "We could not set up automatic billing with your current card. You can either pay manually each month or add a different card that supports recurring payments."

Bad messages:

  • "Authorization code not reusable" (technical jargon)
  • "Your card was declined" (inaccurate, it was not declined)
  • "Payment method failed" (vague and alarming)
// Email template for non-reusable card notification
function getNonReusableCardEmail(customerName: string, last4: string) {
  return {
    subject: 'Action needed: Update your payment method',
    body: `Hi ${customerName},

Your card ending in ${last4} does not support automatic payments.
This means we cannot charge it automatically for your subscription renewals.

You have two options:

1. Pay manually each month when we send you a payment link
2. Add a different card that supports automatic payments
   (most regular bank debit and credit cards work)

To update your payment method, visit:
https://yoursite.com/account/billing

If you have any questions, reply to this email.

Best,
The Payments Team`,
  };
}

Handling Non-Reusable Cards in Subscription Systems

If you are building a subscription system on Paystack, non-reusable cards are a recurring (pun intended) problem. Here is how to architect your subscription billing to handle them gracefully:

// Subscription billing with reusable card check
async function processSubscriptionRenewal(subscriptionId: string) {
  const subscription = await getSubscription(subscriptionId);
  const authorization = await getSavedAuthorization(subscription.customerId);

  // Case 1: No saved authorization
  if (!authorization) {
    await sendPaymentLink(subscription);
    return { status: 'payment_link_sent' };
  }

  // Case 2: Authorization exists but is not reusable
  if (!authorization.reusable) {
    await sendPaymentLink(subscription);
    await notifyCardNotReusable(subscription.customerId, authorization.last4);
    return { status: 'payment_link_sent', reason: 'card_not_reusable' };
  }

  // Case 3: Authorization exists and is reusable
  try {
    const charge = await chargeAuthorization(
      authorization.authorizationCode,
      subscription.customerEmail,
      subscription.amount
    );

    if (charge.data.status === 'success') {
      await extendSubscription(subscriptionId, subscription.interval);
      return { status: 'charged', reference: charge.data.reference };
    }

    // Charge failed for other reasons (expired card, insufficient funds)
    await sendPaymentLink(subscription);
    return { status: 'payment_link_sent', reason: 'charge_failed' };
  } catch (err) {
    await sendPaymentLink(subscription);
    return { status: 'payment_link_sent', reason: 'charge_error' };
  }
}

async function sendPaymentLink(subscription: any) {
  const res = 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: subscription.customerEmail,
      amount: subscription.amount,
      callback_url: `https://yoursite.com/subscription/${subscription.id}/renew-callback`,
    }),
  });

  const data = await res.json();
  await sendRenewalEmail(subscription.customerEmail, data.data.authorization_url);
}

The payment link fallback ensures every customer can still pay, even if their card is not reusable. The customer experience is slightly worse (they have to click a link and enter details) but the alternative is a failed renewal and a churned customer.

Verification: Confirm Your Handling Works

Test each scenario before going live:

  1. Check reusable detection. Make a test payment with a test card. Verify that your code reads the reusable field from the authorization object and stores it correctly. In test mode, Paystack test cards typically return reusable: true, so you may need to manually test the false path by mocking the response.
  2. Test the fallback flow. Set a saved authorization's reusable field to false in your database. Trigger a subscription renewal. Confirm that your system sends a payment link instead of trying to charge the authorization.
  3. Test customer messaging. Trigger the non-reusable card notification. Confirm the email is clear, contains the last 4 digits, and includes a link to update payment methods.
  4. Test the charge_authorization error. If you want to see the actual Paystack error, attempt to charge a non-reusable authorization in test mode. Log the full response for your error handling code.
  5. Review your database. Query for all stored authorizations. Check how many have reusable: false. If you have been storing non-reusable authorizations, clean them up.

For a complete guide to subscription billing with Paystack, including handling all card failure scenarios, see the Paystack Subscription Not Charging troubleshooting guide.

Key Takeaways

  • The reusable field in a Paystack authorization object tells you whether the card can be charged again without customer interaction. If reusable is false, any attempt to call /transaction/charge_authorization will fail.
  • Prepaid cards and most virtual cards return reusable: false because they do not support tokenization. This is a card issuer restriction, not a Paystack limitation.
  • Some Nigerian bank debit cards return reusable: false because the issuing bank blocks recurring charges on certain card tiers. This is common with student accounts and basic savings accounts.
  • Always check the reusable field after the first successful charge before storing the authorization code for future use. Storing a non-reusable authorization wastes database space and causes failed charges later.
  • When a customer has a non-reusable card, redirect them to the Paystack checkout popup for each payment instead of trying to charge the authorization. The popup handles authentication properly each time.
  • For subscription businesses, communicate clearly which card types are supported. Offer M-Pesa, bank transfer, or USSD as alternatives for customers whose cards are not reusable.

Frequently Asked Questions

Why does Paystack return reusable: false for my card?
The reusable field is determined by the card issuer, not by Paystack. Prepaid cards, virtual cards, and cards from banks that restrict recurring charges return reusable: false. This is a card-level restriction that Paystack cannot override. Common examples include gift cards, some fintech app virtual cards, and student account debit cards.
Can I make a non-reusable authorization become reusable?
No. The reusable flag is set by the card issuer at the time of the first charge. You cannot change it. If a customer needs recurring billing, they must use a different card that supports tokenization, or you must use the checkout flow for each payment.
Does reusable: false mean the first charge failed?
No. The first charge can succeed perfectly while the authorization is not reusable. The card processed the one-time payment fine. It just cannot be charged again without the cardholder going through the checkout flow. The first charge and the reusability of the authorization are independent.
How do I know which card types are reusable before the customer pays?
You cannot know for certain until after the first charge. The reusable flag is only available in the authorization object returned after a successful transaction. You can make educated guesses (prepaid cards are usually not reusable) but you cannot guarantee it before the charge. Design your system to handle both cases.
Does Paystack Subscriptions API handle non-reusable cards automatically?
Paystack managed subscriptions require a reusable authorization. If the customer subscribes with a non-reusable card, the first charge succeeds but subsequent renewal attempts fail. Paystack sends a subscription.not_renew webhook. You need to handle this by prompting the customer to update their payment method.

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