Bonaventure OgetoBy Bonaventure Ogeto|

Simulating Declined Cards on Paystack

To simulate a card decline in Paystack test mode: use card 4084080000000409 to trigger "Do Not Honor", or 4084080000000005 to trigger "Insufficient Funds". These cards will fire a charge.failed webhook event. Verify that your webhook handler does not fulfill the order, your frontend shows a useful error message, and your retry flow works.

Test Cards That Trigger Specific Decline Reasons

Card NumberDecline Reasongateway_response
4084080000000409 Declined by issuer Do Not Honor
4084080000000005 Insufficient funds Insufficient Funds
4084080000000007 Card expired (simulate) Expired Card
4084080000000009 Incorrect PIN Incorrect PIN

All test decline cards use CVV 408 and any future expiry date.

Handling the Decline in Your Webhook Handler

// Webhook handler — handle both success and failure events
if (event.event === 'charge.success') {
  await fulfillOrder(event.data.metadata.order_id);
}

if (event.event === 'charge.failed') {
  var orderId = event.data.metadata?.order_id;
  var reason = event.data.gateway_response; // "Do Not Honor", "Insufficient Funds", etc.

  await db.orders.update({
    id: orderId,
    status: 'payment_failed',
    failure_reason: reason,
  });

  // Notify the customer
  await sendEmail(event.data.customer.email, {
    subject: 'Payment failed',
    body: 'Your payment of NGN ' + (event.data.amount / 100) + ' could not be processed. Reason: ' + reason + '. Please try a different card.',
  });
}

Test this by sending the decline test card through your actual checkout flow in test mode, then verifying the webhook fires and your order status updates to payment_failed.

Learn More

Key Takeaways

  • Card 4084080000000409 triggers "Do Not Honor" — the most common real-world decline reason.
  • Card 4084080000000005 triggers "Insufficient Funds" — test that your UI suggests the customer checks their balance.
  • A declined card fires a charge.failed webhook event, not charge.success — your handler must handle both.
  • Never fulfill an order based on the checkout callback alone — always verify via webhook or the verify endpoint.
  • Test your retry flow: can a customer enter a different card after a decline without losing their cart?
  • Log the gateway_response field from decline events — it tells you exactly why the card was declined.

Frequently Asked Questions

Does Paystack send a webhook for declined cards?
Yes. Paystack fires a charge.failed webhook event when a card is declined. Your webhook endpoint receives this event with the same structure as charge.success, but with status: "failed" and a gateway_response explaining the decline reason.
How do I test that my frontend shows the right error message for each decline type?
Use the Paystack Inline popup in test mode with each decline test card. The Paystack popup will display a decline message. Additionally, check that your backend stores the gateway_response and any customer-facing messaging maps it to a helpful instruction (e.g., "Insufficient Funds" → "Please check your account balance or try a different card").
What is the difference between a declined card and a failed charge?
A declined card is when the issuing bank rejects the charge (gateway_response like "Do Not Honor" or "Insufficient Funds"). A failed charge can also result from network issues, expired cards, or Paystack's own risk checks. Both result in a charge.failed webhook event, but the gateway_response field tells you the specific reason.
Should I show the raw gateway_response to customers?
No — raw gateway responses like "Do Not Honor" are technical and unhelpful to customers. Map them to friendly messages: "Do Not Honor" → "Your bank declined this payment. Please try a different card or contact your bank." Keep the raw response in your logs for debugging but show a friendly message to the user.

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