McTaba Labs logo
Software EngineeringThe WhiteboardProjectsEventsMasterclassesWebinarsLearn
Book Call

500+ developers already in the community

Get answers, find accountability partners, and stay ahead

Join Discord →
McTaba Labs logo

Educating 1 million students in tech by 2030.

Programs

  • HighSchool Graduates Launchpad
  • AI & Business Automation
  • Corporate Training
  • McTaba Story & Mission
  • Full-Stack Curriculum
  • Course Pricing & Plans
  • Frequently Asked Questions

Resources

  • Join Discord Community
  • Tech Events in Nairobi
  • Masterclasses
  • Free Resources
  • Learn
  • Campus
  • Africa Tech Talent Report
  • Partner With Us
  • Start a Project
  • Our Work

Contact Us

  • 124 Manyani East Road, Lavington, Nairobi
  • +254727795603
  • Book a Discovery Call

Office Hours:

Mon-Fri: 9am - 2pm EAT

Sat: 10am - 12pm EAT

© 2027 McTaba Labs - A product of Edtech Industries. All rights reserved.

Privacy PolicyTerms of ServiceRefund Policy
  1. Learn
  2. Paystack for African Developers
  3. Handle Paystack Webhooks in Flutter
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Handle Paystack Webhooks in Flutter

Flutter cannot receive Paystack webhooks directly — webhooks are server-to-server HTTP requests. Build a webhook endpoint on your backend (Node.js, Django, etc.), verify the x-paystack-signature, process the event, and then notify your Flutter app via push notifications (FCM), WebSocket, or the Flutter app polling a status endpoint. The backend is the source of truth.

In this article

  1. 1. Webhooks Are Not for Mobile Apps
  2. 2. Backend Webhook Handler
  3. 3. Flutter: Handle Push Notification
  4. 4. Alternative: Polling from Flutter
  5. 5. Learn More

Webhooks Are Not for Mobile Apps

A Paystack webhook is an HTTP POST request from Paystack's servers to a URL you control. Mobile apps do not have a public IP address or domain. Paystack cannot reach your Flutter app directly.

The correct pattern:

  1. Your backend server receives the Paystack webhook
  2. Backend verifies the signature and processes the event
  3. Backend updates your database
  4. Backend notifies the Flutter app through one of three channels

Backend Webhook Handler

Your backend handles the webhook. Here is a Node.js example that also triggers a push notification:

// Node.js/Express webhook handler
var crypto = require('crypto');
var admin = require('firebase-admin'); // For FCM push notifications

app.post('/api/webhooks/paystack', express.raw({ type: 'application/json' }), async (req, res) => {
  var signature = req.headers['x-paystack-signature'];
  var hash = crypto
    .createHmac('sha512', process.env.PAYSTACK_SECRET_KEY)
    .update(req.body)
    .digest('hex');

  if (hash !== signature) {
    return res.status(400).json({ error: 'Invalid signature' });
  }

  res.sendStatus(200); // Respond immediately

  var event = JSON.parse(req.body.toString());

  switch (event.event) {
    case 'charge.success':
      await handleChargeSuccess(event.data);
      break;
    default:
      console.log('Unhandled event:', event.event);
  }
});

async function handleChargeSuccess(data) {
  var reference = data.reference;
  var amount = data.amount / 100;
  var customerEmail = data.customer.email;

  // Update order in database
  var user = await db.users.findByEmail(customerEmail);

  // Push to Flutter app via FCM
  if (user && user.fcmToken) {
    await admin.messaging().send({
      token: user.fcmToken,
      notification: {
        title: 'Payment Confirmed',
        body: 'Your payment of NGN ' + amount + ' was successful',
      },
      data: {
        event: 'charge.success',
        reference: reference,
        amount: String(amount),
      },
    });
  }
}

Get weekly developer tips

Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.

No spam. Unsubscribe anytime.

Flutter: Handle Push Notification

// lib/main.dart
import 'package:firebase_messaging/firebase_messaging.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  // Handle background messages
  FirebaseMessaging.onBackgroundMessage(_handleBackgroundMessage);

  runApp(const MyApp());
}

Future<void> _handleBackgroundMessage(RemoteMessage message) async {
  print('Background message: ${message.data}');
}

// In your app widget
class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    _setupMessaging();
  }

  Future<void> _setupMessaging() async {
    // Get FCM token and send to backend
    var token = await FirebaseMessaging.instance.getToken();
    if (token != null) {
      await _sendTokenToBackend(token);
    }

    // Handle foreground messages
    FirebaseMessaging.onMessage.listen((message) {
      var event = message.data['event'];
      if (event == 'charge.success') {
        var reference = message.data['reference'];
        // Update UI, navigate to success screen
        _handlePaymentSuccess(reference);
      }
    });

    // Handle notification tap (app in background)
    FirebaseMessaging.onMessageOpenedApp.listen((message) {
      var event = message.data['event'];
      if (event == 'charge.success') {
        _navigateToOrderScreen();
      }
    });
  }

  Future<void> _sendTokenToBackend(String token) async {
    // Send FCM token to your backend so it can push to this device
    await http.post(
      Uri.parse('https://your-backend.com/api/users/fcm-token'),
      headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + userToken},
      body: jsonEncode({'fcm_token': token}),
    );
  }
}

