After the WKWebView callback delivers the transaction reference, call your backend verify endpoint using URLSession with async/await. Your backend calls the Paystack verify API and returns the result. In Swift, parse the JSON response, check verified == true, and update your SwiftUI view or UIKit label accordingly.
// PaymentService.swift (continued)
struct VerifyResponse: Codable {
let verified: Bool
let amount: Double?
let currency: String?
let reference: String?
let error: String?
}
extension PaymentService {
func verify(reference: String) async throws -> VerifyResponse {
guard let encodedRef = reference.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let url = URL(string: "(baseURL)/api/pay/verify?reference=(encodedRef)") else {
throw URLError(.badURL)
}
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse else {
throw URLError(.badServerResponse)
}
if httpResponse.statusCode == 200 {
return try JSONDecoder().decode(VerifyResponse.self, from: data)
} else {
let errorResponse = try? JSONDecoder().decode(VerifyResponse.self, from: data)
return errorResponse ?? VerifyResponse(verified: false, amount: nil,
currency: nil, reference: nil,
error: "Verification failed")
}
}
}
// PaymentStatusView.swift (SwiftUI)
import SwiftUI
@MainActor
class PaymentStatusViewModel: ObservableObject {
@Published var state: PaymentState = .loading
enum PaymentState {
case loading
case success(amount: Double, currency: String, reference: String)
case failure(message: String)
}
func verify(reference: String) {
Task {
do {
let result = try await PaymentService.shared.verify(reference: reference)
if result.verified, let amount = result.amount, let currency = result.currency {
state = .success(amount: amount, currency: currency,
reference: result.reference ?? reference)
} else {
state = .failure(message: result.error ?? "Payment not confirmed")
}
} catch {
state = .failure(message: "Network error: (error.localizedDescription)")
}
}
}
}
struct PaymentStatusView: View {
let reference: String
@StateObject private var viewModel = PaymentStatusViewModel()
@Environment(\.dismiss) var dismiss
var body: some View {
Group {
switch viewModel.state {
case .loading:
VStack(spacing: 16) {
ProgressView()
Text("Verifying payment...")
.foregroundColor(.secondary)
}
case .success(let amount, let currency, let ref):
VStack(spacing: 16) {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 64))
.foregroundColor(.green)
Text("Payment Confirmed")
.font(.title)
Text("(currency) (String(format: "%.2f", amount))")
.font(.headline)
Text("Ref: (ref)")
.font(.caption)
.foregroundColor(.secondary)
Button("Continue") { dismiss() }
.buttonStyle(.borderedProminent)
}
case .failure(let message):
VStack(spacing: 16) {
Image(systemName: "xmark.circle.fill")
.font(.system(size: 64))
.foregroundColor(.red)
Text("Verification Failed")
.font(.title)
Text(message)
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
Button("Retry") { viewModel.verify(reference: reference) }
.buttonStyle(.bordered)
Button("Contact Support") { /* Navigate to support */ }
.foregroundColor(.secondary)
}
}
}
.padding()
.onAppear { viewModel.verify(reference: reference) }
}
}
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
This guide is part of the Paystack integration guides by language and framework series.
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