How to Test Payments in Sandbox Mode: Paystack and Flutterwave (Nigeria)
To test Paystack payments: use your test API keys (sk_test_ and pk_test_), initialize transactions against the standard API endpoints, and use Paystack test card numbers (4084 0840 8408 4081 for success, 4084 0840 8408 4085 for declined). For Flutterwave: use test keys (FLWSECK_TEST-), and use Flutterwave test card 4242 4242 4242 4242 for success. Both platforms simulate the full payment flow including webhooks. Bank transfer testing generates a virtual account in sandbox that you can simulate paying. Before going live, run at least 5 test scenarios: successful card, declined card, successful bank transfer, expired transfer, and webhook delivery confirmation.
Why Sandbox Testing Matters
Shipping a payment integration without proper testing is how developers accidentally charge customers wrong amounts, miss payment confirmations, or create duplicate orders. The sandbox exists so you can catch these bugs before real money is involved.
Both Paystack and Flutterwave provide test environments that behave almost identically to production. The key differences:
- No real money moves. Test transactions are simulated.
- You use test API keys, which are separate from your live keys.
- Test card numbers are provided that simulate different outcomes (success, decline, error).
- Webhooks are sent to your configured URL just like in production, but with test data.
- Bank transfers generate virtual accounts that you can simulate paying from the dashboard.
The sandbox uses the same API endpoints as production (api.paystack.co for Paystack, api.flutterwave.com for Flutterwave). The system distinguishes test from live based on your API key prefix, not the URL. This means your code does not need environment-specific URLs. You just swap the API keys.
A common mistake is testing only the "happy path" (successful card payment). Production users will trigger every edge case: wrong OTP, insufficient funds, expired transfer, abandoned checkout, duplicate clicks on the "pay" button. Test these in sandbox before they happen in production.
Paystack Sandbox: Setup and Test Cards
Getting your test keys: Log into the Paystack dashboard. Toggle to "Test Mode" using the switch in the top-left corner. Go to Settings, then API Keys & Webhooks. Copy your test public key (pk_test_...) and test secret key (sk_test_...). Use these in your application's environment variables.
Paystack test card numbers:
- Successful payment: Card number 4084 0840 8408 4081, expiry 12/28, CVV 408, OTP 123456
- Declined (insufficient funds): Card number 4084 0840 8408 4085, same expiry and CVV
- Requires PIN: Card number 5060 6601 1111 1114, PIN 1234, OTP 123456
- International card: Card number 4000 0000 0000 0002, any future expiry and 3-digit CVV
Testing bank transfers: When you initialize a transaction with the "bank_transfer" channel, the sandbox generates a virtual account number. In test mode, Paystack simulates the transfer confirmation automatically after a few seconds. You do not need to actually send money. Watch your webhook endpoint for the confirmation notification.
Testing USSD: USSD payments cannot be fully simulated in sandbox because they require dialing a USSD code on a real phone. Paystack's sandbox shows the USSD prompt but may not trigger a completion webhook automatically. For USSD testing, use the Paystack dashboard's manual simulation tools or wait until you do a small live test.
Webhook testing: Set your webhook URL in the test mode settings (Settings, then API Keys & Webhooks, then Webhook URL). Make sure this points to a publicly accessible endpoint. If your development server is running locally, use a tunneling tool like ngrok to expose it. Paystack sends test webhooks to this URL for every test transaction, just like it would in production.
Flutterwave Sandbox: Setup and Test Cards
Getting your test keys: Log into the Flutterwave dashboard. Switch to "Test Mode" using the toggle. Navigate to Settings, then API. Copy your test public key (FLWPUBK_TEST-...) and test secret key (FLWSECK_TEST-...). Also note the encryption key for inline checkout.
Flutterwave test card numbers:
- Successful payment: Card number 4242 4242 4242 4242, expiry 12/28, CVV 123. This simulates a successful Visa card payment.
- PIN required: Card number 5531 8866 5214 2950, expiry 09/32, CVV 564, PIN 3310, OTP 12345
- Declined: Use specific test cards from the Flutterwave documentation to simulate declines. The available test scenarios may change, so check the latest docs.
- Verve card (Nigeria-specific): Card number 5060 6666 6666 6666 666, expiry 12/28, CVV 123. This simulates a local Verve card payment.
Testing bank transfers: Flutterwave's sandbox generates a test bank transfer instruction (account number and bank name). The sandbox may auto-complete the transfer or provide a button on the checkout page to simulate payment. Check the Flutterwave developer docs for the current simulation behavior, as this varies by integration method.
Webhook setup: In the Flutterwave dashboard under Settings, then Webhooks, set your webhook URL and the "Secret Hash" (a shared secret used to verify webhook authenticity). Send test transactions and verify your endpoint receives the webhook payloads correctly.
Note on amount format: Remember that Flutterwave uses the main currency unit (naira), not the smallest unit (kobo). If you are testing both gateways, be careful not to apply Paystack's kobo conversion to Flutterwave requests or vice versa. This is the most common bug when developers support both platforms.
Five Test Scenarios to Run Before Going Live
Do not go live until you have successfully tested all five of these scenarios.
Scenario 1: Successful card payment. Use the success test card. Verify: (a) the Paystack/Flutterwave checkout opens correctly, (b) the test card is accepted, (c) your callback URL receives the redirect with the transaction reference, (d) your server-side verification confirms the payment, (e) your webhook handler receives and processes the confirmation, (f) the order status in your database updates to "paid."
Scenario 2: Declined card payment. Use the decline test card. Verify: (a) the checkout shows a clear error message to the customer, (b) your system does not mark the order as paid, (c) the customer can retry payment without re-entering their order details, (d) no webhook is sent for a declined card (or if one is sent, your handler correctly identifies it as a failure).
Scenario 3: Successful bank transfer. Initialize a transaction with bank_transfer as a payment channel. Verify: (a) the virtual account details display correctly, (b) the sandbox simulates the transfer, (c) your webhook handler receives the bank transfer confirmation, (d) the order status updates to "paid." This is the test most developers skip, and it is where most production bugs hide.
Scenario 4: Expired bank transfer. Initialize a bank transfer but do not simulate payment. Wait for the transaction to expire (or check if the sandbox has a way to force expiration). Verify: (a) your system receives a failure/expiration webhook, (b) the order status updates to "expired" or "failed," (c) any reserved inventory is released.
Scenario 5: Duplicate webhook handling. Manually send the same webhook payload to your endpoint twice (using curl or Postman). Verify: (a) your server processes the first webhook correctly, (b) the second identical webhook is recognized as a duplicate and does not trigger duplicate order fulfillment, (c) no duplicate entries are created in your database.
Pre-Launch Checklist: Before Switching to Live Keys
Once all test scenarios pass, work through this checklist before accepting real money.
- Swap API keys. Replace all test keys with live keys in your environment variables. Double-check: sk_live_ for Paystack secret key, FLWSECK- for Flutterwave secret key. Do not hardcode keys in your source code. Use environment variables.
- Update webhook URL in the live dashboard. The test-mode webhook URL and live-mode webhook URL are configured separately in both Paystack and Flutterwave. Set the live webhook URL pointing to your production server.
- Verify SSL certificate. Your production domain must have a valid HTTPS certificate. Both gateways require HTTPS for webhooks and checkout redirects.
- Confirm the correct amount format. Verify your code sends amounts in the right format: kobo for Paystack (multiply by 100), naira for Flutterwave (no conversion). Test by logging the exact payload sent to the API.
- Test with a small live transaction. Make one real purchase on your site with a small amount (NGN 100 to 500). Verify the payment succeeds, the webhook arrives, and your order management processes it correctly. Then refund the transaction from the gateway dashboard.
- Set up monitoring. Implement alerts for webhook delivery failures, high error rates, or unusual transaction patterns. Both Paystack and Flutterwave dashboards show webhook delivery logs, but you should also monitor on your end.
- Document your setup. Write internal documentation covering: which gateway you use, where API keys are stored, the webhook URL and what it handles, the amount format, and how to investigate a disputed transaction. Your future self (or a teammate) will need this.
Building Payment Testing Skills
Proper payment testing is part of a broader set of engineering skills: understanding API integration patterns, writing tests, handling edge cases, and deploying applications with the correct configuration. These skills are best learned in context, not in isolation.
McTaba Academy covers payment integration and testing as part of its full-stack curriculum. Full-Stack Software & AI Engineering (NGN 140,000 to 220,000; exchange rates fluctuate, check current price at checkout) includes hands-on exercises for building and testing Paystack and Flutterwave integrations. Deployment: Going Live (NGN 6,000 to 10,000; exchange rates fluctuate, check current price at checkout) covers the production deployment and environment configuration that payment integrations require. Or start with Tech Foundations (NGN 3,500 to 6,000; exchange rates fluctuate, check current price at checkout) if you are still building your programming fundamentals. Create a free account to see all options.
The official sandbox documentation for Paystack and Flutterwave is also essential reading. Both include up-to-date test card numbers, simulation instructions, and sandbox behavior details.
Key Takeaways
- ✓Both Paystack and Flutterwave provide test environments that simulate the full payment flow without moving real money. Use test API keys and test card numbers to verify your integration before accepting live payments.
- ✓Test every payment method you plan to offer: cards, bank transfers, and USSD. Card-only testing misses issues with bank transfer webhook handling, which is the most common source of production bugs.
- ✓Verify your webhook handler works correctly in test mode. Simulate successful payments, failed payments, and duplicate webhooks. Your system should handle all three cases without breaking.
- ✓Before switching to live keys, complete a pre-launch checklist: webhook URL configured in the dashboard, SSL certificate valid, environment variables updated, error handling tested, and at least one small live transaction confirmed.
Frequently Asked Questions
- Do Paystack and Flutterwave test modes use different API endpoints?
- No. Both use the same API endpoints for test and live modes. The system determines the mode based on your API key prefix (test keys vs live keys). This simplifies your code because you only need to swap the key value, not the URL. Your environment variables control which mode you are in.
- Can I test webhooks on localhost?
- Not directly. Paystack and Flutterwave need to send HTTP requests to your webhook endpoint, which means it must be publicly accessible. For local development, use a tunneling tool like ngrok, which creates a public URL that forwards to your local server. Run ngrok and point your webhook URL in the payment gateway dashboard to the generated ngrok URL.
- How do I test bank transfer payments in sandbox mode?
- When you initialize a transaction with the bank_transfer channel using test keys, the sandbox generates a virtual account. Paystack typically auto-completes the simulated transfer after a short delay, triggering the webhook. Flutterwave may provide a simulation button on the checkout page. The exact behavior can vary, so check the latest sandbox documentation for each platform.
- Should I test with real money before going live?
- Yes, after sandbox testing. Make one or two small real transactions (NGN 100 to 500) using live keys to verify the end-to-end flow with actual bank infrastructure. This catches issues that the sandbox cannot simulate, like real OTP delivery delays, actual bank transfer settlement times, and live webhook delivery. Refund the test transactions from your gateway dashboard afterward.
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