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. Accept Payments with Paystack in iOS with Swift
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Accept Payments with Paystack in iOS with Swift

iOS apps cannot call the Paystack API with a secret key. Initialize the transaction on your backend and return the authorization_url. Open it in a WKWebView in Swift. Implement WKNavigationDelegate to detect when Paystack redirects to your callback_url, extract the reference, and verify on your backend using URLSession.

In this article

  1. 1. Backend: Initialize Transaction
  2. 2. WKWebView Payment Controller
  3. 3. SwiftUI Integration
  4. 4. Learn More

Backend: Initialize Transaction

Call your backend from Swift to get the authorization URL:

// PaymentService.swift
struct InitializeResponse: Codable {
    let authorization_url: String
    let access_code: String
    let reference: String
}

struct InitializeRequest: Codable {
    let email: String
    let amount: Double
}

class PaymentService {
    static let shared = PaymentService()
    private let baseURL = "https://your-backend.com"

    func initialize(email: String, amount: Double) async throws -> InitializeResponse {
        guard let url = URL(string: "(baseURL)/api/pay/initialize") else {
            throw URLError(.badURL)
        }

        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = try JSONEncoder().encode(InitializeRequest(email: email, amount: amount))

        let (data, response) = try await URLSession.shared.data(for: request)

        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw URLError(.badServerResponse)
        }

        return try JSONDecoder().decode(InitializeResponse.self, from: data)
    }
}

WKWebView Payment Controller

// PaymentViewController.swift
import UIKit
import WebKit

protocol PaymentViewControllerDelegate: AnyObject {
    func paymentDidSucceed(reference: String)
    func paymentDidFail()
    func paymentWasCancelled()
}

class PaymentViewController: UIViewController, WKNavigationDelegate {
    private var webView: WKWebView!
    private let authorizationURL: String
    private let callbackURLPrefix = "https://yourapp.com/payment/callback"

    weak var delegate: PaymentViewControllerDelegate?

    init(authorizationURL: String) {
        self.authorizationURL = authorizationURL
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) { fatalError() }

    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Complete Payment"
        navigationItem.leftBarButtonItem = UIBarButtonItem(
            barButtonSystemItem: .cancel,
            target: self, action: #selector(cancel)
        )

        webView = WKWebView(frame: view.bounds)
        webView.navigationDelegate = self
        view.addSubview(webView)

        if let url = URL(string: authorizationURL) {
            webView.load(URLRequest(url: url))
        }
    }

    @objc func cancel() {
        delegate?.paymentWasCancelled()
        dismiss(animated: true)
    }

    func webView(_ webView: WKWebView,
                 decidePolicyFor navigationAction: WKNavigationAction,
                 decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        let url = navigationAction.request.url?.absoluteString ?? ""

        if url.hasPrefix(callbackURLPrefix) {
            // Extract reference from callback URL
            if let components = URLComponents(string: url),
               let reference = components.queryItems?.first(where: {
                   $0.name == "reference" || $0.name == "trxref"
               })?.value {
                decisionHandler(.cancel)
                delegate?.paymentDidSucceed(reference: reference)
                dismiss(animated: true)
                return
            }
        }

        decisionHandler(.allow)
    }
}

Get weekly developer tips

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

No spam. Unsubscribe anytime.

SwiftUI Integration

// PaymentWebView.swift (SwiftUI)
import SwiftUI
import WebKit

struct PaystackWebView: UIViewRepresentable {
    let authURL: String
    let callbackPrefix: String
    var onSuccess: (String) -> Void
    var onCancel: () -> Void

    func makeUIView(context: Context) -> WKWebView {
        let webView = WKWebView()
        webView.navigationDelegate = context.coordinator
        return webView
    }

    func updateUIView(_ webView: WKWebView, context: Context) {
        if let url = URL(string: authURL) {
            webView.load(URLRequest(url: url))
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }

    class Coordinator: NSObject, WKNavigationDelegate {
        var parent: PaystackWebView

        init(parent: PaystackWebView) { self.parent = parent }

        func webView(_ webView: WKWebView,
                     decidePolicyFor navigationAction: WKNavigationAction,
                     decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
            let url = navigationAction.request.url?.absoluteString ?? ""

            if url.hasPrefix(parent.callbackPrefix),
               let components = URLComponents(string: url),
               let reference = components.queryItems?.first(where: {
                   $0.name == "reference" || $0.name == "trxref"
               })?.value {
                decisionHandler(.cancel)
                parent.onSuccess(reference)
                return
            }
            decisionHandler(.allow)
        }
    }
}

// Usage in a SwiftUI view:
// PaystackWebView(
//     authURL: authURL,
//     callbackPrefix: "https://yourapp.com/payment/callback",
//     onSuccess: { reference in verifyPayment(reference) },
//     onCancel: { dismiss() }
// )

Learn More

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

Next: Verify Paystack Payments in iOS with Swift

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Never put the Paystack secret key in your iOS app bundle. Initialize transactions from a backend server.
  • ✓Use WKWebView with WKNavigationDelegate to display and intercept Paystack checkout.
  • ✓Detect the callback URL in decidePolicyFor navigationAction and extract the reference.
  • ✓Use URLSession (or Alamofire) to call your backend verify endpoint from Swift.
  • ✓SwiftUI apps can use WebView via UIViewRepresentable wrapping WKWebView.
  • ✓Configure App Transport Security to allow HTTPS connections to paystack.co if using older iOS targets.

Frequently Asked Questions

Can I use SFSafariViewController instead of WKWebView for Paystack checkout on iOS?
Yes. SFSafariViewController opens Paystack checkout in an in-app browser with better performance and privacy than WKWebView. To detect the callback, use a universal link or custom URL scheme. The user returns to your app via a deep link. SFSafariViewController cannot intercept navigations like WKWebView can.
Does Paystack have an official iOS Swift SDK?
Paystack does not maintain a well-supported official iOS SDK as of 2026. The WKWebView approach in this guide is reliable and depends only on the stable Paystack REST API. Community libraries exist on GitHub but verify maintenance status before use.
How do I handle App Transport Security (ATS) for Paystack on iOS?
Paystack uses HTTPS on paystack.co, so ATS allows it by default. You should not need to add exceptions. If you see ATS errors, check that your callback URL also uses HTTPS. ATS blocks HTTP connections in iOS apps.
What is the best way to call my backend API from Swift?
URLSession with async/await is the standard library approach. Alamofire is popular for more complex networking needs. For simple POST and GET calls to initialize and verify payments, URLSession is sufficient and has no dependencies.

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

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

Handle Paystack Webhooks in iOS with Swift

Paystack webhooks go to your server. This guide explains building the backend webhook handler and delivering payment events to an iOS Swift app via Apple Push Notification service (APNs) or Firebase Cloud Messaging.

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

Paystack Redirect Checkout vs Inline Checkout: Choosing Correctly

Paystack gives you two ways to present the checkout page: a full-page redirect to checkout.paystack.com, or an inline popup that overlays your site. Both collect payment identically. The difference is user experience, implementation complexity, and how you handle the post-payment flow.

Read more

Understanding Kobo, Pesewa and Cents: Paystack Amount Handling

Paystack expects amounts in the smallest currency unit: kobo for Naira, pesewas for Cedis, cents for Rand, Shillings, and Dollars. This guide covers the conversion rules, the floating-point traps that cause wrong charges, and the helper functions that keep your amounts correct.

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.