gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-ios] branch master updated: add in most wallet requests


From: gnunet
Subject: [taler-taler-ios] branch master updated: add in most wallet requests
Date: Mon, 06 Sep 2021 06:52:11 +0200

This is an automated email from the git hooks/post-receive script.

jonathan-buchanan pushed a commit to branch master
in repository taler-ios.

The following commit(s) were added to refs/heads/master by this push:
     new c699856  add in most wallet requests
c699856 is described below

commit c6998563642e8574bc57385371008e7a8579c8e8
Author: Jonathan Buchanan <jonathan.russ.buchanan@gmail.com>
AuthorDate: Mon Sep 6 00:50:22 2021 -0400

    add in most wallet requests
---
 Taler.xcodeproj/project.pbxproj               |   16 +-
 Taler/BalanceList.swift                       |   76 ++
 Taler/{ContentView.swift => BalanceRow.swift} |   16 +-
 Taler/SceneDelegate.swift                     |    9 +-
 Taler/WalletBackend.swift                     | 1176 ++++++++++++++++++++++++-
 TalerTests/TimestampTests.swift               |   43 +
 6 files changed, 1293 insertions(+), 43 deletions(-)

diff --git a/Taler.xcodeproj/project.pbxproj b/Taler.xcodeproj/project.pbxproj
index 12f8c85..0943e6f 100644
--- a/Taler.xcodeproj/project.pbxproj
+++ b/Taler.xcodeproj/project.pbxproj
@@ -11,9 +11,10 @@
                D1472E5526B9206800896566 /* AmountTests.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D1472E5426B9206800896566 /* AmountTests.swift 
*/; };
                D14AFD2124D232B300C51073 /* AppDelegate.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D14AFD2024D232B300C51073 /* AppDelegate.swift 
*/; };
                D14AFD2324D232B300C51073 /* SceneDelegate.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D14AFD2224D232B300C51073 /* SceneDelegate.swift 
*/; };
-               D14AFD2524D232B300C51073 /* ContentView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D14AFD2424D232B300C51073 /* ContentView.swift 
*/; };
                D14AFD3824D232B500C51073 /* TalerTests.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D14AFD3724D232B500C51073 /* TalerTests.swift */; 
};
                D14AFD4324D232B500C51073 /* TalerUITests.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D14AFD4224D232B500C51073 /* TalerUITests.swift 
*/; };
+               D14CE1B226C39E5D00612DBE /* BalanceRow.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D14CE1B126C39E5D00612DBE /* BalanceRow.swift */; 
};
+               D14CE1B426C3A2D400612DBE /* BalanceList.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D14CE1B326C3A2D400612DBE /* BalanceList.swift 
*/; };
                D17D8B7225ADB29A001BD43D /* libbrotli.a in Frameworks */ = {isa 
= PBXBuildFile; fileRef = D17D8B4F25ADB12D001BD43D /* libbrotli.a */; };
                D17D8B7325ADB29A001BD43D /* libzlib.a in Frameworks */ = {isa = 
PBXBuildFile; fileRef = D17D8B4725ADB12B001BD43D /* libzlib.a */; };
                D17D8B7425ADB29A001BD43D /* libv8_zlib.a in Frameworks */ = 
{isa = PBXBuildFile; fileRef = D17D8B5425ADB12D001BD43D /* libv8_zlib.a */; };
@@ -34,6 +35,7 @@
                D17D8B8325ADB29B001BD43D /* libllhttp.a in Frameworks */ = {isa 
= PBXBuildFile; fileRef = D17D8B4D25ADB12C001BD43D /* libllhttp.a */; };
                D17D8B8425ADB29B001BD43D /* libhistogram.a in Frameworks */ = 
{isa = PBXBuildFile; fileRef = D17D8B5625ADB130001BD43D /* libhistogram.a */; };
                D17D8B8525ADB29B001BD43D /* libcares.a in Frameworks */ = {isa 
= PBXBuildFile; fileRef = D17D8B4825ADB12B001BD43D /* libcares.a */; };
+               D18DBB5E26DF160D00A4480D /* TimestampTests.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = D18DBB5D26DF160D00A4480D /* 
TimestampTests.swift */; };
                D1AFF0F3268D59C200FBB744 /* libiono.a in Frameworks */ = {isa = 
PBXBuildFile; fileRef = D1AFF0F2268D59A500FBB744 /* libiono.a */; };
                D1BA3F9226B8889600A5848B /* Amount.swift in Sources */ = {isa = 
PBXBuildFile; fileRef = D1BA3F9126B8889600A5848B /* Amount.swift */; };
                D1D65B9826992E4600C1012A /* WalletBackend.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = D1D65B9726992E4600C1012A /* WalletBackend.swift 
*/; };
@@ -117,7 +119,6 @@
                D14AFD1D24D232B300C51073 /* Taler.app */ = {isa = 
PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; 
path = Taler.app; sourceTree = BUILT_PRODUCTS_DIR; };
                D14AFD2024D232B300C51073 /* AppDelegate.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
AppDelegate.swift; sourceTree = "<group>"; };
                D14AFD2224D232B300C51073 /* SceneDelegate.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
SceneDelegate.swift; sourceTree = "<group>"; };
-               D14AFD2424D232B300C51073 /* ContentView.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
ContentView.swift; sourceTree = "<group>"; };
                D14AFD2624D232B500C51073 /* Assets.xcassets */ = {isa = 
PBXFileReference; lastKnownFileType = folder.assetcatalog; path = 
Assets.xcassets; sourceTree = "<group>"; };
                D14AFD2C24D232B500C51073 /* Base */ = {isa = PBXFileReference; 
lastKnownFileType = file.storyboard; name = Base; path = 
Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
                D14AFD2E24D232B500C51073 /* Info.plist */ = {isa = 
PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; 
sourceTree = "<group>"; };
@@ -127,6 +128,8 @@
                D14AFD3E24D232B500C51073 /* TalerUITests.xctest */ = {isa = 
PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path 
= TalerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
                D14AFD4224D232B500C51073 /* TalerUITests.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
TalerUITests.swift; sourceTree = "<group>"; };
                D14AFD4424D232B500C51073 /* Info.plist */ = {isa = 
PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; 
sourceTree = "<group>"; };
+               D14CE1B126C39E5D00612DBE /* BalanceRow.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
BalanceRow.swift; sourceTree = "<group>"; };
+               D14CE1B326C3A2D400612DBE /* BalanceList.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
BalanceList.swift; sourceTree = "<group>"; };
                D1595BBD25A550750049971F /* libnode.89.dylib */ = {isa = 
PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = 
libnode.89.dylib; path = "ios-node-v8/out/Debug/libnode.89.dylib"; sourceTree = 
"<group>"; };
                D1595BC625A5527C0049971F /* NodeMobile.framework */ = {isa = 
PBXFileReference; lastKnownFileType = wrapper.framework; name = 
NodeMobile.framework; path = 
"nodejs-mobile-v0.3.2-ios/Release-universal/NodeMobile.framework"; sourceTree = 
"<group>"; };
                D17D8B4425ADB12B001BD43D /* libv8_libbase.a */ = {isa = 
PBXFileReference; lastKnownFileType = archive.ar; name = libv8_libbase.a; path 
= "ios-node-v8/taler-ios-build/compiled/node-arm64/libv8_libbase.a"; sourceTree 
= "<group>"; };
@@ -149,6 +152,7 @@
                D17D8B5525ADB12E001BD43D /* libuvwasi.a */ = {isa = 
PBXFileReference; lastKnownFileType = archive.ar; name = libuvwasi.a; path = 
"ios-node-v8/taler-ios-build/compiled/node-arm64/libuvwasi.a"; sourceTree = 
"<group>"; };
                D17D8B5625ADB130001BD43D /* libhistogram.a */ = {isa = 
PBXFileReference; lastKnownFileType = archive.ar; name = libhistogram.a; path = 
"ios-node-v8/taler-ios-build/compiled/node-arm64/libhistogram.a"; sourceTree = 
"<group>"; };
                D17D8B5725ADB130001BD43D /* libtorque_base.a */ = {isa = 
PBXFileReference; lastKnownFileType = archive.ar; name = libtorque_base.a; path 
= "ios-node-v8/taler-ios-build/compiled/node-arm64/libtorque_base.a"; 
sourceTree = "<group>"; };
+               D18DBB5D26DF160D00A4480D /* TimestampTests.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
TimestampTests.swift; sourceTree = "<group>"; };
                D1AB963B259EB13D00DEAB23 /* libnode.89.dylib */ = {isa = 
PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = 
libnode.89.dylib; path = 
"ios-node-v8/taler-ios-build/compiled/x64-v8a/libnode.89.dylib"; sourceTree = 
"<group>"; };
                D1AFF0F2268D59A500FBB744 /* libiono.a */ = {isa = 
PBXFileReference; lastKnownFileType = archive.ar; name = libiono.a; path = 
iono/compiled/x64/libiono.a; sourceTree = "<group>"; };
                D1BA3F9126B8889600A5848B /* Amount.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = Amount.swift; 
sourceTree = "<group>"; };
@@ -271,7 +275,8 @@
                                D14AFD2224D232B300C51073 /* SceneDelegate.swift 
*/,
                                D1D65B9726992E4600C1012A /* WalletBackend.swift 
*/,
                                D1BA3F9126B8889600A5848B /* Amount.swift */,
-                               D14AFD2424D232B300C51073 /* ContentView.swift 
*/,
+                               D14CE1B126C39E5D00612DBE /* BalanceRow.swift */,
+                               D14CE1B326C3A2D400612DBE /* BalanceList.swift 
*/,
                                D14AFD2624D232B500C51073 /* Assets.xcassets */,
                                D14AFD2B24D232B500C51073 /* 
LaunchScreen.storyboard */,
                                D14AFD2E24D232B500C51073 /* Info.plist */,
@@ -285,6 +290,7 @@
                                D14AFD3724D232B500C51073 /* TalerTests.swift */,
                                D14AFD3924D232B500C51073 /* Info.plist */,
                                D1472E5426B9206800896566 /* AmountTests.swift 
*/,
+                               D18DBB5D26DF160D00A4480D /* 
TimestampTests.swift */,
                        );
                        path = TalerTests;
                        sourceTree = "<group>";
@@ -588,8 +594,9 @@
                                D1BA3F9226B8889600A5848B /* Amount.swift in 
Sources */,
                                D14AFD2124D232B300C51073 /* AppDelegate.swift 
in Sources */,
                                D14AFD2324D232B300C51073 /* SceneDelegate.swift 
in Sources */,
-                               D14AFD2524D232B300C51073 /* ContentView.swift 
in Sources */,
                                D1D65B9826992E4600C1012A /* WalletBackend.swift 
in Sources */,
+                               D14CE1B426C3A2D400612DBE /* BalanceList.swift 
in Sources */,
+                               D14CE1B226C39E5D00612DBE /* BalanceRow.swift in 
Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
@@ -599,6 +606,7 @@
                        files = (
                                D14AFD3824D232B500C51073 /* TalerTests.swift in 
Sources */,
                                D1472E5526B9206800896566 /* AmountTests.swift 
in Sources */,
+                               D18DBB5E26DF160D00A4480D /* 
TimestampTests.swift in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
diff --git a/Taler/BalanceList.swift b/Taler/BalanceList.swift
new file mode 100644
index 0000000..5e77b6c
--- /dev/null
+++ b/Taler/BalanceList.swift
@@ -0,0 +1,76 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2021 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under 
the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+ * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+import SwiftUI
+
+struct IdentifiedArray<T>: RandomAccessCollection {
+    struct Item {
+        var id: Int
+        var item: T
+    }
+    
+    typealias Element = Item
+    typealias ItemArray = [Item]
+    typealias Index = ItemArray.Index
+    typealias SubSequence = IdentifiedArray<T>
+    typealias Indices = ItemArray.Indices
+    
+    var items: ItemArray
+    var startIndex: ItemArray.Index {
+        get {
+            return items.startIndex
+        }
+    }
+    var endIndex: ItemArray.Index {
+        get {
+            return items.endIndex
+        }
+    }
+    
+    subscript(position: ItemArray.Index) -> Element {
+        get {
+            return items[position]
+        }
+        set(value) {
+            items[position] = value
+        }
+    }
+    
+    init(_ array: [T]) {
+        self.items = array.enumerated().map({ (index, element) in
+            return Item(id: index, item: element)
+        })
+    }
+}
+
+struct BalanceList: View {
+    var balances: IdentifiedArray<Balance>
+    
+    var body: some View {
+        List(balances, id: \.id) { balance in
+            BalanceRow(balance: balance.item)
+        }
+    }
+}
+
+struct BalanceList_Previews: PreviewProvider {
+    static var previews: some View {
+        try! BalanceList(balances: IdentifiedArray<Balance>([
+            Balance(available: Amount(fromString: "USD:0.01"), 
pendingIncoming: Amount(fromString: "USD:0.02"), pendingOutgoing: 
Amount(fromString: "USD:0.03"), requiresUserInput: true),
+            Balance(available: Amount(fromString: "EUR:0.02"), 
pendingIncoming: Amount(fromString: "EUR:0.01"), pendingOutgoing: 
Amount(fromString: "EUR:0.03"), requiresUserInput: false)
+        ]))
+    }
+}
diff --git a/Taler/ContentView.swift b/Taler/BalanceRow.swift
similarity index 52%
rename from Taler/ContentView.swift
rename to Taler/BalanceRow.swift
index 4406fc3..405a141 100644
--- a/Taler/ContentView.swift
+++ b/Taler/BalanceRow.swift
@@ -16,14 +16,22 @@
 
 import SwiftUI
 
-struct ContentView: View {
+struct BalanceRow: View {
+    var balance: Balance
+    
     var body: some View {
-        Text("Hello, World!")
+        VStack(alignment: .leading, spacing: 
/*@START_MENU_TOKEN@*/nil/*@END_MENU_TOKEN@*/, content: {
+            Text("Available: \(balance.available.description)")
+            Text("Pending Incoming: \(balance.pendingIncoming.description)")
+            Text("Pending Outgoing: \(balance.pendingOutgoing.description)")
+            Text("Requires User Input: 
\(balance.requiresUserInput.description)")
+        })
+        .padding()
     }
 }
 
