taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

WalletBackendRequest.swift (8821B)


      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     var operation: String { get }
     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 exchangeLegacyKeys = "exchange-legacy-keys"
     37         case unknown     // => type unknown, currency name taken from amount
     38 
     39         /// A scope type this app does not know must not make the whole balance list undecodable.
     40         /// NOTE: `unknown` is app-invented and wallet-core's codecForScopeInfo rejects it,
     41         /// so a degraded ScopeInfo must never be sent back as an argument.
     42         init(from decoder: Decoder) throws {
     43             let raw = try decoder.singleValueContainer().decode(String.self)
     44             self = ScopeInfoType(rawValue: raw) ?? .unknown
     45         }
     46     }
     47     var type: ScopeInfoType
     48     var currency: String        // 3-char ISO 4217 code for global currency. Regional MUST be >= 4 letters
     49     var noFees: Bool?           // only for "global". Regional have this field per Exchange
     50     var url: String?            // only for "exchange", "auditor" and "exchange-legacy-keys"
     51     var masterPub: String?      // only for "exchange-legacy-keys": the superseded key
     52 
     53     public static func zero() -> ScopeInfo {
     54         ScopeInfo(type: .unknown, currency: UNKNOWN)
     55     }
     56     public static func zero(_ currency: String) -> ScopeInfo {
     57         ScopeInfo(type: .unknown, currency: currency)
     58     }
     59     public static func < (lhs: ScopeInfo, rhs: ScopeInfo) -> Bool {
     60         if lhs.type == .global {
     61             if rhs.type == .global {        // both global ==> alphabetic currency
     62                 return lhs.currency < rhs.currency
     63             }
     64             return true                     // global comes first
     65         }
     66         if rhs.type == .global {
     67             return false                    // global comes first
     68         }
     69         if lhs.currency == rhs.currency {
     70             if let lhsBaseURL = lhs.url {
     71                 if let rhsBaseURL = rhs.url {
     72                     return lhsBaseURL < rhsBaseURL
     73                 }
     74                 return true
     75             }
     76             if rhs.url != nil {
     77                 return false
     78             }
     79             // fall thru
     80         }
     81         return lhs.currency < rhs.currency
     82     }
     83     public static func == (lhs: ScopeInfo, rhs: ScopeInfo) -> Bool {
     84         if let lhsBaseURL = lhs.url {
     85             if let rhsBaseURL = rhs.url {
     86                 if lhsBaseURL != rhsBaseURL { return false }    // different exchanges
     87                                                                 // else fall thru and check type & currency
     88             } else { return false }                             // left but not right
     89         } else if rhs.url != nil {
     90             return false                                        // right but not left
     91         }
     92         return lhs.type == rhs.type &&
     93         lhs.currency == rhs.currency &&
     94         lhs.masterPub == rhs.masterPub      // two legacy-key scopes of one exchange differ
     95     }
     96     public func hash(into hasher: inout Hasher) {
     97         hasher.combine(type)
     98         if let url {
     99             hasher.combine(url)
    100         }
    101         hasher.combine(currency)
    102         hasher.combine(masterPub)
    103     }
    104 }
    105 // MARK: -
    106 /// A billing or mailing location.
    107 struct Location: Codable, Equatable, Hashable {
    108     var country: String?
    109     var country_subdivision: String?
    110     var district: String?
    111     var town: String?
    112     var town_location: String?
    113     var post_code: String?
    114     var street: String?
    115     var building_name: String?
    116     var building_number: String?
    117     var address_lines: [String]?
    118 }
    119 
    120 /// Information identifying a merchant.
    121 struct MerchantInfo: Identifiable, Codable, Hashable, Sendable {
    122     var name: String
    123     var email: String?
    124     var website: String?
    125     var logo: String?       // ImageDataUrl
    126     var address: Location?
    127     var jurisdiction: Location?
    128 
    129     var id: String {
    130         return name + (email ?? EMPTYSTRING)
    131     }
    132 }
    133 
    134 /// A tax made on a payment.
    135 struct Tax: Codable {
    136     var name: String
    137     var tax: Amount
    138 }
    139 
    140 /// A product being purchased from a merchant.
    141 /// https://docs.taler.net/core/api-merchant.html#the-contract-terms
    142 struct Product: Codable, Identifiable {
    143     var product_id: String?
    144     var description: String
    145 //    var description_i18n:   ?
    146     var quantity: Int?
    147     var unit: String?
    148     var price: Amount?
    149     var image: String? // product image 128x128 encoded
    150     var taxes: [Tax]?
    151     var delivery_date: Timestamp?
    152 
    153     var id: String { product_id ?? "unknown" }
    154 }
    155 
    156 /// Brief information about an order.
    157 struct OrderShortInfo: Codable {
    158     var orderId: String
    159     var merchant: MerchantInfo
    160     var summary: String
    161     var summary_i18n: I18nDict?
    162     var products: [Product]?
    163     var fulfillmentUrl: String?
    164     var fulfillmentMessage: String?
    165     var fulfillmentMessage_i18n: I18nDict?
    166     var contractTermsHash: String?
    167 }
    168 // MARK: -
    169 /// A request to force update an exchange.
    170 struct WalletBackendForceUpdateRequest: WalletBackendFormattedRequest {
    171     var operation: String { "addRequest" }
    172     func args() -> Args { Args(exchangeBaseUrl: exchangeBaseUrl) }
    173 
    174     var exchangeBaseUrl: String
    175     struct Args: Encodable {
    176         var exchangeBaseUrl: String
    177     }
    178 
    179     struct Response: Decodable {}
    180 }
    181 
    182 /// A request to deposit funds.
    183 struct WalletBackendCreateDepositGroupRequest: WalletBackendFormattedRequest {
    184     var operation: String { "createDepositGroup" }
    185     func args() -> Args { Args(depositPayToUri: depositePayToUri, amount: amount) }
    186 
    187     var depositePayToUri: String
    188     var amount: Amount
    189     
    190     struct Args: Encodable {
    191         var depositPayToUri: String
    192         var amount: Amount
    193     }
    194     
    195     struct Response: Decodable {
    196         var depositGroupId: String
    197     }
    198 }
    199 
    200 /// A request to get information about a payment request.
    201 struct WalletBackendPreparePayRequest: WalletBackendFormattedRequest {
    202     var operation: String { "preparePay" }
    203     func args() -> Args { Args(talerPayUri: talerPayUri) }
    204     var talerPayUri: String
    205 
    206     struct Args: Encodable {
    207         var talerPayUri: String
    208     }
    209 
    210     struct Response: Decodable {}
    211 }
    212 
    213 // MARK: -
    214 struct IntegrationTestArgs: Codable {
    215     var exchangeBaseUrl: String
    216     var bankBaseUrl: String
    217     var merchantBaseUrl: String
    218     var merchantApiKey: String
    219     var amountToWithdraw: String
    220     var amountToSpend: String
    221 }
    222 
    223 /// A request to run a basic integration test.
    224 struct WalletBackendRunIntegrationTestRequest: WalletBackendFormattedRequest {
    225     var operation: String { "runIntegrationTest" }
    226     func args() -> Args { integrationTestArgs }
    227     var integrationTestArgs: IntegrationTestArgs
    228     
    229     typealias Args = IntegrationTestArgs
    230     
    231     struct Response: Decodable {}
    232 }
    233 
    234 struct TestPayArgs: Codable {
    235     var merchantBaseUrl: String
    236     var merchantApiKey: String
    237     var amount: String
    238     var summary: String
    239 }
    240 
    241 /// A request to make a test payment.
    242 struct WalletBackendTestPayRequest: WalletBackendFormattedRequest {
    243     var operation: String { "testPay" }
    244     func args() -> Args { testPayArgs }
    245 
    246     var testPayArgs: TestPayArgs
    247     typealias Args = TestPayArgs
    248     
    249     struct Response: Decodable {}
    250 }
    251 
    252 struct Coin: Codable {
    253     var denom_pub: String
    254     var denom_pub_hash: String
    255     var denom_value: String
    256     var coin_pub: String
    257     var exchange_base_url: String
    258     var remaining_value: String
    259     var refresh_parent_coin_pub: String
    260     var withdrawal_reserve_pub: String
    261     var coin_suspended: Bool
    262 }
    263 
    264 /// A request to dump all coins to JSON.
    265 struct WalletBackendDumpCoinsRequest: WalletBackendFormattedRequest {
    266     var operation: String { "dumpCoins" }
    267     func args() -> Args { Args() }
    268     struct Args: Encodable { }
    269 
    270     struct Response: Decodable {
    271         var coins: [Coin]
    272     }
    273     
    274 }
    275 
    276 /// A request to suspend or unsuspend a coin.
    277 struct WalletBackendSuspendCoinRequest: WalletBackendFormattedRequest {
    278     struct Response: Decodable {}
    279     var operation: String { "setCoinSuspended" }
    280     func args() -> Args { Args(coinPub: coinPub, suspended: suspended) }
    281 
    282     var coinPub: String
    283     var suspended: Bool
    284     
    285     struct Args: Encodable {
    286         var coinPub: String
    287         var suspended: Bool
    288     }
    289 }
    290 
    291