Subscribe a user by calling your backend to initialize a transaction with a Paystack plan code. Open the authorization URL in WKWebView. After checkout, Paystack creates the subscription and your backend receives the webhook. Check subscription status from iOS by calling your backend API — not Paystack directly.
If you distribute on the App Store and sell digital subscriptions consumed within the app, Apple requires in-app purchases via StoreKit. Paystack subscriptions are appropriate for:
Check the current App Store Review Guidelines before launching.
// Models/SubscriptionStatus.swift
struct SubscriptionStatus: Codable {
let active: Bool
let planName: String?
let nextBillingDate: String?
let subscriptionCode: String?
let emailToken: String?
enum CodingKeys: String, CodingKey {
case active
case planName = "plan_name"
case nextBillingDate = "next_billing_date"
case subscriptionCode = "subscription_code"
case emailToken = "email_token"
}
}
// SubscriptionService.swift
extension PaymentService {
func getSubscriptionStatus(email: String) async throws -> SubscriptionStatus {
let encodedEmail = email.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? email
guard let url = URL(string: "(baseURL)/api/subscription/status?email=(encodedEmail)") else {
throw URLError(.badURL)
}
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(SubscriptionStatus.self, from: data)
}
func cancelSubscription(code: String, token: String) async throws {
guard let url = URL(string: "(baseURL)/api/subscription/cancel") else {
throw URLError(.badURL)
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONEncoder().encode([
"subscription_code": code,
"email_token": token
])
let (_, _) = try await URLSession.shared.data(for: request)
}
}
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
// SubscriptionView.swift
import SwiftUI
@MainActor
class SubscriptionViewModel: ObservableObject {
@Published var status: SubscriptionStatus?
@Published var loading = false
@Published var error: String?
func load(email: String) {
loading = true
Task {
do {
status = try await PaymentService.shared.getSubscriptionStatus(email: email)
} catch {
self.error = error.localizedDescription
}
loading = false
}
}
func cancel() {
guard let code = status?.subscriptionCode,
let token = status?.emailToken else { return }
loading = true
Task {
do {
try await PaymentService.shared.cancelSubscription(code: code, token: token)
status = SubscriptionStatus(active: false, planName: nil,
nextBillingDate: nil,
subscriptionCode: nil, emailToken: nil)
} catch {
self.error = "Cancellation failed: (error.localizedDescription)"
}
loading = false
}
}
}
struct SubscriptionView: View {
let userEmail: String
@StateObject private var viewModel = SubscriptionViewModel()
var body: some View {
Group {
if viewModel.loading {
ProgressView("Loading...")
} else if let status = viewModel.status {
if status.active {
VStack(alignment: .leading, spacing: 16) {
Label("Active Subscription", systemImage: "checkmark.seal.fill")
.font(.headline)
.foregroundColor(.green)
if let planName = status.planName {
Text("Plan: (planName)")
}
if let nextDate = status.nextBillingDate {
Text("Next billing: (nextDate)")
.foregroundColor(.secondary)
}
Button("Cancel Subscription", role: .destructive) {
viewModel.cancel()
}
.padding(.top)
}
.padding()
} else {
VStack(spacing: 16) {
Text("No active subscription")
NavigationLink("Choose a Plan") {
PlanSelectionView(userEmail: userEmail)
}
.buttonStyle(.borderedProminent)
}
}
}
}
.onAppear { viewModel.load(email: userEmail) }
.alert("Error", isPresented: Binding(
get: { viewModel.error != nil },
set: { if !$0 { viewModel.error = nil } }
)) {
Button("OK", role: .cancel) {}
} message: {
Text(viewModel.error ?? "")
}
}
}
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