WalletBackendRequest.swift (8451B)
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 { 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 Merchant: Codable { 110 var name: String 111 var address: Location? 112 var jurisdiction: Location? 113 } 114 115 /// A tax made on a payment. 116 struct Tax: Codable { 117 var name: String 118 var tax: Amount 119 } 120 121 /// A product being purchased from a merchant. 122 /// https://docs.taler.net/core/api-merchant.html#the-contract-terms 123 struct Product: Codable, Identifiable { 124 var product_id: String? 125 var description: String 126 // var description_i18n: ? 127 var quantity: Int? 128 var unit: String? 129 var price: Amount? 130 var image: String? // product image 128x128 encoded 131 var taxes: [Tax]? 132 var delivery_date: Timestamp? 133 134 var id: String { product_id ?? "unknown" } 135 } 136 137 /// Brief information about an order. 138 struct OrderShortInfo: Codable { 139 var orderId: String 140 var merchant: Merchant 141 var summary: String 142 // var summary_i18n: ? 143 var products: [Product]? 144 var fulfillmentUrl: String? 145 var fulfillmentMessage: String? 146 // var fulfillmentMessage_i18n: ? 147 var contractTermsHash: String? 148 } 149 // MARK: - 150 /// A request to process a refund. 151 struct WalletBackendApplyRefundRequest: WalletBackendFormattedRequest { 152 func operation() -> String { "applyRefund" } 153 func args() -> Args { Args(talerRefundUri: talerRefundUri) } 154 155 var talerRefundUri: String 156 157 struct Args: Encodable { 158 var talerRefundUri: String 159 } 160 161 struct Response: Decodable { 162 var contractTermsHash: String 163 var amountEffectivePaid: Amount 164 var amountRefundGranted: Amount 165 var amountRefundGone: Amount 166 var pendingAtExchange: Bool 167 var info: OrderShortInfo 168 } 169 } 170 171 172 /// A request to force update an exchange. 173 struct WalletBackendForceUpdateRequest: WalletBackendFormattedRequest { 174 func operation() -> String { "addRequest" } 175 func args() -> Args { Args(exchangeBaseUrl: exchangeBaseUrl) } 176 177 var exchangeBaseUrl: String 178 struct Args: Encodable { 179 var exchangeBaseUrl: String 180 } 181 182 struct Response: Decodable {} 183 } 184 185 /// A request to deposit funds. 186 struct WalletBackendCreateDepositGroupRequest: WalletBackendFormattedRequest { 187 func operation() -> String { "createDepositGroup" } 188 func args() -> Args { Args(depositPayToUri: depositePayToUri, amount: amount) } 189 190 var depositePayToUri: String 191 var amount: Amount 192 193 struct Args: Encodable { 194 var depositPayToUri: String 195 var amount: Amount 196 } 197 198 struct Response: Decodable { 199 var depositGroupId: String 200 } 201 } 202 203 /// A request to get information about a payment request. 204 struct WalletBackendPreparePayRequest: WalletBackendFormattedRequest { 205 func operation() -> String { "preparePay" } 206 func args() -> Args { Args(talerPayUri: talerPayUri) } 207 var talerPayUri: String 208 209 struct Args: Encodable { 210 var talerPayUri: String 211 } 212 213 struct Response: Decodable {} 214 } 215 216 // MARK: - 217 struct IntegrationTestArgs: Codable { 218 var exchangeBaseUrl: String 219 var bankBaseUrl: String 220 var merchantBaseUrl: String 221 var merchantApiKey: String 222 var amountToWithdraw: String 223 var amountToSpend: String 224 } 225 226 /// A request to run a basic integration test. 227 struct WalletBackendRunIntegrationTestRequest: WalletBackendFormattedRequest { 228 func operation() -> String { "runIntegrationTest" } 229 func args() -> Args { integrationTestArgs } 230 var integrationTestArgs: IntegrationTestArgs 231 232 typealias Args = IntegrationTestArgs 233 234 struct Response: Decodable {} 235 } 236 237 struct TestPayArgs: Codable { 238 var merchantBaseUrl: String 239 var merchantApiKey: String 240 var amount: String 241 var summary: String 242 } 243 244 /// A request to make a test payment. 245 struct WalletBackendTestPayRequest: WalletBackendFormattedRequest { 246 func operation() -> String { "testPay" } 247 func args() -> Args { testPayArgs } 248 249 var testPayArgs: TestPayArgs 250 typealias Args = TestPayArgs 251 252 struct Response: Decodable {} 253 } 254 255 struct Coin: Codable { 256 var denom_pub: String 257 var denom_pub_hash: String 258 var denom_value: String 259 var coin_pub: String 260 var exchange_base_url: String 261 var remaining_value: String 262 var refresh_parent_coin_pub: String 263 var withdrawal_reserve_pub: String 264 var coin_suspended: Bool 265 } 266 267 /// A request to dump all coins to JSON. 268 struct WalletBackendDumpCoinsRequest: WalletBackendFormattedRequest { 269 func operation() -> String { "dumpCoins" } 270 func args() -> Args { Args() } 271 struct Args: Encodable { } 272 273 struct Response: Decodable { 274 var coins: [Coin] 275 } 276 277 } 278 279 /// A request to suspend or unsuspend a coin. 280 struct WalletBackendSuspendCoinRequest: WalletBackendFormattedRequest { 281 struct Response: Decodable {} 282 func operation() -> String { "setCoinSuspended" } 283 func args() -> Args { Args(coinPub: coinPub, suspended: suspended) } 284 285 var coinPub: String 286 var suspended: Bool 287 288 struct Args: Encodable { 289 var coinPub: String 290 var suspended: Bool 291 } 292 } 293 294