Liest /api/v1/credits/{balance,transactions} von credits.mana.how (mana-auth-JWT),
Pendant zu @mana/mana-credits-client. baseURL überschreibbar. Basis der nativen
Gesamt-Mana-Anzeige (ManaCreditsUI).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
81 lines
2.9 KiB
Swift
81 lines
2.9 KiB
Swift
import Foundation
|
|
|
|
/// Mana-Guthaben eines Nutzers — alle Werte in Mana (1 Mana = 1 Cent).
|
|
/// Wire-Mirror von `GET /api/v1/credits/balance` (mana-credits). Das
|
|
/// Guthaben ist EIN Wert pro Nutzer, app-übergreifend — die ökosystemweite
|
|
/// „Gesamt-Mana-Anzeige".
|
|
public struct ManaCreditBalance: Decodable, Sendable, Equatable {
|
|
public let balance: Int
|
|
public let totalEarned: Int
|
|
public let totalSpent: Int
|
|
|
|
public init(balance: Int, totalEarned: Int, totalSpent: Int) {
|
|
self.balance = balance
|
|
self.totalEarned = totalEarned
|
|
self.totalSpent = totalSpent
|
|
}
|
|
}
|
|
|
|
/// Eine Mana-Bewegung. Wire-Mirror von `GET /api/v1/credits/transactions`.
|
|
/// `amount` ist vorzeichenbehaftet (Gutschrift positiv, Ausgabe negativ).
|
|
public struct ManaCreditTransaction: Decodable, Sendable, Identifiable {
|
|
public let id: String
|
|
public let type: String
|
|
public let status: String
|
|
public let amount: Int
|
|
public let balanceBefore: Int
|
|
public let balanceAfter: Int
|
|
public let appId: String
|
|
public let description: String
|
|
public let createdAt: String
|
|
public let completedAt: String?
|
|
}
|
|
|
|
/// Fehler des Credits-Clients.
|
|
public enum ManaCreditsError: Error, LocalizedError {
|
|
case http(Int)
|
|
|
|
public var errorDescription: String? {
|
|
switch self {
|
|
case let .http(code): "mana-credits antwortete mit HTTP \(code)."
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Client für das ökosystemweite Mana-Guthaben (mana-credits-Service).
|
|
///
|
|
/// Spricht standardmäßig `credits.mana.how` mit dem mana-auth-JWT — genau
|
|
/// wie das Web-Pendant `@mana/mana-credits-client`. Das Guthaben gilt
|
|
/// app-übergreifend; jede native mana-App kann es so anzeigen.
|
|
///
|
|
/// `baseURL` ist überschreibbar (App-Config / Tests); die Endpoint-Pfade
|
|
/// (`/api/v1/credits/*`) sind Verein-Plattform-Konstanten.
|
|
public struct ManaCreditsClient: Sendable {
|
|
private let transport: AuthenticatedTransport
|
|
|
|
public init(
|
|
auth: AuthClient,
|
|
baseURL: URL = URL(string: "https://credits.mana.how")!
|
|
) {
|
|
transport = AuthenticatedTransport(baseURL: baseURL, auth: auth)
|
|
}
|
|
|
|
/// `GET /api/v1/credits/balance` — aktuelles Mana-Guthaben.
|
|
public func balance() async throws -> ManaCreditBalance {
|
|
try await get("/api/v1/credits/balance")
|
|
}
|
|
|
|
/// `GET /api/v1/credits/transactions` — letzte Mana-Bewegungen.
|
|
public func transactions(limit: Int = 25, offset: Int = 0) async throws -> [ManaCreditTransaction] {
|
|
try await get("/api/v1/credits/transactions?limit=\(limit)&offset=\(offset)")
|
|
}
|
|
|
|
private func get<T: Decodable>(_ path: String) async throws -> T {
|
|
let (data, http) = try await transport.request(path: path)
|
|
guard (200 ..< 300).contains(http.statusCode) else {
|
|
throw ManaCreditsError.http(http.statusCode)
|
|
}
|
|
// JSONDecoder lokal erzeugen — hält den Client `Sendable`.
|
|
return try JSONDecoder().decode(T.self, from: data)
|
|
}
|
|
}
|