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 iOS with Swift
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Handle Paystack Webhooks in iOS with Swift

iOS apps cannot receive Paystack webhooks directly. Build a backend webhook endpoint, verify the signature, and update your database. Send payment events to the iOS app via APNs push notifications using Firebase Cloud Messaging or the Apple Push Notification service directly. In iOS, handle push notifications in AppDelegate or UNUserNotificationCenter and update your app state accordingly.

In this article

  1. 1. Architecture: Backend Receives, iOS Displays
  2. 2. Backend: Webhook + APNs Push
  3. 3. iOS: Push Notification Setup
  4. 4. Learn More

Architecture: Backend Receives, iOS Displays

The payment notification flow for iOS:

  1. Paystack fires a webhook to your backend
  2. Backend verifies the signature and processes the event
  3. Backend updates the database (order marked paid)
  4. Backend sends an APNs push notification via FCM or directly
  5. iOS receives the push, updates the UI

Your iOS app never makes outbound connections to Paystack. It only talks to your backend.

Backend: Webhook + APNs Push

// Node.js backend with Firebase Admin SDK (FCM to iOS)
var crypto = require('crypto');
var admin = require('firebase-admin');

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

    if (hash !== sig) return res.status(400).send('Invalid signature');
    res.sendStatus(200);

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

    if (event.event === 'charge.success') {
      var email = event.data.customer.email;
      var reference = event.data.reference;
      var amount = event.data.amount / 100;

      await db.orders.markAsPaid(reference);

      var user = await db.users.findByEmail(email);
      if (user?.iosApnsToken) {
        // Send via FCM (which delivers to APNs for iOS)
        await admin.messaging().send({
          token: user.iosFcmToken,
          apns: {
            payload: {
              aps: {
                alert: {
                  title: 'Payment Confirmed',
                  body: 'NGN ' + amount + ' received'
                },
                sound: 'default',
                badge: 1
              }
            }
          },
          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.

iOS: Push Notification Setup

// AppDelegate.swift
import UIKit
import UserNotifications
import FirebaseCore
import FirebaseMessaging

@main
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate,
                   UNUserNotificationCenterDelegate {

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        FirebaseApp.configure()

        UNUserNotificationCenter.current().delegate = self
        Messaging.messaging().delegate = self

        UNUserNotificationCenter.current().requestAuthorization(
            options: [.alert, .badge, .sound]
        ) { granted, error in
            if granted {
                DispatchQueue.main.async {
                    application.registerForRemoteNotifications()
                }
            }
        }
        return true
    }

    // FCM token received — send to your backend
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        guard let token = fcmToken else { return }
        sendTokenToBackend(token: token)
    }

    // Notification received while app is in foreground
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        let userInfo = notification.request.content.userInfo
        handlePaystackEvent(userInfo: userInfo)
        completionHandler([.banner, .sound])
    }

    // Notification tapped (app in background or closed)
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        let userInfo = response.notification.request.content.userInfo
        handlePaystackEvent(userInfo: userInfo)
        completionHandler()
    }

    private func handlePaystackEvent(userInfo: [AnyHashable: Any]) {
        guard let event = userInfo["event"] as? String,
              let reference = userInfo["reference"] as? String else { return }

        if event == "charge.success" {
            // Post notification so any listening View can update
            NotificationCenter.default.post(
                name: Notification.Name("PaystackPaymentConfirmed"),
                object: nil,
                userInfo: ["reference": reference]
            )
        }
    }

    private func sendTokenToBackend(token: String) {
        // POST token to your backend API
    }
}

Learn More

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

Sign up for the McTaba newsletter

Key Takeaways

  • ✓iOS apps do not have public HTTPS endpoints. Paystack webhooks cannot reach them directly.
  • ✓Build the webhook handler on your backend. iOS app is only a consumer of payment events.
  • ✓Use APNs (via Firebase Cloud Messaging or directly) to push payment events to the iOS app.
  • ✓Implement UNUserNotificationCenterDelegate to handle notifications in the foreground and background.
  • ✓Request notification permissions using UNUserNotificationCenter.requestAuthorization early in the app lifecycle.
  • ✓For in-app real-time updates, use polling or a WebSocket while the user is on a payment-waiting screen.

Frequently Asked Questions

Do I need Firebase to send push notifications for Paystack events on iOS?
No. You can send directly via APNs using libraries like node-apn or go-apns2. Firebase Cloud Messaging is convenient because it handles the APNs certificate management and token refresh. For most apps, FCM is the easier path.
What is the difference between device token and FCM token in iOS?
The device token is the raw APNs token from iOS. The FCM token is a Firebase registration token that Firebase maps to the APNs token internally. When using Firebase, use the FCM token. Firebase handles token refresh and delivery to APNs for you.
Can I receive Paystack events in a SwiftUI app via push notifications?
Yes. Use @UIApplicationDelegateAdaptor in your SwiftUI app to connect AppDelegate with push notification handling. Post a NotificationCenter notification from AppDelegate when a payment event arrives, and observe it in your SwiftUI views with .onReceive.
What if the user denies notification permissions on iOS?
Fall back to polling. After checkout, start polling your backend status endpoint every few seconds on the payment-pending screen. If the user denies permissions, do not ask again immediately — guide them to Settings if they want to enable notifications later.

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 iOS with Swift

Accept Paystack payments in a native iOS app with Swift. This guide covers backend-driven transaction initialization, loading the Paystack checkout in WKWebView, detecting the callback redirect, and integrating with SwiftUI.

Read more

Verify Paystack Payments in iOS with Swift

After Paystack checkout in iOS, verify the transaction by calling your backend. This guide covers the Swift verification flow using URLSession with async/await, Observable objects for SwiftUI, and error handling.

Read more

Build Paystack Subscriptions in iOS with Swift

Add Paystack subscription billing to an iOS app built with Swift. This guide covers the subscription checkout flow, checking subscription status from your backend, displaying plan information in SwiftUI, and handling cancellation.

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.