Alternative: Polling from Flutter

If push notifications are not set up yet, polling is a simpler fallback. Poll your backend every few seconds while the user is on a payment-pending screen:

// Poll for payment status
Timer? _pollingTimer;

void _startPolling(String reference) {
  _pollingTimer = Timer.periodic(const Duration(seconds: 3), (timer) async {
    var status = await _checkPaymentStatus(reference);
    if (status == 'success') {
      timer.cancel();
      _navigateToSuccess();
    } else if (status == 'failed') {
      timer.cancel();
      _showFailure();
    }
    // Continue polling if status is 'pending'
  });
}

@override
void dispose() {
  _pollingTimer?.cancel();
  super.dispose();
}

Cancel the timer when the screen is disposed to avoid memory leaks.

Learn More

This guide is part of the Paystack integration guides by language and framework series.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Paystack webhooks are sent to an HTTPS URL on your server. Flutter apps cannot receive them directly.
  • ✓Build a webhook handler on your backend (Node.js, Django, Laravel, etc.) — not in Flutter.
  • ✓After your backend processes a webhook, notify the Flutter app via FCM push notification, WebSocket, or polling.
  • ✓Push notifications (Firebase Cloud Messaging) are the most reliable way to wake the Flutter app on payment events.
  • ✓For in-app real-time updates (user is looking at the screen), use polling every 3-5 seconds or a WebSocket.
  • ✓Always verify the webhook signature on your backend before pushing anything to the Flutter app.

Frequently Asked Questions

Can I receive Paystack webhooks directly in a Flutter app?
No. Webhooks require an HTTPS server with a public domain. Flutter apps run on mobile devices that do not have public IP addresses or domains. You must have a backend server to receive webhooks.
What is the simplest way to notify Flutter after a webhook?
Polling is the simplest. After checkout, poll your backend status endpoint every 3-5 seconds for up to 30 seconds. If payment success is recorded (from the webhook), show the success screen. Firebase Cloud Messaging (FCM) is more reliable but requires more setup.
Do I need Firebase to handle Paystack webhooks in Flutter?
No. Firebase is one option for push notifications. You can also use polling (simplest), WebSockets, or OneSignal for push notifications. The webhook itself is handled entirely on your backend regardless of which notification method you choose.
How do I test Paystack webhooks locally for Flutter development?
Use ngrok or Cloudflare Tunnel to expose your local backend server. Set the webhook URL in the Paystack dashboard to the tunnel URL. Your Flutter app connects to localhost or the tunnel URL. Webhooks arrive at your local server and you can debug locally.

Learn Paystack Integration

KES 2,999

The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.

Start Paystack Course

Not ready? Create Free Account

Keep reading

Paystack Integration Guides by Language and Framework

A complete directory of Paystack integration guides organized by programming language and framework. Each guide covers accepting payments, verifying transactions, handling webhooks, and building subscriptions in your specific stack.

Read more

Accept Payments with Paystack in Flutter

Accept Paystack payments in a Flutter app. This guide covers using paystack_flutter or a custom WebView to launch checkout, capturing the callback, and verifying the transaction from your backend.

Read more

Verify Paystack Payments in Flutter

After a Paystack checkout in Flutter, verify the transaction by calling your backend, which calls the Paystack verify API. This guide shows the full verification flow from the WebView callback to showing the result to the user.

Read more

Build Paystack Subscriptions in Flutter

Add Paystack subscription billing to a Flutter mobile app. This guide covers the subscription flow from Flutter, checking subscription status from your backend, displaying plan information, and handling subscription management actions.

Read more

Idempotent Paystack Webhook Handlers: The Complete Pattern

A complete guide to building idempotent Paystack webhook handlers. Covers why Paystack sends duplicate events, using transaction references as idempotency keys, database-level deduplication with a processed_events table, and the full production-ready pattern in Node.js.

Read more

Why You Must Return 200 Immediately from a Paystack Webhook

The number one Paystack webhook reliability rule: return 200 before doing anything else. This guide explains why Paystack has a timeout, what happens when you break the rule, and how to implement the respond-first-then-queue pattern in Node.js.

Read more

Explore related topics

M-Pesa & African Stack

M-Pesa Daraja API tutorials, USSD development, and WhatsApp Business API guides. The African Stack that global bootcamps do not teach.

Learn to Code in Kenya

Everything a Kenyan needs to know before writing their first line of code. Career paths, real costs, hardware requirements, and honest answers about what it takes.

Learning to Code

Roadmaps, language guides, and practical advice for learning to code and launching a programming career.

Breaking Into Tech

Practical, no-nonsense guides for anyone looking to start or change their career in tech. From choosing your first language to acing interviews.

The African Stack: Skills for This Market

The skills that make Kenyan developers hard to replace: M-Pesa, USSD, WhatsApp, fintech, and building for the African market.

Bonaventure Ogeto

Bonaventure Ogeto

Founder, McTaba Labs

Bonaventure is the founder of McTaba Labs, a coding bootcamp in Nairobi, Kenya. He has spent years building products for the African market and mentoring the next generation of developers across the continent.