Skip to content

Swift / iOS SDK

Swift 5.9+   iOS 15+   macOS 12+

Native Swift SDK using URLSession and Apple's CryptoKit. async/await throughout. Distributed via Swift Package Manager.

Install

Xcode

File → Add Package Dependencies → paste:

https://github.com/neptune-astro/neptune-astro

Select the AstroSDK product.

Package.swift

swift
// Package.swift
dependencies: [
    .package(url: "https://github.com/neptune-astro/neptune-astro.git", from: "1.0.0")
],
targets: [
    .target(name: "MyApp", dependencies: [
        .product(name: "AstroSDK", package: "neptune-astro")
    ])
]

Quick Start

swift
import AstroSDK

let astro = AstroClient(config: AstroConfig(
    baseURL: URL(string: "https://astro.neptune.ly/api/v1")!,
    merchantKey: ProcessInfo.processInfo.environment["ASTRO_KEY"]
))

// Create session (call from your backend in production — not from the app directly)
let session = try await astro.payments.createSession(
    CreateSessionRequest(
        amount: 50_000,     // 50.000 LYD
        currency: "LYD",
        destination: Destination(type: "alias", value: "mtellesy"),
        reference: "order_1042",
        redirectUrl: "myapp://payment/result"
    )
)

// Open checkout URL
if let url = URL(string: session.checkoutUrl) {
    await UIApplication.shared.open(url)
}
swift
// In your @main App (SwiftUI) or SceneDelegate (UIKit)

// SwiftUI
@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL { url in
                    handlePaymentCallback(url)
                }
        }
    }
}

func handlePaymentCallback(_ url: URL) {
    guard url.scheme == "myapp", url.host == "payment" else { return }
    let params = URLComponents(url: url, resolvingAgainstBaseURL: false)?
        .queryItems?.reduce(into: [String: String]()) { $0[$1.name] = $1.value }
    
    if params?["status"] == "completed" {
        NotificationCenter.default.post(name: .paymentCompleted, object: params?["session_id"])
    }
}

Payments

swift
// Get session
let updated = try await astro.payments.getSession("ops_01HZGV...")
print(updated.status) // "PENDING" | "PROCESSING" | "COMPLETED" | "FAILED"

// List sessions
let sessions = try await astro.payments.listSessions(status: "COMPLETED")

// Cancel
let cancelled = try await astro.payments.cancelSession(session.sessionId)

Alias Resolution

swift
// Resolve alias → IBAN + bank
let resolved = try await astro.alias.resolve("mtellesy")
print("\(resolved.bankHandle): \(resolved.iban)")

// Get linked accounts
let accounts = try await astro.alias.getAccounts("mtellesy")

Open Banking

swift
// 1. Create consent
let consent = try await astro.openBanking.createConsent(
    bankHandle: "andalus",
    scopes: ["accounts:read", "transactions:read"],
    redirectUri: "myapp://ob/callback",
    state: UUID().uuidString,
    codeChallenge: pkce.challenge
)

// 2. Open consent URL
if let url = URL(string: consent.consentUrl) {
    await UIApplication.shared.open(url)
}

// 3. Handle callback deep link and exchange code
let tokens = try await astro.openBanking.exchangeCode(
    code: callbackURL.queryParam("code")!,
    redirectUri: "myapp://ob/callback",
    consentId: consent.consentId,
    codeVerifier: pkce.verifier
)

// 4. Fetch data
let accounts = try await astro.openBanking.getAccounts(
    accessToken: tokens.accessToken,
    consentId: consent.consentId
)
let txns = try await astro.openBanking.getTransactions(
    accessToken: tokens.accessToken,
    consentId: consent.consentId,
    accountId: accounts[0].accountId
)

Webhook Verification (Vapor server-side)

swift
import AstroSDK
import Vapor

let verifier = astro.webhookVerifier(
    secret: Environment.get("ASTRO_WEBHOOK_SECRET")!
)

app.post("webhooks", "astro") { req async throws -> HTTPStatus in
    let body = req.body.string ?? ""
    let sig = req.headers.first(name: "X-OpenWave-Signature") ?? ""
    
    try verifier.handle(rawBody: body, signature: sig, handlers: [
        "payment.completed": { payload in
            if let data = payload["data"] as? [String: Any],
               let ref = data["reference"] as? String {
                await db.markPaid(ref)
            }
        },
        "payment.failed": { payload in
            // handle failure
        }
    ])
    return .ok
}

Error Handling

swift
do {
    let session = try await astro.payments.createSession(...)
} catch let error as AstroError {
    switch error.code {
    case "INSUFFICIENT_FUNDS":
        showAlert("Insufficient balance")
    case "ALIAS_NOT_FOUND":
        showAlert("Alias not found")
    default:
        showAlert("Payment error: \(error.localizedDescription)")
    }
}

Source

packages/astro-swift/ in the neptune-astro repo.

Built on the OpenWave open standard.