Common Paystack and Flutterwave API Errors (And How to Fix Them)
The most common Paystack and Flutterwave API errors fall into five categories: authentication failures (wrong or missing API keys), amount format errors (sending naira instead of kobo for Paystack or vice versa for Flutterwave), webhook configuration issues (wrong URL, missing signature verification, or server not responding), card payment declines (insufficient funds, expired card, wrong OTP), and network-related failures (timeouts, DNS issues, SSL problems). Most of these are preventable with proper setup and testing in the sandbox environment before going live.
Authentication Errors (401 and 403)
Authentication errors are the first wall most developers hit. They are also the easiest to fix once you understand the key system.
"Invalid key" or "Unauthorized" (HTTP 401)
Cause: You are sending a request with an invalid or missing API key. Common scenarios:
- You copied the key incorrectly (trailing space, missing character, extra character).
- You are using a test key against the live API endpoint or a live key against the test endpoint.
- You are sending the public key (pk_...) where the secret key (sk_...) is required. Server-side API calls require the secret key. The public key is only for the client-side inline checkout.
- Your key was revoked or regenerated on the dashboard, but your code still uses the old one.
Fix: Copy your keys fresh from the Paystack or Flutterwave dashboard. Verify the prefix: sk_test_ or sk_live_ for Paystack secret keys, FLWSECK_TEST- or FLWSECK- for Flutterwave secret keys. Store them in environment variables and ensure your application is reading from the correct variable. Log (but never expose publicly) the first 10 characters of the key your app is using to confirm it matches what the dashboard shows.
"Forbidden" (HTTP 403)
Cause: Your account does not have permission for the endpoint you are calling. This happens when:
- You try to use a live-only feature (like transfers or payouts) from a test key.
- Your Paystack or Flutterwave account has not completed KYC verification, and you are trying to use a feature that requires it.
- Your account has been restricted by the provider (usually for compliance reasons).
Fix: Check your account status on the dashboard. Complete KYC verification if pending. For transfers and payouts, ensure your account has been approved for disbursement by the provider.
Amount Format Errors
This is the single most common integration bug, and it trips up experienced developers too.
Paystack: "Amount must be at least 100" or customer charged 100x the intended amount
Paystack expects amounts in the lowest currency denomination. For Nigeria, that is kobo. NGN 1 = 100 kobo. So if you want to charge NGN 5,000, you send 500000 (five hundred thousand kobo), not 5000.
If you send 5000, Paystack interprets that as 5000 kobo = NGN 50. Your customer pays NGN 50 instead of NGN 5,000. If you accidentally multiply twice, your customer gets charged NGN 500,000 instead of NGN 5,000. Both scenarios cause real problems.
Flutterwave: Wrong amount charged
Flutterwave expects amounts in the main currency unit. NGN 5,000 is sent as 5000. If you apply the kobo conversion thinking Flutterwave works like Paystack, you charge the customer 100x more than intended.
Prevention: Create a helper function that converts your internal price representation to the correct format for each gateway. Test with small amounts (NGN 100) in sandbox mode before processing any real transactions. Log the amount sent to the API and the amount the customer sees, and compare them during testing.
"Amount must be a positive integer"
Both gateways require integer amounts. If you pass a float (like 500000.50 for Paystack or 5000.50 for Flutterwave), the API will reject it. Round to the nearest integer. For prices that include kobo fractions, round up to avoid underpayment.
Minimum amount errors
Paystack has a minimum transaction amount of NGN 50 (5000 kobo). Flutterwave also has minimums that vary by payment method. If you try to charge less than the minimum, the API returns an error. This matters for testing: use at least NGN 100 for test transactions to avoid hitting minimums.
Webhook Errors and Missed Notifications
Webhook issues cause the most damage in production because they are silent failures. Your customer pays, but your system never finds out.
"Webhook URL not reachable"
Cause: The URL you set in the dashboard is not publicly accessible. This happens when:
- Your server is behind a firewall that blocks incoming POST requests from external sources.
- Your webhook URL points to localhost (which the payment gateway cannot reach).
- Your domain's DNS is not resolving correctly.
- Your SSL certificate is invalid or expired (both gateways require HTTPS for webhooks).
Fix: Verify your webhook URL is publicly accessible by making a POST request to it from an external tool like Postman or curl from a different machine. Ensure HTTPS is working with a valid certificate.
Webhook arrives but your server returns an error
Both Paystack and Flutterwave expect your webhook endpoint to return HTTP 200 within a few seconds. If your endpoint returns a 500 error, times out, or takes too long to respond, the gateway may retry (potentially causing duplicate processing) or eventually give up.
Fix: Respond with HTTP 200 immediately upon receiving the webhook. Process the payload asynchronously. Do your database updates, email sending, and order fulfillment after you have already responded to the gateway.
Webhook signature verification fails
Paystack signs webhooks using HMAC SHA-512 with your secret key. The signature is in the x-paystack-signature header. Flutterwave uses a secret hash that you set in your dashboard, sent in the verif-hash header. If your verification logic is wrong (using the wrong key, wrong hashing algorithm, or comparing incorrectly), you will reject legitimate webhooks.
Fix: Copy the signature verification code from the official documentation exactly. Use your secret key, not your public key. For Paystack, compute HMAC SHA-512 of the raw request body (not the parsed JSON) and compare it to the header value.
Missing webhooks for bank transfers
Card payments can work without webhooks because the redirect callback tells you the payment completed. Bank transfers and USSD payments rely entirely on webhooks for confirmation. If your webhooks are not working, card payments appear fine but bank transfers stay "pending" forever. This is why testing bank transfer webhooks specifically is critical before going live.
Card Payment Declines and Customer-Side Errors
Card payment failures are often not bugs in your code. They are customer-side issues. But you need to handle them gracefully.
Common card decline reasons:
- Insufficient funds: The customer's account balance is below the transaction amount. Paystack returns this as a transaction failure. Show the customer a clear message: "Payment declined. Please check your account balance and try again, or use bank transfer."
- Card not enabled for online payments: Some Nigerian debit cards (especially older Verve cards) are not enabled for online transactions by default. The customer needs to visit their bank or use their banking app to enable online transactions.
- Wrong OTP: Nigerian card payments require 3D Secure OTP verification. If the customer enters the wrong OTP, the transaction fails. They need to retry and enter the correct OTP from their SMS.
- OTP not received: The bank's SMS system can be slow, especially during peak hours. The customer waits for an OTP that arrives too late, and the transaction times out. This is a bank infrastructure issue, not something you can fix. Offer bank transfer as an alternative.
- Expired card: The card's expiry date has passed. The customer needs to get a new card from their bank.
- Card blocked: The bank has blocked the card due to suspected fraud, missed payments, or the customer's request. Only the bank can resolve this.
How to handle card declines in your UI:
- Show a clear, specific error message. "Payment declined" is not enough. If the gateway returns a reason (like "insufficient funds"), display it.
- Offer alternatives. "Your card payment was declined. You can try again with a different card, or pay via bank transfer." Having bank transfer as a fallback catches customers whose cards have issues.
- Do not force the customer to re-enter their order details. Preserve the cart and order state so they can retry payment without starting over.
- Log the decline reason for your own analysis. If you see a pattern (e.g., many declines from a specific bank), it may indicate a bank-side issue worth monitoring.
Network and Infrastructure Errors
Network errors happen between your server and the payment gateway. They are often intermittent and can be difficult to debug.
Timeout errors
Your request to the Paystack or Flutterwave API takes too long and your HTTP client times out. Causes include slow network between your server and the gateway, gateway processing delays during high traffic, or your server's DNS resolution being slow.
Fix: Set reasonable timeouts (15 to 30 seconds for payment initialization, longer for verification). Implement retry logic with exponential backoff for verification requests (but never retry a payment initialization, as this could create duplicate charges). Use a reliable hosting provider with good network connectivity to Nigerian infrastructure.
SSL/TLS errors
"SSL certificate problem" or "UNABLE_TO_VERIFY_LEAF_SIGNATURE" errors mean your server cannot verify the payment gateway's SSL certificate. This can happen when your server's CA certificate bundle is outdated or when a proxy is intercepting HTTPS traffic.
Fix: Update your server's CA certificates. On Ubuntu/Debian: run the ca-certificates update command. In Node.js, ensure you are not setting rejectUnauthorized to false in production (this disables SSL verification and is a security risk).
DNS resolution failures
Your server cannot resolve api.paystack.co or api.flutterwave.com. This is usually a hosting infrastructure issue, not a code problem. Fix: check your server's DNS configuration. Use reliable DNS resolvers. Consider adding a fallback DNS resolver.
Rate limiting
Both Paystack and Flutterwave have rate limits on their APIs. If you send too many requests in a short period (e.g., initializing hundreds of transactions per second), you will get HTTP 429 (Too Many Requests) errors. Normal e-commerce traffic rarely hits these limits. If you do, implement request queuing and exponential backoff.
Debugging Workflow and Learning Resources
When you hit an error you cannot immediately identify, follow this workflow:
- Check the HTTP status code. 401/403 = authentication. 400 = bad request (check your payload format). 404 = wrong endpoint URL. 500 = gateway-side issue (retry). 429 = rate limited.
- Read the response body. Both Paystack and Flutterwave return error messages in the response body. The "message" field usually tells you exactly what went wrong.
- Check the dashboard. Both payment gateways have transaction logs in their dashboards. Find the failed transaction and check the detailed status and error information there.
- Compare with the docs. Pull up the specific API endpoint documentation and compare your request payload field by field. Check field names (case-sensitive), data types (string vs integer), and required vs optional fields.
- Test in sandbox. Reproduce the error in test mode first. The sandbox behaves identically to production for most error scenarios, and you can experiment without risk.
The official Paystack and Flutterwave documentation includes error code references. Bookmark these and check them before posting questions on forums. Most errors are documented.
If you want to build strong debugging skills alongside payment integration knowledge, structured training helps. McTaba Academy's Full-Stack Software & AI Engineering course (NGN 140,000 to 220,000; exchange rates fluctuate, check current price at checkout) includes payment gateway integration with real debugging exercises. Tech Foundations (NGN 3,500 to 6,000; exchange rates fluctuate, check current price at checkout) is a good starting point if you are still building your programming fundamentals. Or start with a free account to see what is available.
Key Takeaways
- ✓Authentication errors (401/403) are almost always caused by using the wrong API key, mixing up test and live keys, or exposing the public key where the secret key is needed. Check your key prefixes: sk_ is secret (server-side), pk_ is public (client-side).
- ✓Paystack expects amounts in kobo (multiply naira by 100). Flutterwave expects amounts in the main currency unit (naira). Getting this wrong is the single most common integration bug for both platforms.
- ✓Webhook failures cause the most production headaches. If your webhook endpoint is unreachable, returns an error, or does not respond within the timeout, you will miss payment confirmations for bank transfers and USSD payments.
- ✓Card payment declines are often customer-side issues (insufficient funds, wrong OTP, expired card), not bugs in your code. But you must handle them gracefully with clear error messages.
Frequently Asked Questions
- Why does Paystack say "Invalid amount" when I send the correct naira value?
- Paystack expects amounts in kobo, not naira. NGN 5,000 should be sent as 500000. If you send 5000, Paystack interprets that as NGN 50 (5000 kobo). If the resulting amount is below the minimum transaction amount, you get an "Invalid amount" error. Multiply your naira amount by 100 before sending it to Paystack.
- My Paystack webhook is not being received. What should I check first?
- Check these in order: (1) Is the webhook URL correct in your Paystack dashboard? (2) Is your server publicly accessible at that URL? Test with curl or Postman from an external machine. (3) Is your endpoint using HTTPS with a valid SSL certificate? (4) Is your firewall allowing incoming POST requests? (5) Is your endpoint returning HTTP 200? Check your server logs for incoming requests to the webhook path.
- Can I use the same API keys for both test and live transactions?
- No. Paystack and Flutterwave provide separate sets of keys for test and live environments. Test keys work only in the sandbox. Live keys work only in production. Using the wrong keys is one of the most common errors. Test keys start with sk_test_ and pk_test_ for Paystack. Live keys start with sk_live_ and pk_live_. Flutterwave test keys contain "TEST" in the key string.
- A customer says they paid but my system shows the order as pending. What happened?
- Most likely a webhook issue. The customer may have completed a bank transfer or USSD payment, but the webhook notification did not reach your server. Check your webhook logs first. Then verify the transaction manually using the Paystack or Flutterwave verification API with the transaction reference. If the verification confirms the payment succeeded, update the order manually and investigate why the webhook failed (check the webhook delivery logs in the gateway dashboard).
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