-struct ContentView_Previews: PreviewProvider {
+struct BalanceRow_Previews: PreviewProvider {
     static var previews: some View {
-        ContentView()
+        try! BalanceRow(balance: Balance(available: Amount(fromString: 
"USD:0.01"), pendingIncoming: Amount(fromString: "USD:0.02"), pendingOutgoing: 
Amount(fromString: "USD:0.03"), requiresUserInput: true))
     }
 }
diff --git a/Taler/SceneDelegate.swift b/Taler/SceneDelegate.swift
index a895f65..79b086e 100644
--- a/Taler/SceneDelegate.swift
+++ b/Taler/SceneDelegate.swift
@@ -27,15 +27,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
         // If using a storyboard, the `window` property will automatically be 
initialized and attached to the scene.
         // This delegate does not imply the connecting scene or session are 
new (see `application:configurationForConnectingSceneSession` instead).
 
-        // Create the SwiftUI view that provides the window contents.
-        let contentView = ContentView()
-
         // Use a UIHostingController as window root view controller.
         if let windowScene = scene as? UIWindowScene {
-            let window = UIWindow(windowScene: windowScene)
-            window.rootViewController = UIHostingController(rootView: 
contentView)
+            /*let window = UIWindow(windowScene: windowScene)
+            window.rootViewController = UIHostingController(rootView: /* */)
             self.window = window
-            window.makeKeyAndVisible()
+            window.makeKeyAndVisible()*/
         }
     }
 
