HomeBlogWeb Development
Web Development

How to Integrate Flutterwave into Your Web App

The payment gateway for products collecting money across multiple African countries. Works with any stack.

JS

Jovi Studio

9 min read · Web Development

When Flutterwave is the right choice

If your product only needs to collect payments in Nigeria, Paystack is simpler. Flutterwave earns its place when you need multiple African countries in the same integration — M-Pesa in Kenya, mobile money in Ghana, EFT in South Africa, cards everywhere. 30+ currencies, one API. If your roadmap includes pan-African expansion, building on Flutterwave from the start is less painful than migrating to it after you've already shipped Paystack.

How Flutterwave payments work

Same core pattern as any hosted checkout: your server creates a payment with the Flutterwave API, gets back a hosted payment link, redirects the user. They pay using whichever local method is available in their country. Flutterwave redirects back to your URL with a transaction ID. You verify that ID server-side before releasing anything. The payment methods shown — cards, M-Pesa, mobile money, bank transfer — are determined automatically by the user's currency and country.

1. Get your API keys

From the Flutterwave dashboard under Settings > API. The secret key lives on the server only:

bash
# Test keys
FLUTTERWAVE_SECRET_KEY=FLWSECK_TEST-...
FLUTTERWAVE_PUBLIC_KEY=FLWPUBK_TEST-...
FLW_WEBHOOK_HASH=your_custom_hash # Set this in FLW dashboard > Webhooks

2. Create a payment (server-side)

A plain API call — works in any language. Pass currency to control which payment methods Flutterwave shows:

javascript
async function createFlutterwavePayment({ email, name, amount, currency = 'NGN' }) {
  const tx_ref = `order-${Date.now()}-${Math.random().toString(36).slice(2)}`;

  const response = await fetch('https://api.flutterwave.com/v3/payments', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.FLUTTERWAVE_SECRET_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      tx_ref,
      amount,
      currency,       // 'NGN', 'KES', 'GHS', 'ZAR', 'USD', etc.
      redirect_url: `${process.env.APP_URL}/payment/verify`,
      customer: { email, name },
      customizations: { title: 'Your Product Name' },
    }),
  });

  const data = await response.json();
  if (data.status !== 'success') throw new Error('Flutterwave payment creation failed');

  // Redirect user to data.data.link
  return { link: data.data.link, tx_ref };
}

3. Verify on return (required)

Flutterwave appends transaction_id and tx_ref to your redirect URL. Verify the transaction_id before fulfilling — never trust the redirect alone:

javascript
async function verifyFlutterwavePayment(transactionId) {
  const response = await fetch(
    `https://api.flutterwave.com/v3/transactions/${transactionId}/verify`,
    {
      headers: {
        Authorization: `Bearer ${process.env.FLUTTERWAVE_SECRET_KEY}`,
      },
    }
  );

  const data = await response.json();

  if (data.data?.status === 'successful') {
    return { success: true, data: data.data };
  }

  return { success: false };
}

4. Webhooks for async payment methods

Mobile money and bank transfers don't complete instantly. Webhooks tell your server when money actually arrives. Flutterwave uses a simple hash comparison — no HMAC, just a secret string you set in the dashboard:

javascript
// Register your webhook URL in FLW dashboard > Settings > Webhooks
async function handleFlutterwaveWebhook(body, signatureHeader) {
  const secretHash = process.env.FLW_WEBHOOK_HASH;

  if (signatureHeader !== secretHash) {
    throw new Error('Invalid Flutterwave webhook signature');
  }

  const event = typeof body === 'string' ? JSON.parse(body) : body;

  if (
    event.event === 'charge.completed' &&
    event.data.status === 'successful'
  ) {
    const { tx_ref, amount, currency, customer } = event.data;
    // Use tx_ref as idempotency key — this webhook may fire more than once
  }

  return { received: true };
}

5. Flutterwave inline checkout (client-side)

To trigger the checkout modal directly on your page without a redirect:

html
<script src="https://checkout.flutterwave.com/v3.js"></script>

<button onclick="payWithFlutterwave()">Pay Now</button>

<script>
  function payWithFlutterwave() {
    FlutterwaveCheckout({
      public_key: 'FLWPUBK_TEST-...', // Public key only
      tx_ref: 'order-' + Date.now(),
      amount: 5000,
      currency: 'NGN',
      customer: {
        email: 'customer@email.com',
        name: 'Customer Name',
      },
      callback: function(data) {
        // Always verify data.transaction_id server-side before fulfilling
        verifyOnServer(data.transaction_id);
      },
      onclose: function() {},
    });
  }
</script>

Key currencies and what they unlock

Currency selection determines which local payment methods Flutterwave displays to your user:

  • NGN — Nigerian cards, bank transfer, USSD, Opay, PalmPay
  • KES — M-Pesa (Kenya's dominant payment method — essential if you have Kenyan users)
  • GHS — MTN Mobile Money, Vodafone Cash, AirtelTigo
  • ZAR — South African Visa/Mastercard, EFT
  • USD / GBP / EUR — international cards, useful for diaspora payments or global users

Common questions

Selling to users across Africa?

We've built Flutterwave integrations for products collecting payments in Nigeria, Kenya, Ghana, and beyond — including setups that route between Flutterwave and Stripe based on where the user is. Tell us your stack and your markets.