WalletBackendRequest.swift (8094B)
1 /* 2 * This file is part of GNU Taler, ©2022-25 Taler Systems S.A. 3 * See LICENSE.md 4 */ 5 /** 6 * @author Jonathan Buchanan 7 * @author Marc Stibane 8 */ 9 import Foundation 10 import AnyCodable 11 import taler_swift 12 13 /// A request sent to the wallet backend. 14 struct WalletBackendRequest: Encodable { 15 /// The operation name of the request. 16 var operation: String 17 18 /// The body of the request as JSON. 19 var args: AnyEncodable 20 } 21 22 protocol WalletBackendFormattedRequest { 23 associatedtype Args: Encodable 24 associatedtype Response: Decodable 25 26 func operation() -> String 27 func args() -> Args 28 } 29 // MARK: - 30 /// The scope of a currency: either global or a dedicated exchange 31 struct ScopeInfo: Codable, Hashable { 32 enum ScopeInfoType: String, Codable { 33 case global 34 case exchange 35 case auditor 36 case madeUp // => type unknown, currency name taken from amount 37 } 38 var type: ScopeInfoType 39 var currency: String // 3-char ISO 4217 code for global currency. Regional MUST be >= 4 letters 40 var noFees: Bool? // only for "global". Regional have this field per Exchange 41 var url: String? // only for "exchange" and "auditor" 42 43 public static func zero() -> ScopeInfo { 44 ScopeInfo(type: .madeUp, currency: UNKNOWN) 45 } 46 public static func zero(_ currency: String) -> ScopeInfo { 47 ScopeInfo(type: .madeUp, currency: currency) 48 } 49 public static func < (lhs: ScopeInfo, rhs: ScopeInfo) -> Bool { 50 if lhs.type == .global { 51 if rhs.type == .global { // both global ==> alphabetic currency 52 return lhs.currency < rhs.currency 53 } 54 return true // global comes first 55 } 56 if rhs.type == .global { 57 return false // global comes first 58 } 59 if lhs.currency == rhs.currency { 60 if let lhsBaseURL = lhs.url { 61 if let rhsBaseURL = rhs.url { 62 return lhsBaseURL < rhsBaseURL 63 } 64 return true 65 } 66 if rhs.url != nil { 67 return false 68 } 69 // fall thru 70 } 71 return lhs.currency < rhs.currency 72 } 73 public static func == (lhs: ScopeInfo, rhs: ScopeInfo) -> Bool { 74 if let lhsBaseURL = lhs.url { 75 if let rhsBaseURL = rhs.url { 76 if lhsBaseURL != rhsBaseURL { return false } // different exchanges 77 // else fall thru and check type & currency 78 } else { return false } // left but not right 79 } else if rhs.url != nil { 80 return false // right but not left 81 } 82 return lhs.type == rhs.type && 83 lhs.currency == rhs.currency 84 } 85 public func hash(into hasher: inout Hasher) { 86 hasher.combine(type) 87 if let url { 88 hasher.combine(url) 89 } 90 hasher.combine(currency) 91 } 92 } 93 // MARK: - 94 /// A billing or mailing location. 95 struct Location: Codable, Equatable, Hashable { 96 var country: String? 97 var country_subdivision: String? 98 var district: String? 99 var town: String? 100 var town_location: String? 101 var post_code: String? 102 var street: String? 103 var building_name: String? 104 var building_number: String? 105 var address_lines: [String]? 106 } 107 108 /// Information identifying a merchant. 109 struct MerchantInfo: Identifiable, Codable, Hashable, Sendable { 110 var name: String 111 var email: String? 112 var website: String? 113 var logo: String? // ImageDataUrl 114 var address: Location? 115 var jurisdiction: Location? 116 117 var id: String { 118 return name + (email ?? EMPTYSTRING) 119 } 120 } 121 122 /// A tax made on a payment. 123 struct Tax: Codable { 124 var name: String 125 var tax: Amount 126 } 127 128 /// A product being purchased from a merchant. 129 /// https://docs.taler.net/core/api-merchant.html#the-contract-terms 130 struct Product: Codable, Identifiable { 131 var product_id: String? 132 var description: String 133 // var description_i18n: ? 134 var quantity: Int? 135 var unit: String? 136 var price: Amount? 137 var image: String? // product image 128x128 encoded 138 var taxes: [Tax]? 139 var delivery_date: Timestamp? 140 141 var id: String { product_id ?? "unknown" } 142 } 143 144 /// Brief information about an order. 145 struct OrderShortInfo: Codable { 146 var orderId: String 147 var merchant: MerchantInfo 148 var summary: String 149 var summary_i18n: I18nDict? 150 var products: [Product]? 151 var fulfillmentUrl: String? 152 var fulfillmentMessage: String? 153 var fulfillmentMessage_i18n: I18nDict? 154 var contractTermsHash: String? 155 } 156 // MARK: - 157 /// A request to force update an exchange. 158 struct WalletBackendForceUpdateRequest: WalletBackendFormattedRequest { 159 func operation() -> String { "addRequest" } 160 func args() -> Args { Args(exchangeBaseUrl: exchangeBaseUrl) } 161 162 var exchangeBaseUrl: String 163 struct Args: Encodable { 164 var exchangeBaseUrl: String 165 } 166 167 struct Response: Decodable {} 168 } 169 170 /// A request to deposit funds. 171 struct WalletBackendCreateDepositGroupRequest: WalletBackendFormattedRequest { 172 func operation() -> String { "createDepositGroup" } 173 func args() -> Args { Args(depositPayToUri: depositePayToUri, amount: amount) } 174 175 var depositePayToUri: String 176 var amount: Amount 177 178 struct Args: Encodable { 179 var depositPayToUri: String 180 var amount: Amount 181 } 182 183 struct Response: Decodable { 184 var depositGroupId: String 185 } 186 } 187 188 /// A request to get information about a payment request. 189 struct WalletBackendPreparePayRequest: WalletBackendFormattedRequest { 190 func operation() -> String { "preparePay" } 191 func args() -> Args { Args(talerPayUri: talerPayUri) } 192 var talerPayUri: String 193 194 struct Args: Encodable { 195 var talerPayUri: String 196 } 197 198 struct Response: Decodable {} 199 } 200 201 // MARK: - 202 struct IntegrationTestArgs: Codable { 203 var exchangeBaseUrl: String 204 var bankBaseUrl: String 205 var merchantBaseUrl: String 206 var merchantApiKey: String 207 var amountToWithdraw: String 208 var amountToSpend: String 209 } 210 211 /// A request to run a basic integration test. 212 struct WalletBackendRunIntegrationTestRequest: WalletBackendFormattedRequest { 213 func operation() -> String { "runIntegrationTest" } 214 func args() -> Args { integrationTestArgs } 215 var integrationTestArgs: IntegrationTestArgs 216 217 typealias Args = IntegrationTestArgs 218 219 struct Response: Decodable {} 220 } 221 222 struct TestPayArgs: Codable { 223 var merchantBaseUrl: String 224 var merchantApiKey: String 225 var amount: String 226 var summary: String 227 } 228 229 /// A request to make a test payment. 230 struct WalletBackendTestPayRequest: WalletBackendFormattedRequest { 231 func operation() -> String { "testPay" } 232 func args() -> Args { testPayArgs } 233 234 var testPayArgs: TestPayArgs 235 typealias Args = TestPayArgs 236 237 struct Response: Decodable {} 238 } 239 240 struct Coin: Codable { 241 var denom_pub: String 242 var denom_pub_hash: String 243 var denom_value: String 244 var coin_pub: String 245 var exchange_base_url: String 246 var remaining_value: String 247 var refresh_parent_coin_pub: String 248 var withdrawal_reserve_pub: String 249 var coin_suspended: Bool 250 } 251 252 /// A request to dump all coins to JSON. 253 struct WalletBackendDumpCoinsRequest: WalletBackendFormattedRequest { 254 func operation() -> String { "dumpCoins" } 255 func args() -> Args { Args() } 256 struct Args: Encodable { } 257 258 struct Response: Decodable { 259 var coins: [Coin] 260 } 261 262 } 263 264 /// A request to suspend or unsuspend a coin. 265 struct WalletBackendSuspendCoinRequest: WalletBackendFormattedRequest { 266 struct Response: Decodable {} 267 func operation() -> String { "setCoinSuspended" } 268 func args() -> Args { Args(coinPub: coinPub, suspended: suspended) } 269 270 var coinPub: String 271 var suspended: Bool 272 273 struct Args: Encodable { 274 var coinPub: String 275 var suspended: Bool 276 } 277 } 278 279