diff --git a/Taler/WalletBackend.swift b/Taler/WalletBackend.swift
index d5cc3ab..f1b462f 100644
--- a/Taler/WalletBackend.swift
+++ b/Taler/WalletBackend.swift
@@ -83,80 +83,1021 @@ fileprivate struct WalletBackendInitRequest: 
WalletBackendRequest {
     }
 }
 
-fileprivate struct WalletBackendGetTransactionsRequest: WalletBackendRequest {
+/**
+    An balance on a wallet.
+ */
+struct Balance: Decodable {
+    var available: Amount
+    var pendingIncoming: Amount
+    var pendingOutgoing: Amount
+    var requiresUserInput: Bool
+}
+
+/**
+    A request to get the balances held in the wallet.
+ */
+class WalletBackendGetBalancesRequest: WalletBackendRequest {
+    struct BalancesResponse: Decodable {
+        var balances: [Balance]
+    }
+    struct RequestArgs: Encodable {
+        
+    }
+    typealias Args = RequestArgs
+    typealias Response = BalancesResponse
+    private var requestArgs: RequestArgs
+    private let success: ([Balance]) -> Void
+    private let failure: () -> Void
+    
+    init(onSuccess: @escaping ([Balance]) -> Void, onFailure: @escaping () -> 
Void) {
+        self.requestArgs = RequestArgs()
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "getBalances"
+    }
+    
+    func args() -> Args {
+        return requestArgs
+    }
+    
+    func success(result: Response) {
+        self.success(result.balances)
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A timestamp, measured in milliseconds elapsed from an epoch.
+ */
+enum TimestampError: Error {
+    case invalidString
+}
+
+enum Timestamp: Codable, Equatable {
+    case millisecondsSinceEpoch(Int)
+    case never
+    
+    enum CodingKeys: String, CodingKey {
+        case t_ms = "t_ms"
+    }
+    
+    init(from decoder: Decoder) throws {
+        let container = try decoder.container(keyedBy: CodingKeys.self)
+        do {
+            self = Timestamp.millisecondsSinceEpoch(try 
container.decode(Int.self, forKey: .t_ms))
+        } catch {
+            let stringValue = try container.decode(String.self, forKey: .t_ms)
+            if stringValue == "never" {
+                self = Timestamp.never
+            } else {
+                throw TimestampError.invalidString
+            }
+        }
+    }
+    
+    func encode(to encoder: Encoder) throws {
+        var value = encoder.singleValueContainer()
+        switch self {
+        case .millisecondsSinceEpoch(let t_ms):
+            try value.encode(t_ms)
+        case .never:
+            try value.encode("never")
+        }
+    }
+}
+
+/**
+    A billing or mailing location.
+ */
+struct Location: Codable {
+    var country: String?
+    var country_subdivision: String?
+    var district: String?
+    var town: String?
+    var town_location: String?
+    var post_code: String?
+    var street: String?
+    var building_name: String?
+    var building_number: String?
+    var address_lines: [String]?
+}
+
+/**
+    Information identifying a merchant.
+ */
+struct Merchant: Codable {
+    var name: String
+    var address: Location?
+    var jurisdiction: Location?
+}
+
+/**
+    A tax made on a payment.
+ */
+struct Tax: Codable {
+    var name: String
+    var tax: Amount
+}
+
+/**
+    A product being purchased from a merchant.
+ */
+struct Product: Codable {
+    var product_id: String?
+    var description: String
+    // description_i18n?
+    var quantity: Int
+    var unit: String
+    var price: Amount?
+    var image: String // URL to a product image
+    var taxes: [Tax]?
+    var delivery_date: Timestamp?
+}
+
+/**
+    Brief information about an order.
+ */
+struct OrderShortInfo: Codable {
+    var orderId: String
+    var merchant: Merchant
+    var summary: String
+    // summary_i18n?
+    var products: [Product]
+    var fulfillmentUrl: String?
+    var fulfillmentMessage: String?
+    // fulfillmentMessage_i18n?
+}
+
+enum TransactionTypeError: Error {
+    case unknownTypeError
+}
+
+/**
+    Different types of transactions.
+ */
+enum TransactionType: Codable {
+    case withdrawal
+    case payment
+    case refund
+    case tip
+    case refresh
+    
+    init(from decoder: Decoder) throws {
+        let value = try decoder.singleValueContainer()
+        let str = try value.decode(String.self)
+        let codingNames = [
+            "TransactionWithdrawal" : TransactionType.withdrawal,
+            "TransactionPayment" : TransactionType.payment,
+            "TransactionRefund" : TransactionType.refund,
+            "TransactionTip" : TransactionType.tip,
+            "TransactionRefresh" : TransactionType.refresh
+        ]
+        if let type = codingNames[str] {
+            self = type
+        } else {
+            throw TransactionTypeError.unknownTypeError
+        }
+    }
+    
+    func encode(to encoder: Encoder) throws {
+        var value = encoder.singleValueContainer()
+        switch self {
+        case .withdrawal:
+            try value.encode("TransactionWithdrawal")
+        case .payment:
+            try value.encode("TransactionPayment")
+        case .refund:
+            try value.encode("TransactionRefund")
+        case .tip:
+            try value.encode("TransactionTip")
+        case .refresh:
+            try value.encode("TransactionRefresh")
+        }
+    }
+}
+
+/**
+    An error associated with a transaction.
+ */
+struct TransactionError: Codable {
+    var ec: Int
+    var hint: String?
+    //var details: Any?
+}
+
+/**
+    A wallet transaction.
+ */
+struct Transaction: Codable {
+    var transactionId: String
+    var type: TransactionType
+    var timestamp: Timestamp
+    var pending: Bool
+    var error: TransactionError?
+    var amountRaw: Amount
+    var amountEffective: Amount
+}
+
+/**
+    A request to get the transactions in the wallet's history.
+ */
+class WalletBackendGetTransactionsRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var currency: String?
+        var search: String?
+    }
+    struct TransactionsResponse: Decodable {
+        
+    }
+    typealias Args = RequestArgs
+    typealias Response = TransactionsResponse
+    private var requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(onSuccess: @escaping () -> Void, onFailure: @escaping () -> Void) {
+        self.requestArgs = RequestArgs(currency: nil, search: nil)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    init(filterCurrency: String, onSuccess: @escaping () -> Void, onFailure: 
@escaping () -> Void) {
+        self.requestArgs = RequestArgs(currency: filterCurrency, search: nil)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    init(searchString: String, onSuccess: @escaping () -> Void, onFailure: 
@escaping () -> Void) {
+        self.requestArgs = RequestArgs(currency: nil, search: searchString)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    init(filterCurrency: String, searchString: String, onSuccess: @escaping () 
-> Void, onFailure: @escaping () -> Void) {
+        self.requestArgs = RequestArgs(currency: filterCurrency, search: 
searchString)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "getTransactions"
+    }
+    
+    func args() -> Args {
+        return requestArgs
+    }
+    
+    func success(result: Response) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to delete a wallet transaction by ID.
+ */
+class WalletBackendDeleteTransactionRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var transactionId: String
+    }
+    struct RequestResponse: Decodable {
+        
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private var requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(transactionID: String, onSuccess: @escaping () -> Void, onFailure: 
@escaping () -> Void) {
+        self.requestArgs = RequestArgs(transactionId: transactionID)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "deleteTransaction"
+    }
+    
+    func args() -> Args {
+        return requestArgs
+    }
+    
+    func success(result: Response) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to process a refund.
+ */
+class WalletBackendApplyRefundRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var talerRefundUri: String
+    }
+    struct RequestResponse: Decodable {
+        var contractTermsHash: String
+        var amountEffectivePaid: Amount
+        var amountRefundGranted: Amount
+        var amountRefundGone: Amount
+        var pendingAtExchange: Bool
+        var info: OrderShortInfo
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: (RequestResponse) -> Void
+    private let failure: () -> Void
+    
+    init(refundURI: String, onSuccess: @escaping (RequestResponse) -> Void, 
onFailure: @escaping () -> Void) {
+        self.requestArgs = RequestArgs(talerRefundUri: refundURI)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "applyRefund"
+    }
+    
+    func args() -> Args {
+        return requestArgs
+    }
+    
+    func success(result: Response) {
+        self.success(result)
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to list exchanges.
+ */
+class WalletBackendListExchanges: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        
+    }
+    struct ExchangeListItem: Decodable {
+        var exchangeBaseUrl: String
+        var currency: String
+        var paytoUris: [String]
+    }
+    struct RequestResponse: Decodable {
+        var exchanges: [ExchangeListItem]
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let success: ([ExchangeListItem]) -> Void
+    private let failure: () -> Void
+    
+    init(onSuccess: @escaping ([ExchangeListItem]) -> Void, onFailure: 
@escaping () -> Void) {
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "listExchanges"
+    }
+    
+    func args() -> RequestArgs {
+        return RequestArgs()
+    }
+    
+    func success(result: RequestResponse) {
+        self.success(result.exchanges)
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to add an exchange.
+ */
+class WalletBackendAddRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var exchangeBaseUrl: String
+    }
+    struct RequestResponse: Decodable {
+        
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(exchangeBaseURL: String, onSuccess: @escaping () -> Void, onFailure: 
@escaping () -> Void) {
+        self.requestArgs = RequestArgs(exchangeBaseUrl: exchangeBaseURL)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "addRequest"
+    }
+    
+    func args() -> RequestArgs {
+        return requestArgs
+    }
+    
+    func success(result: RequestResponse) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to force update an exchange.
+ */
+class WalletBackendForceUpdateRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var exchangeBaseUrl: String
+    }
+    struct RequestResponse: Decodable {
+        
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(exchangeBaseURL: String, onSuccess: @escaping () -> Void, onFailure: 
@escaping () -> Void) {
+        self.requestArgs = RequestArgs(exchangeBaseUrl: exchangeBaseURL)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "addRequest"
+    }
+    
+    func args() -> RequestArgs {
+        return requestArgs
+    }
+    
+    func success(result: RequestResponse) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to query an exchange's terms of service.
+ */
+class WalletBackendGetExchangeTermsOfService: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var exchangeBaseUrl: String
+    }
+    struct RequestResponse: Decodable {
+        var tos: String
+        var currentEtag: String
+        var acceptedEtag: String
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(exchangeBaseURL: String, onSuccess: @escaping () -> Void, onFailure: 
@escaping () -> Void) {
+        self.requestArgs = RequestArgs(exchangeBaseUrl: exchangeBaseURL)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "getExchangeTos"
+    }
+    
+    func args() -> RequestArgs {
+        return requestArgs
+    }
+    
+    func success(result: RequestResponse) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to mark an exchange's terms of service as accepted.
+ */
+class WalletBackendSetExchangeTermsOfServiceAccepted: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var exchangeBaseUrl: String
+        var acceptedEtag: String
+    }
+    struct RequestResponse: Decodable {
+        
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(exchangeBaseURL: String, acceptedEtag: String, onSuccess: @escaping 
() -> Void, onFailure: @escaping () -> Void) {
+        self.requestArgs = RequestArgs(exchangeBaseUrl: exchangeBaseURL, 
acceptedEtag: acceptedEtag)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "setExchangeTosAccepted"
+    }
+    
+    func args() -> RequestArgs {
+        return requestArgs
+    }
+    
+    func success(result: RequestResponse) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+struct ExchangeListItem: Codable {
+    var exchangeBaseUrl: String
+    var currency: String
+    var paytoUris: [String]
+}
+
+/**
+    A request to get an exchange's withdrawal details.
+ */
+class WalletBackendGetWithdrawalDetailsForURIRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var talerWithdrawUri: String
+    }
+    struct RequestResponse: Decodable {
+        var amount: Amount
+        var defaultExchangeBaseUrl: String?
+        var possibleExchanges: [ExchangeListItem]
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(talerWithdrawUri: String, onSuccess: @escaping () -> Void, onFailure: 
@escaping () -> Void) {
+        self.requestArgs = RequestArgs(talerWithdrawUri: talerWithdrawUri)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "getWithdrawalDetailsForUri"
+    }
+    
+    func args() -> RequestArgs {
+        return requestArgs
+    }
+    
+    func success(result: RequestResponse) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to get an exchange's withdrawal details.
+ */
+class WalletBackendGetWithdrawalDetailsForAmountRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var exchangeBaseUrl: String
+        var amount: Amount
+    }
+    struct RequestResponse: Decodable {
+        var tosAccepted: Bool
+        var amountRaw: Amount
+        var amountEffective: Amount
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(exchangeBaseURL: String, amount: Amount, onSuccess: @escaping () -> 
Void, onFailure: @escaping () -> Void) {
+        self.requestArgs = RequestArgs(exchangeBaseUrl: exchangeBaseURL, 
amount: amount)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "getWithdrawalDetailsForAmount"
+    }
+    
+    func args() -> RequestArgs {
+        return requestArgs
+    }
+    
+    func success(result: RequestResponse) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to accept a bank-integrated withdrawl.
+ */
+class WalletBackendAcceptBankIntegratedWithdrawalRequest: WalletBackendRequest 
{
+    struct RequestArgs: Encodable {
+        var talerWithdrawUri: String
+        var exchangeBaseUrl: String
+    }
+    struct RequestResponse: Decodable {
+        var bankConfirmationUrl: String?
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(talerWithdrawURI: String, exchangeBaseURL: String, onSuccess: 
@escaping () -> Void, onFailure: @escaping () -> Void) {
+        self.requestArgs = RequestArgs(talerWithdrawUri: talerWithdrawURI, 
exchangeBaseUrl: exchangeBaseURL)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "acceptWithdrawal"
+    }
+    
+    func args() -> RequestArgs {
+        return requestArgs
+    }
+    
+    func success(result: RequestResponse) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to accept a manual withdrawl.
+ */
+class WalletBackendAcceptManualWithdrawalRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var exchangeBaseUrl: String
+        var amount: Amount
+    }
+    struct RequestResponse: Decodable {
+        var exchangePaytoUris: [String]
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(exchangeBaseURL: String, amount: Amount, onSuccess: @escaping () -> 
Void, onFailure: @escaping () -> Void) {
+        self.requestArgs = RequestArgs(exchangeBaseUrl: exchangeBaseURL, 
amount: amount)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "acceptManualWithdrawal"
+    }
+    
+    func args() -> RequestArgs {
+        return requestArgs
+    }
+    
+    func success(result: RequestResponse) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to deposit funds.
+ */
+class WalletBackendCreateDepositGroupRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var depositPayToUri: String
+        var amount: Amount
+    }
+    struct RequestResponse: Decodable {
+        var depositGroupId: String
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(depositPayToUri: String, amount: Amount, onSuccess: @escaping () -> 
Void, onFailure: @escaping () -> Void) {
+        self.requestArgs = RequestArgs(depositPayToUri: depositPayToUri, 
amount: amount)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "createDepositGroup"
+    }
+    
+    func args() -> RequestArgs {
+        return requestArgs
+    }
+    
+    func success(result: RequestResponse) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to get information about a payment request.
+ */
+class WalletBackendPreparePayRequest: WalletBackendRequest {
     struct RequestArgs: Encodable {
+        var talerPayUri: String
+    }
+    struct RequestResponse: Decodable {
         
     }
     typealias Args = RequestArgs
-    typealias Response = String
-    private var requestArgs: RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
     
-    init() {
-        requestArgs = RequestArgs()
+    init(talerPayUri: String, onSuccess: @escaping () -> Void, onFailure: 
@escaping () -> Void) {
+        self.requestArgs = RequestArgs(talerPayUri: talerPayUri)
+        self.success = onSuccess
+        self.failure = onFailure
     }
     
     func operation() -> String {
-        return "getTransactions"
+        return "preparePay"
     }
     
-    func args() -> Args {
+    func args() -> RequestArgs {
         return requestArgs
     }
     
-    func success(result: Response) {
+    func success(result: RequestResponse) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to confirm a payment.
+ */
+class WalletBackendConfirmPayRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var proposalId: String
+    }
+    struct RequestResponse: Decodable {
         
     }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(proposalId: String, onSuccess: @escaping () -> Void, onFailure: 
@escaping () -> Void) {
+        self.requestArgs = RequestArgs(proposalId: proposalId)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "abortFailedPayWithRefund"
+    }
+    
+    func args() -> RequestArgs {
+        return requestArgs
+    }
+    
+    func success(result: RequestResponse) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to prepare a tip.
+ */
+class WalletBackendPrepareTipRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var talerTipUri: String
+    }
+    struct RequestResponse: Decodable {
+        var walletTipId: String
+        var accepted: Bool
+        var tipAmountRaw: Amount
+        var tipAmountEffective: Amount
+        var exchangeBaseUrl: String
+        var expirationTimestamp: Timestamp
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(talerTipURI: String, onSuccess: @escaping () -> Void, onFailure: 
@escaping () -> Void) {
+        self.requestArgs = RequestArgs(talerTipUri: talerTipURI)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "prepareTip"
+    }
+    
+    func args() -> RequestArgs {
+        return requestArgs
+    }
+    
+    func success(result: RequestResponse) {
+        self.success()
+    }
     
     func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to accept a tip.
+ */
+class WalletBackendAcceptTipRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var walletTipId: String
+    }
+    struct RequestResponse: Decodable {
         
     }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(walletTipId: String, onSuccess: @escaping () -> Void, onFailure: 
@escaping () -> Void) {
+        self.requestArgs = RequestArgs(walletTipId: walletTipId)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "acceptTip"
+    }
+    
+    func args() -> RequestArgs {
+        return requestArgs
+    }
+    
+    func success(result: RequestResponse) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
 }
 
-struct WalletBackendGetBalancesRequest: WalletBackendRequest {
-    struct Balance: Decodable {
-        var available: Amount
-        var pendingIncoming: Amount
-        var pendingOutgoing: Amount
-        var requiresUserInput: Bool
+/**
+    A request to abort a failed payment.
+ */
+class WalletBackendAbortFailedPaymentRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var proposalId: String
     }
-    struct BalancesResponse: Decodable {
-        var balances: [Balance]
+    struct RequestResponse: Decodable {
+        
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(proposalId: String, onSuccess: @escaping () -> Void, onFailure: 
@escaping () -> Void) {
+        self.requestArgs = RequestArgs(proposalId: proposalId)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "confirmPay"
+    }
+    
+    func args() -> RequestArgs {
+        return requestArgs
+    }
+    
+    func success(result: RequestResponse) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
     }
+}
+
+/**
+    A request to withdraw a balance from the TESTKUDOS environment.
+ */
+class WalletBackendWithdrawTestkudosRequest: WalletBackendRequest {
     struct RequestArgs: Encodable {
         
+    }
+    struct RequestResponse: Decodable {
+        
     }
     typealias Args = RequestArgs
-    typealias Response = BalancesResponse
-    private var requestArgs: RequestArgs
-    private let success: ([Balance]) -> Void
+    typealias Response = RequestResponse
+    private let success: () -> Void
     private let failure: () -> Void
     
-    init(onSuccess: @escaping ([Balance]) -> Void, onFailure: @escaping () -> 
Void) {
-        self.requestArgs = RequestArgs()
+    init(onSuccess: @escaping () -> Void, onFailure: @escaping () -> Void) {
         self.success = onSuccess
         self.failure = onFailure
     }
     
     func operation() -> String {
-        return "getBalances"
+        return "withdrawTestkudos"
     }
     
     func args() -> Args {
-        return requestArgs
+        return RequestArgs()
     }
     
     func success(result: Response) {
-        print("balances???")
+        self.success()
     }
     
     func error(_ err: WalletBackendResponseError) {
-        
+        self.failure()
     }
 }
 
-struct WalletBackendWithdrawTestBalance: WalletBackendRequest {
+/**
+    A request to add a test balance to the wallet.
+ */
+class WalletBackendWithdrawTestBalance: WalletBackendRequest {
     struct RequestArgs: Encodable {
-        var amount: String
+        var amount: Amount
         var bankBaseUrl: String
         var exchangeBaseUrl: String
     }
@@ -164,7 +1105,7 @@ struct WalletBackendWithdrawTestBalance: 
WalletBackendRequest {
     typealias Response = String
     private var requestArgs: RequestArgs
     
-    init(amount: String, bankBaseUrl: String, exchangeBaseUrl: String) {
+    init(amount: Amount, bankBaseUrl: String, exchangeBaseUrl: String) {
         requestArgs = RequestArgs(amount: amount, bankBaseUrl: bankBaseUrl, 
exchangeBaseUrl: exchangeBaseUrl)
     }
     
@@ -185,6 +1126,183 @@ struct WalletBackendWithdrawTestBalance: 
WalletBackendRequest {
     }
 }
 
+struct IntegrationTestArgs: Codable {
+    var exchangeBaseUrl: String
+    var bankBaseUrl: String
+    var merchantBaseUrl: String
+    var merchantApiKey: String
+    var amountToWithdraw: String
+    var amountToSpend: String
+}
+
+/**
+    A request to run a basic integration test.
+ */
+class WalletBackendRunIntegrationTestRequest: WalletBackendRequest {
+    struct RequestResponse: Decodable {
+        
+    }
+    typealias Args = IntegrationTestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: IntegrationTestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(args: IntegrationTestArgs, onSuccess: @escaping () -> Void, 
onFailure: @escaping () -> Void) {
+        self.requestArgs = args
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "runIntegrationTest"
+    }
+    
+    func args() -> Args {
+        return requestArgs
+    }
+    
+    func success(result: Response) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+struct TestPayArgs: Codable {
+    var merchantBaseUrl: String
+    var merchantApiKey: String
+    var amount: String
+    var summary: String
+}
+
+/**
+    A request to make a test payment.
+ */
+class WalletBackendTestPayRequest: WalletBackendRequest {
+    struct RequestResponse: Decodable {
+        
+    }
+    typealias Args = TestPayArgs
+    typealias Response = RequestResponse
+    private let requestArgs: TestPayArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(args: TestPayArgs, onSuccess: @escaping () -> Void, onFailure: 
@escaping () -> Void) {
+        self.requestArgs = args
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "testPay"
+    }
+    
+    func args() -> Args {
+        return requestArgs
+    }
+    
+    func success(result: Response) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+struct Coin: Codable {
+    var denom_pub: String
+    var denom_pub_hash: String
+    var denom_value: String
+    var coin_pub: String
+    var exchange_base_url: String
+    var remaining_value: String
+    var refresh_parent_coin_pub: String
+    var withdrawal_reserve_pub: String
+    var coin_suspended: Bool
+}
+
+/**
+    A request to dump all coins to JSON.
+ */
+class WalletBackendDumpCoinsRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        
+    }
+    struct RequestResponse: Decodable {
+        var coins: [Coin]
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(onSuccess: @escaping () -> Void, onFailure: @escaping () -> Void) {
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "dumpCoins"
+    }
+    
+    func args() -> Args {
+        return RequestArgs()
+    }
+    
+    func success(result: Response) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
+/**
+    A request to suspend or unsuspend a coin.
+ */
+class WalletBackendSuspendCoinRequest: WalletBackendRequest {
+    struct RequestArgs: Encodable {
+        var coinPub: String
+        var suspended: Bool
+    }
+    struct RequestResponse: Decodable {
+        
+    }
+    typealias Args = RequestArgs
+    typealias Response = RequestResponse
+    private let requestArgs: RequestArgs
+    private let success: () -> Void
+    private let failure: () -> Void
+    
+    init(coinPub: String, suspended: Bool, onSuccess: @escaping () -> Void, 
onFailure: @escaping () -> Void) {
+        self.requestArgs = RequestArgs(coinPub: coinPub, suspended: suspended)
+        self.success = onSuccess
+        self.failure = onFailure
+    }
+    
+    func operation() -> String {
+        return "setCoinSuspended"
+    }
+    
+    func args() -> Args {
+        return requestArgs
+    }
+    
+    func success(result: Response) {
+        self.success()
+    }
+    
+    func error(_ err: WalletBackendResponseError) {
+        self.failure()
+    }
+}
+
 enum WalletBackendError: Error {
     case serializationError
     case deserializationError
@@ -234,7 +1352,7 @@ class WalletBackend: IonoMessageHandler {
         
         waitUntilReady()
         //try! sendRequest(request: WalletBackendWithdrawTestBalance(amount: 
"TESTKUDOS:10", bankBaseUrl: "https://bank.test.taler.net/";, exchangeBaseUrl: 
"https://exchange.test.taler.net/";))
-        try! sendRequest(request: WalletBackendGetBalancesRequest(onSuccess: { 
([WalletBackendGetBalancesRequest.Balance]) -> Void in
+        try! sendRequest(request: WalletBackendGetBalancesRequest(onSuccess: { 
([Balance]) -> Void in
             
         }, onFailure: { () -> Void in
             
diff --git a/TalerTests/TimestampTests.swift b/TalerTests/TimestampTests.swift
new file mode 100644
index 0000000..d2b2433
--- /dev/null
+++ b/TalerTests/TimestampTests.swift
@@ -0,0 +1,43 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2021 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under 
the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+ * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+import XCTest
+@testable import Taler
+
+class TimestampTests: XCTestCase {
+
+    override func setUpWithError() throws {
+        // Put setup code here. This method is called before the invocation of 
each test method in the class.
+    }
+
+    override func tearDownWithError() throws {
+        // Put teardown code here. This method is called after the invocation 
of each test method in the class.
+    }
+
+    func testTimestamps() throws {
+        let json1 = "{ \"t_ms\" : 12309487 }".data(using: .utf8)!
+        let json2 = "{ \"t_ms\" : \"never\" }".data(using: .utf8)!
+        let json3 = "{ \"t_ms\" : \"sometime\" }".data(using: .utf8)!
+        
+        var t: Timestamp = try! JSONDecoder().decode(Timestamp.self, from: 
json1)
+        XCTAssertEqual(t, Timestamp.millisecondsSinceEpoch(12309487))
+        
+        t = try! JSONDecoder().decode(Timestamp.self, from: json2)
+        XCTAssertEqual(t, Timestamp.never)
+        XCTAssertThrowsError(t = try JSONDecoder().decode(Timestamp.self, 
from: json3))
+    }
+
+}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]