gnunet-svn
[Top][All Lists]
Advanced

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

[taler-cashless2ecash] branch master updated: code: add bank integration


From: gnunet
Subject: [taler-cashless2ecash] branch master updated: code: add bank integration and wirewatch gateway api
Date: Thu, 14 Mar 2024 16:22:42 +0100

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

joel-haeberli pushed a commit to branch master
in repository cashless2ecash.

The following commit(s) were added to refs/heads/master by this push:
     new 552d2fd  code: add bank integration and wirewatch gateway api
552d2fd is described below

commit 552d2fde8cf2f2c1db7f2cf84e78f7ef90202a35
Author: Joel-Haeberli <haebu@rubigen.ch>
AuthorDate: Thu Mar 14 16:22:19 2024 +0100

    code: add bank integration and wirewatch gateway api
---
 docs/Makefile                                     |   2 +-
 docs/content/appendix/meeting_notes.tex           |  30 +--
 docs/content/architecture/exchange.tex            |   0
 docs/content/architecture/nonce2ecash.tex         |  62 +++---
 docs/content/architecture/overview.tex            |   8 +
 docs/content/context/context.tex                  |   4 +
 docs/content/context/taler.tex                    |  11 ++
 docs/content/context/wallee.tex                   |   5 +
 docs/content/introduction/goal.tex                |   6 +-
 docs/content/introduction/introduction.tex        |  38 +---
 docs/thesis.pdf                                   | Bin 1292636 -> 1284387 
bytes
 docs/thesis.tex                                   |   3 +
 nonce2ecash/go.mod                                |  13 +-
 nonce2ecash/go.sum                                |  25 +++
 nonce2ecash/pkg/common/codec.go                   |   6 +-
 nonce2ecash/pkg/common/http-util.go               |  72 +++++--
 nonce2ecash/pkg/common/http-util_test.go          |  62 ++++++
 nonce2ecash/pkg/common/model.go                   |  21 +-
 nonce2ecash/pkg/db/db.go                          |  66 +++++++
 nonce2ecash/pkg/db/provider.go                    |  22 +++
 nonce2ecash/pkg/db/withdrawal.go                  |  29 +++
 nonce2ecash/pkg/handler.go                        |   7 +-
 nonce2ecash/pkg/taler-bank-integration/client.go  |  43 ++++-
 nonce2ecash/pkg/taler-bank-integration/model.go   |  19 ++
 nonce2ecash/pkg/taler-wirewatch-gateway/client.go | 225 ++++++++++++++++++++++
 nonce2ecash/pkg/taler-wirewatch-gateway/model.go  |  92 +++++++++
 specs/api-nonce2ecash.rst                         |  50 +++--
 specs/overview.plantuml                           |  40 ++++
 28 files changed, 834 insertions(+), 127 deletions(-)

diff --git a/docs/Makefile b/docs/Makefile
index 1ee4041..c906724 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -5,7 +5,7 @@ pdf:
 
 clean-all:
        latexmk -C
-       rm -Rf *.glg *.glo *.gls *.ist *.lol *.bbl
+       rm -Rf *.glg *.glo *.gls *.ist *.lol *.bbl *.aux
 
 clean:
        latexmk -c
\ No newline at end of file
diff --git a/docs/content/appendix/meeting_notes.tex 
b/docs/content/appendix/meeting_notes.tex
index d8081be..45c6fe6 100644
--- a/docs/content/appendix/meeting_notes.tex
+++ b/docs/content/appendix/meeting_notes.tex
@@ -98,16 +98,6 @@
     \item How do we want to handle different currencies? How about currencies 
like Bitcoin? Currency is determined by the currency of the exchange.
 \end{itemize}
 
-\textbf{Action points}
-\begin{itemize}
-    \item 
-\end{itemize}
-
-\textbf{Decisions}
-\begin{itemize}
-    \item
-\end{itemize}
-
 \subsection*{06.03.2024}
 
 \textbf{Participants}
@@ -138,9 +128,25 @@
     \item write SQL schema and generate UML using schema-spy instead of 
writing UML.
 \end{itemize}
 
-\textbf{Decisions}
+\subsection*{13.03.2024}
+
+\textbf{Participants}
+
 \begin{itemize}
-    \item
+    \item Fehrensen Benjamin
+    \item Grothoff Christian
+    \item H\"aberli Joel
+\end{itemize}
+
+\textbf{Topics}
+\begin{itemize}
+    \item SQL Schema of nonce2ecash.
+\end{itemize}
+
+\textbf{Action points}
+\begin{itemize}
+    \item Add rst file to official docs Repository
+    \item Add proper versioning to the SQL script.
 \end{itemize}
 
 % TEMPLATE % 
diff --git a/docs/content/architecture/exchange.tex 
b/docs/content/architecture/exchange.tex
deleted file mode 100644
index e69de29..0000000
diff --git a/docs/content/architecture/nonce2ecash.tex 
b/docs/content/architecture/nonce2ecash.tex
index d978263..237c931 100644
--- a/docs/content/architecture/nonce2ecash.tex
+++ b/docs/content/architecture/nonce2ecash.tex
@@ -1,43 +1,51 @@
-\section*{nonce2ecash}
-
-\subsection*{REST API}
+\section*{The nonce2ecash RESTful API}
 
 The API of the nonce2ecash component mirrors the flow from the creation of a 
nonce2ecash mapping to the creation of the reserve. Therefore three endpoints 
are implemented.
 
-An openapi specification of the API in yaml format can be found in 
\autoref*{appendix-api-spec}.
-
-\subsubsection*{Withdrawal Registration}
-\label{section-api-withdrawal-registration}
-
-With the \textit{withdrawal-registration} endpoint a nonce is registered at 
the nonce2ecash component of the Exchange. The \textit{N2CWithdrawalRequest} 
contains not only the nonce but also a reserve public key generated by the 
Wallet, the amount to withdraw and the provider type (e.g. \textit{WALLEE}). 
The provider type shall make the nonce2ecash component provider agnostic. Like 
this other providers can be added in the future.
+A specification of the API in yaml format can be found in 
\autoref*{appendix-api-spec}.
 
-\subsubsection*{Withdrawal Processing}
+\subsection{Authentication}
 
-With the \textit{withdrawal-processing} endpoint, the provider can notify the 
Exchange about the payment. Therefore the provider sends a 
\textit{N2CPaymentNotification} message to the API. The message contains the 
nonce to identify which withdrawal is processed, a provider specific 
transaction identifier which allows the nonce2ecash component to verify the 
payment at the providers backend and a flag indicating the success or failure 
of the payment.
+Terminals which authenticate against the nonce2ecash API must provide their 
respective access token. Therefore, they provide a \texttt{Authorization: 
Bearer \$ACCESS\_TOKEN} header, where \$ACCESS\_TOKEN is a secret 
authentication token configured by the exchange and must begin with the RFC 
8959 prefix \textit{secret-token}.
 
-\subsubsection*{Withdrawal Status}
+\subsection{Configuration of nonce2ecash}
 
-With the \textit{withdrawal-status} endpoint, the status of a mapping can be 
retrieved. With a parameter called listenForStatus the consumer can initiate 
the a long-polling which will only end if the mapping comes to the requested 
state.
+\begin{itemize}
+  \item \textbf{Method:} GET
+  \item \textbf{Endpoint:} /config
+  \item \textbf{Description:} Return the protocol version and configuration 
information about the nonce2ecash API.
+  \item \textbf{Response:} HTTP status code 200 OK. The exchange responds with 
a \texttt{Nonce2ecashConfig} object.
+\end{itemize}
 
-\subsubsection*{Database}
+\subsection{Withdrawing using nonce2ecash}
 
-The database of the nonce2ecash component is small and only has one table and 
two enumerations, which are presented as separate tables themself. 
+Withdrawals with a nonce2ecash are based on withdrawal operations which 
register a withdrawal identifier (nonce) at the nonce2ecash component. The 
provider must first create a unique identifier for the withdrawal operation 
(the \texttt{WITHDRAWAL\_ID}) to interact with the withdrawal operation and 
eventually withdraw using the wallet.
 
-\textbf{n2c\_status}
+\begin{itemize}
+  \item \textbf{Method:} POST
+  \item \textbf{Endpoint:} /withdrawal-operation
+  \item \textbf{Description:} Initiate the withdrawal operation, identified by 
the \texttt{WITHDRAWAL\_ID}.
+  \item \textbf{Request:} The request body contains a 
\texttt{WithdrawRegistration} object.
+  \item \textbf{Response:} HTTP status code 204 No content, 400 Bad request, 
or 500 Internal Server error.
+\end{itemize}
 
-The \textit{n2c\_status} table represents the enumeration of all possible 
states a mapping of a nonce to a reserve public key can be in.
+\begin{itemize}
+  \item \textbf{Method:} GET
+  \item \textbf{Endpoint:} /withdrawal-operation/\$WITHDRAWAL\_ID
+  \item \textbf{Description:} Query information about a withdrawal operation, 
identified by the \texttt{WITHDRAWAL\_ID}.
+  \item \textbf{Response:} HTTP status code 200 OK or 404 Not found.
+\end{itemize}
 
-\textbf{n2c\_provider\_type}
+\begin{itemize}
+  \item \textbf{Method:} POST
+  \item \textbf{Endpoint:} /withdrawal-operation/\$WITHDRAWAL\_ID
+  \item \textbf{Description:} Notifies nonce2ecash about an executed payment 
for a specific withdrawal.
+  \item \textbf{Request:} The request body contains a 
\texttt{PaymentNotification} object.
+  \item \textbf{Response:} HTTP status code 204 No content, 400 Bad request, 
404 Not found, or 500 Internal Server error.
+\end{itemize}
 
-The \textit{n2c\_provider\_type} table represents the enumeration of all 
accepted providers for the nonce2ecash process. For this thesis, the only 
supported provider will be \textit{WALLEE}. In the future more providers might 
be implemented.
+\section*{The nonce2ecash database}
 
-\textbf{n2c\_nonce\_reservepubkey}
+The database of the nonce2ecash component must track two different aspects. 
The first is the mapping of a nonce to a reserve public key to enable 
withdrawals and the second aspect is the authentication of terminals allowing 
withdrawals owned by terminal providers like \textit{Wallee}.
 
-The \textit{n2c\_nonce\_reservepubkey} table represents the mapping of a nonce 
to a reserve public key. The table holds the nonce, the reserve public key, the 
\textit{n2c\_status}, the \textit{n2c\_provider\_type} and a timestamp which 
represents the time of registration of the withdrawal (The time when the 
\autoref*{section-api-withdrawal-registration}). 
 
-\begin{figure}[h]
-    \centering
-    
\includegraphics[width=0.7\textwidth]{pictures/diagrams/db_nonce2ecash_erd.png}
-    \caption{Entity Relation Diagram of the nonce2ecash component}
-    \label{fig-diagram-erd-nonce2ecash}
-\end{figure}
diff --git a/docs/content/architecture/overview.tex 
b/docs/content/architecture/overview.tex
index 3371c7e..919962e 100644
--- a/docs/content/architecture/overview.tex
+++ b/docs/content/architecture/overview.tex
@@ -1,3 +1,11 @@
+\section*{GNU Taler}
+% TODO 
+General introduction to GNU Taler
+
+\section*{Wallee}
+% TODO 
+General introduction to Wallee
+
 \section*{Overview}
 
 \subsection*{Components}
diff --git a/docs/content/context/context.tex b/docs/content/context/context.tex
new file mode 100644
index 0000000..05fe1c2
--- /dev/null
+++ b/docs/content/context/context.tex
@@ -0,0 +1,4 @@
+To better understand the architecture, specification and the implementation of 
the new components, this chapter contains the description of the necessary 
components of the Taler system and the Wallee system. This is however not a 
complete documentation or explanation but rather a compilation of the features 
needed to implement the respective components.
+
+\include{content/context/taler}
+\include{content/context/wallee}
\ No newline at end of file
diff --git a/docs/content/context/taler.tex b/docs/content/context/taler.tex
new file mode 100644
index 0000000..def4940
--- /dev/null
+++ b/docs/content/context/taler.tex
@@ -0,0 +1,11 @@
+\section*{GNU Taler}
+
+GNU Taler is a payment system which allows paying for goods in an anonymous 
way while maintaining a trustworthy relationship between exchanges, merchants 
and customers. This goal is reached by implementing strong cryptographic 
primitives. Taler is wether a blockchain nor a currency. It is a payment 
system. It allows transferring money from A to B anonymously but still letting 
regulators control that no bad activities are going on.
+
+\subsection*{Wirewatch Gateway RESTful API}
+
+The wirewatch gateway helps communicating with the Exchanges core using 
convenient API. This includes retrieving information about transactions which 
were sent through the EBICS system. EBICS stands for \textit{Electronic Banking 
Internet Communication Standard} and represents an interface for interbank 
communication based on TCP/IP. Taler can retrieve incoming transaction through 
the EBICS interface. This will help nonce2ecash to capture the transaction of 
the Terminal Backend to the Ex [...]
+
+\subsection*{Bank Integration RESTful API}
+
+The Bank Integration API is used to withdraw digital currency. It supplies API 
to initiate a withdrawal and fetching status information about a withdrawal. 
When initiating a withdrawal a reserve is created at the exchange which 
contains digital currency signed by the exchange. Since these assets are linked 
to a reserve public key supplied by the Taler Wallet, they can be retrieved by 
the Wallet. The reserve will only be collectable, if the transaction was 
approved by the Exchange as vali [...]
diff --git a/docs/content/context/wallee.tex b/docs/content/context/wallee.tex
new file mode 100644
index 0000000..36b2174
--- /dev/null
+++ b/docs/content/context/wallee.tex
@@ -0,0 +1,5 @@
+\section*{Wallee}
+
+\subsection*{Wallee Terminal}
+
+\subsection*{Wallee Backend and API}
\ No newline at end of file
diff --git a/docs/content/introduction/goal.tex 
b/docs/content/introduction/goal.tex
index 0216a0b..560f02a 100644
--- a/docs/content/introduction/goal.tex
+++ b/docs/content/introduction/goal.tex
@@ -1,10 +1,10 @@
 \section*{Goal}
 
-The goal of this thesis is to implement a process which allows withdrawing 
Taler using a credit card at a terminal.
+The goal of this thesis is to implement a process which allows withdrawing 
Taler using a credit card at a terminal of the terminal provider 
\textit{Wallee}.
 
-\subsection*{cashless2ecash}
+\subsection*{nonce2ecash}
 
-Therefore a new component, named \textbf{cashless2ecash (or maybe 
nonce2ecash??)}, will be implemented as part of the Taler Exchange. 
Cashless2ecash allows to check that the payment was accepted by the provider 
and therefore can guarantee, that the money of the withdrawing user will reach 
the bank account owned by the Taler Exchange.
+Therefore a new component, named \textbf{nonce2ecash}, will be implemented as 
part of the Taler Exchange. Nonce2ecash will mediate between the Taler Exchange 
and the terminal provider. This includes checking that the transaction of the 
debitor reaches the account of the Exchange and therefore the digital currency 
can be withdrawn by the user, using its Wallet.
 
 \subsection*{Wallee}
 A new app for the payment terminal provider \textbf{Wallee} must be 
implemented which allows to start the withdrawal using providers facilities. 
The provider will guarantee through its backend, that the payment was 
successful. This puts the liability of the payment on the provider of the 
terminal.
diff --git a/docs/content/introduction/introduction.tex 
b/docs/content/introduction/introduction.tex
index 0cdcc06..0a30f7d 100644
--- a/docs/content/introduction/introduction.tex
+++ b/docs/content/introduction/introduction.tex
@@ -1,51 +1,23 @@
 \section*{Motivation}
 
-Ever tried withdrawing Taler? It is quite hard if your bank does not implement 
and run the facilities supplied by Taler ecosystem or you are not able to find 
people controlling the respective Exchange. The second approach is somewhat 
prone to human errors, but it works.
+Ever tried withdrawing Taler? It is quite hard if a bank you have access to, 
does not implement and run the facilities supplied by Taler or you are not able 
to find people controlling the respective Exchange. The second approach is 
somewhat strange and prone to human errors anyway, but it works.
 
 So the problem currently is that it is not possible for users to get Taler if 
the above options are not feasible for them. This thesis proposes a way 
allowing users who own a credit card and a Taler Wallet, to buy Taler at a 
terminal supporting the withdrawal of Taler.
 
 To make the withdrawal possible, various loose ends must be put together 
within the Taler ecosystem and the terminal provider. Also a new component 
called \textit{cashless2ecash} is implemented. The thesis focuses on the 
terminal provider called \textit{Wallee}. Therefore an application for the 
Wallee Terminal (PAX A50) for withdrawing Taler is implemented.
 
-With these components, a trustworthy relationship can be created, which makes 
it possible for the Exchange to issue coins to a user. Therefore the Exchange 
is not putting his trust on the money received but rather on the promise of a 
trusted third party (the terminal provider) to put the received money in a 
location, controlled by the Exchange eventually (e.g. a bank account owned by 
the Exchange).
+With these components, a trustworthy relationship can be created, which makes 
it possible for the Exchange to issue digital currency to a user. Therefore the 
Exchange is not putting his trust on the money received but rather on the 
promise of a trusted third party (the terminal provider) to put the received 
money in a location, controlled by the Exchange eventually (e.g. a bank account 
owned by the Exchange).
 
 This enables a broader group of people to leverage Taler for their payments. 
Which eventually leads to wider adoption of the payment system Taler.
 
-\section*{Trust Relations}
-Withdrawing Taler coins establishes the need for a new trust model. In order 
to gather a deeper understanding of the difference between the current trust 
model and the new model they are described here. 
-
-\subsection*{Direct Trust - Direct-Payment-Withdrawal}
-The \textit{direct trust} model is the model existing at the time. It allows 
the withdrawal of Taler using direct interaction between the deptor 
(withdrawing party) and the Taler Exchange. It allows the deptor to present 
credentials and values to the Exchange which it can claim and return the 
equivalent in coins. In case a deptor is unable to pay the amount he wants to 
buy coins from the Exchange, the Exchange will simply reject the request and 
tell the deptor that his balance is too low [...]
-
-The \textit{transaction} itself is the trust anchor. No trusted third party is 
needed.
-
-\subsection*{Indirect Trust - Eventual-Payment-Withdrawal}
-In the \textit{indirect trust} model is the model we must facilitate to be 
able to withdraw coins using a credit card. This comes from how payment systems 
are implemented and that transactions are not immediate but rather eventually 
executed. This means that if a payment is made using a credit card, the amount 
is not immediately transferred from the deptor to the seller. Instead the 
credit card provider (e.g. Mastercard or Visa) guarantees the fulfillment to 
the seller, by lending money  [...]
-
-The \textit{guarantee for the transaction} of the credit card provider is the 
trust anchor. The provider steps in as trusted third party.
-
-\subsection*{Why Wallee?}
-Wallee is not a credit card provider, but a payment system provider. So why 
should the Taler Exchange trust Wallee?
-% TODO: Why can we trust the terminals of Wallee?
-
-\subsection*{Chain of Trust}
-The above leads to a chain of trust that must be maintained in order for the 
system to be accountable and trustworthy. If one single link in the chain 
breaks our trust assumption, the chain of trust is broken and the system 
becomes untrustworthy and therefore not usable.
-
-\section*{GNU Taler}
-% TODO 
-General introduction to GNU Taler
-
-\section*{Wallee}
-% TODO 
-General introduction to Wallee
-
 \section*{Perspectives}
 During the initial analysis of the task, two main areas of work were 
discovered. One is the Taler Exchange and the other is the Application for the 
terminal. This led to different views on the system by two different players 
within it. To allow a more concise view on the system and to support the 
readers and implementer, two perspectives shall be kept in mind. They have 
different views on the process but need to interact with each other seamlessly.
 
 \subsection*{Terminal Application}
-The perspective of the terminal application includes all processes within the 
application which interacts with the user and its credit card allowing the 
withdrawal of coins. The terminal application wants to conviently allow the 
withdrawal of coins and charge fees to cover costs and risks.
+The perspective of the terminal application includes all processes within the 
application which interacts with the user, his Taler Wallet and its credit card 
allowing the withdrawal of digital currency. The terminal application wants to 
conviently allow the withdrawal of digital currency and charge fees to cover 
its costs and risks.
 
-\subsection*{Taler Exchange (cashless2ecash)}
-The perspective of the Taler Exchange includes all processes within 
cashless2ecash component and the interaction with the terminal application, 
terminal backend and the wallet of the user. The Taler Exchange wants to allow 
withdrawal of coins only to users who pay the equivalent value to the Exchange 
and stay out of legal implications.
+\subsection*{Taler Exchange (nonce2ecash)}
+The perspective of the Taler Exchange includes all processes within 
nonce2ecash component and the interaction with the terminal application, 
terminal backend and the wallet of the user. The Taler Exchange wants to allow 
withdrawal of digital currency only to users who pay the equivalent value to 
the Exchange. The Exchange wants to stay out of any legal implications at all 
costs.
 
 \section*{Fees}
 Since buying Taler using a credit card leverages a third party payment system, 
new \textbf{fees} are introduced and must be taken care of. The fees to buy 
Taler are defined by the third party payment system and therefore do not lie in 
the control of the Taler system. The fees are retrieved by the terminal and 
added to the amount of money which is to be withdrawn by the user. Only after 
giving the confirmation to buy the specified amount of Taler with the specified 
amount of fees, the pay [...]
diff --git a/docs/thesis.pdf b/docs/thesis.pdf
index 78e3a00..1122f9e 100644
Binary files a/docs/thesis.pdf and b/docs/thesis.pdf differ
diff --git a/docs/thesis.tex b/docs/thesis.tex
index 79bccee..5c53870 100644
--- a/docs/thesis.tex
+++ b/docs/thesis.tex
@@ -177,6 +177,9 @@
 \input{content/introduction/introduction}
 \input{content/introduction/goal}
 
+\chapter{Context}
+\input{content/context/context}
+
 \chapter{Architecture}
 \input{content/architecture/overview}
 \input{content/architecture/nonce2ecash}
diff --git a/nonce2ecash/go.mod b/nonce2ecash/go.mod
index 0292e31..8eb0dc2 100644
--- a/nonce2ecash/go.mod
+++ b/nonce2ecash/go.mod
@@ -4,4 +4,15 @@ go 1.22.0
 
 require gotest.tools/v3 v3.5.1
 
-require github.com/google/go-cmp v0.5.9 // indirect
+require (
+       github.com/google/go-cmp v0.5.9 // indirect
+       github.com/jackc/pgpassfile v1.0.0 // indirect
+       github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // 
indirect
+       github.com/jackc/pgx/v5 v5.5.5 // indirect
+       github.com/jinzhu/inflection v1.0.0 // indirect
+       github.com/jinzhu/now v1.1.5 // indirect
+       github.com/lib/pq v1.10.9 // indirect
+       golang.org/x/crypto v0.17.0 // indirect
+       golang.org/x/text v0.14.0 // indirect
+       gorm.io/gorm v1.25.7 // indirect
+)
diff --git a/nonce2ecash/go.sum b/nonce2ecash/go.sum
index 7dd4ab5..39d4d5f 100644
--- a/nonce2ecash/go.sum
+++ b/nonce2ecash/go.sum
@@ -1,4 +1,29 @@
+github.com/davecgh/go-spew v1.1.0/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/go-cmp v0.5.9/go.mod 
h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/jackc/pgpassfile v1.0.0 
h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
+github.com/jackc/pgpassfile v1.0.0/go.mod 
h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
+github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a 
h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
+github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod 
h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
+github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
+github.com/jackc/pgx/v5 v5.5.5/go.mod 
h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
+github.com/jinzhu/inflection v1.0.0 
h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod 
h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
+github.com/jinzhu/now v1.1.5/go.mod 
h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
+github.com/lib/pq v1.10.9/go.mod 
h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/pmezard/go-difflib v1.0.0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod 
h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod 
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
+golang.org/x/crypto v0.17.0/go.mod 
h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod 
h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod 
h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A=
+gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
 gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
 gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
diff --git a/nonce2ecash/pkg/common/codec.go b/nonce2ecash/pkg/common/codec.go
index 2799040..bf802a0 100644
--- a/nonce2ecash/pkg/common/codec.go
+++ b/nonce2ecash/pkg/common/codec.go
@@ -7,9 +7,9 @@ import (
 )
 
 type Codec[T any] interface {
-       httpApplicationContentHeader() string
-       encode(*T) (io.Reader, error)
-       decode(io.Reader) (*T, error)
+       HttpApplicationContentHeader() string
+       Encode(*T) (io.Reader, error)
+       Decode(io.Reader) (*T, error)
 }
 
 type JsonCodec[T any] struct {
diff --git a/nonce2ecash/pkg/common/http-util.go 
b/nonce2ecash/pkg/common/http-util.go
index 3812d2a..a552fc8 100644
--- a/nonce2ecash/pkg/common/http-util.go
+++ b/nonce2ecash/pkg/common/http-util.go
@@ -2,25 +2,46 @@ package common
 
 import (
        "errors"
+       "fmt"
        "net/http"
        "strings"
 )
 
 const HTTP_OK = 200
 const HTTP_NO_CONTENT = 204
+const HTTP_BAD_REQUEST = 400
+const HTTP_UNAUTHORIZED = 401
 const HTTP_NOT_FOUND = 404
 const HTTP_CONFLICT = 409
 const HTTP_INTERNAL_SERVER_ERROR = 500
 
 // execute a GET request and parse body or retrieve error
-func HttpGetBodyOrError[T any](
+func HttpGet2[T any](
+       req string,
+       codec Codec[T],
+) (*T, int, error) {
+
+       return HttpGet(
+               req,
+               nil,
+               nil,
+               codec,
+       )
+}
+
+// execute a GET request and parse body or retrieve error
+// path- and query-parameters can be set to add query and path parameters
+func HttpGet[T any](
        req string,
        pathParams map[string]string,
        queryParams map[string]string,
        codec Codec[T],
 ) (*T, int, error) {
 
-       res, err := http.Get(formatUrl(req, pathParams, queryParams))
+       url := formatUrl(req, pathParams, queryParams)
+       fmt.Println("GET:", url)
+
+       res, err := http.Get(url)
        if err != nil {
                return nil, -1, err
        }
@@ -28,13 +49,32 @@ func HttpGetBodyOrError[T any](
        if codec == nil {
                return nil, res.StatusCode, err
        } else {
-               resBody, err := codec.decode(res.Body)
+               resBody, err := codec.Decode(res.Body)
                return resBody, res.StatusCode, err
        }
 }
 
 // execute a POST request and parse response or retrieve error
-func HttpPostOrError[T any, R any](
+func HttpPost2[T any, R any](
+       req string,
+       body *T,
+       requestCodec Codec[T],
+       responseCodec Codec[R],
+) (*R, int, error) {
+
+       return HttpPost(
+               req,
+               nil,
+               nil,
+               body,
+               requestCodec,
+               responseCodec,
+       )
+}
+
+// execute a POST request and parse response or retrieve error
+// path- and query-parameters can be set to add query and path parameters
+func HttpPost[T any, R any](
        req string,
        pathParams map[string]string,
        queryParams map[string]string,
@@ -43,11 +83,14 @@ func HttpPostOrError[T any, R any](
        responseCodec Codec[R],
 ) (*R, int, error) {
 
+       url := formatUrl(req, pathParams, queryParams)
+       fmt.Println("POST:", url)
+
        var res *http.Response
        if body == nil {
                if requestCodec == nil {
                        res, err := http.Post(
-                               formatUrl(req, pathParams, queryParams),
+                               url,
                                "",
                                nil,
                        )
@@ -65,14 +108,14 @@ func HttpPostOrError[T any, R any](
                        return nil, -1, errors.New("invalid arguments - body 
was present but no codec was defined")
                } else {
 
-                       encodedBody, err := requestCodec.encode(body)
+                       encodedBody, err := requestCodec.Encode(body)
                        if err != nil {
                                return nil, -1, err
                        }
 
                        res, err = http.Post(
-                               formatUrl(req, pathParams, queryParams),
-                               requestCodec.httpApplicationContentHeader(),
+                               url,
+                               requestCodec.HttpApplicationContentHeader(),
                                encodedBody,
                        )
 
@@ -86,7 +129,7 @@ func HttpPostOrError[T any, R any](
                return nil, res.StatusCode, nil
        }
 
-       resBody, err := responseCodec.decode(res.Body)
+       resBody, err := responseCodec.Decode(res.Body)
        if err != nil {
                return nil, -1, err
        }
@@ -109,7 +152,7 @@ func formatUrl(
 // The function expects each parameter in the path to be prefixed
 // using a ':'. The function handles url as follows:
 //
-//     /some/:param/tobereplaced -> ':param' will be replace with value.
+//     /some/:param/tobereplaced -> ':param' will be replaced with value.
 //
 // For replacements, the pathParams map must be supplied. The map contains
 // the name of the parameter with the value mapped to it.
@@ -119,14 +162,17 @@ func setUrlPath(
        pathParams map[string]string,
 ) string {
 
-       if pathParams == nil && len(pathParams) < 1 {
+       if pathParams == nil || len(pathParams) < 1 {
                return req
        }
 
        var url = req
        for k, v := range pathParams {
 
-               url = strings.Replace(url, ":"+k, v, 1)
+               if !strings.HasPrefix(k, "/") {
+                       // prevent scheme postfix replacements
+                       url = strings.Replace(url, ":"+k, v, 1)
+               }
        }
        return url
 }
@@ -136,7 +182,7 @@ func setUrlQuery(
        queryParams map[string]string,
 ) string {
 
-       if queryParams == nil && len(queryParams) < 1 {
+       if queryParams == nil || len(queryParams) < 1 {
                return req
        }
 
diff --git a/nonce2ecash/pkg/common/http-util_test.go 
b/nonce2ecash/pkg/common/http-util_test.go
new file mode 100644
index 0000000..88fa1b6
--- /dev/null
+++ b/nonce2ecash/pkg/common/http-util_test.go
@@ -0,0 +1,62 @@
+package common_test
+
+import (
+       "fmt"
+       "nonce2ecash/pkg/common"
+       "testing"
+)
+
+const URL_GET = "https://jsonplaceholder.typicode.com/todos/:id";
+const URL_POST = "https://jsonplaceholder.typicode.com/posts";
+
+type TestStruct struct {
+       UserId    int    `json:"userId"`
+       Id        int    `json:"id"`
+       Title     string `json:"title"`
+       Completed bool   `json:"completed"`
+}
+
+func TestGET(t *testing.T) {
+
+       res, status, err := common.HttpGet(
+               URL_GET,
+               map[string]string{
+                       "id": "1",
+               },
+               map[string]string{},
+               common.NewJsonCodec[TestStruct](),
+       )
+
+       if err != nil {
+               t.Errorf("%s", err.Error())
+               t.FailNow()
+       }
+
+       fmt.Println("res:", res, ", status:", status)
+}
+
+func TestPOST(t *testing.T) {
+
+       res, status, err := common.HttpPost(
+               URL_POST,
+               map[string]string{
+                       "id": "1",
+               },
+               map[string]string{},
+               &TestStruct{
+                       UserId:    1,
+                       Id:        1,
+                       Title:     "TEST",
+                       Completed: false,
+               },
+               common.NewJsonCodec[TestStruct](),
+               common.NewJsonCodec[TestStruct](),
+       )
+
+       if err != nil {
+               t.Errorf("%s", err.Error())
+               t.FailNow()
+       }
+
+       fmt.Println("res:", res, ", status:", status)
+}
diff --git a/nonce2ecash/pkg/common/model.go b/nonce2ecash/pkg/common/model.go
index 747d324..c3a1bab 100644
--- a/nonce2ecash/pkg/common/model.go
+++ b/nonce2ecash/pkg/common/model.go
@@ -6,11 +6,26 @@ type WithdrawalIdentifier string
 // https://docs.taler.net/core/api-common.html#cryptographic-primitives
 type EddsaPublicKey string
 
+// https://docs.taler.net/core/api-common.html#hash-codes
+type HashCode string
+
+// https://docs.taler.net/core/api-common.html#hash-codes
+type ShortHashCode string
+
+// https://docs.taler.net/core/api-common.html#timestamps
+type Timestamp struct {
+       Ts int `json:"t_s"`
+}
+
+// https://docs.taler.net/core/api-common.html#wadid
+type WadId [6]uint32
+
+// according to 
https://docs.taler.net/core/api-bank-integration.html#tsref-type-BankWithdrawalOperationStatus
 type WithdrawalOperationStatus string
 
 const (
        PENDING   WithdrawalOperationStatus = "pending"
-       SELECTED                            = "selected"
-       ABORTED                             = "aborted"
-       CONFIRMED                           = "confirmed"
+       SELECTED  WithdrawalOperationStatus = "selected"
+       ABORTED   WithdrawalOperationStatus = "aborted"
+       CONFIRMED WithdrawalOperationStatus = "confirmed"
 )
diff --git a/nonce2ecash/pkg/db/db.go b/nonce2ecash/pkg/db/db.go
new file mode 100644
index 0000000..0d5a07b
--- /dev/null
+++ b/nonce2ecash/pkg/db/db.go
@@ -0,0 +1,66 @@
+package db
+
+import (
+       "context"
+       "strconv"
+       "strings"
+
+       "github.com/jackc/pgx/v5"
+)
+
+const POSTGRESQL_SCHEME = "postgres://"
+const NONCE2ECASH_DATABASE = "nonce2ecash"
+
+type DatabaseConfig interface {
+       ConnectionString() string
+}
+
+type PostgresNonce2ecashDatabaseConfig struct {
+       DatabaseConfig
+
+       host     string
+       username string
+       password string
+       port     int
+}
+
+func NewDbConf(
+       host string,
+       port int,
+       username string,
+       password string,
+) DatabaseConfig {
+
+       cfg := new(PostgresNonce2ecashDatabaseConfig)
+       cfg.host = host
+       cfg.port = port
+       cfg.username = username
+       cfg.password = password
+
+       return cfg
+}
+
+func NewDb(cfg DatabaseConfig) (*pgx.Conn, error) {
+
+       return pgx.Connect(
+               context.Background(),
+               cfg.ConnectionString(),
+       )
+}
+
+func (cfg *PostgresNonce2ecashDatabaseConfig) ConnectionString() string {
+
+       // format: postgres://username:password@hostname:port/database_name
+       return strings.Join([]string{
+               POSTGRESQL_SCHEME,
+               cfg.username,
+               ":",
+               cfg.password,
+               "@",
+               cfg.host,
+               ":",
+               strconv.FormatInt(int64(cfg.port), 10),
+               "/",
+               NONCE2ECASH_DATABASE,
+       }, "")
+}
diff --git a/nonce2ecash/pkg/db/provider.go b/nonce2ecash/pkg/db/provider.go
new file mode 100644
index 0000000..212e9f9
--- /dev/null
+++ b/nonce2ecash/pkg/db/provider.go
@@ -0,0 +1,22 @@
+package db
+
+import "gorm.io/gorm"
+
+type TerminalProvider struct {
+       gorm.Model
+
+       ProviderTerminalID int64  `gorm:"primaryKey"`
+       Name               string `gorm:"unique;not null"`
+       BackendBaseURL     string `gorm:"not null"`
+       BackendCredentials string `gorm:"not null"`
+}
+
+type Terminal struct {
+       gorm.Model
+       TerminalID  int64            `gorm:"primaryKey"`
+       AccessToken []byte           `gorm:"type:bytea;not 
null;check:LENGTH(access_token)=32"`
+       Active      bool             `gorm:"not null;default:true"`
+       ProviderID  int64            `gorm:"not null"`
+       Provider    TerminalProvider `gorm:"foreignKey:ProviderID"`
+       Description string
+}
diff --git a/nonce2ecash/pkg/db/withdrawal.go b/nonce2ecash/pkg/db/withdrawal.go
new file mode 100644
index 0000000..c696e7a
--- /dev/null
+++ b/nonce2ecash/pkg/db/withdrawal.go
@@ -0,0 +1,29 @@
+package db
+
+import (
+       "nonce2ecash/pkg/common"
+
+       "gorm.io/gorm"
+)
+
+type Withdrawal struct {
+       gorm.Model
+
+       WithdrawalId          []byte `gorm:"type:bytea;primaryKey"`
+       ReservePubKey         []byte `gorm:"type:bytea"`
+       RegistrationTs        int64
+       Amount                TalerAmountCurrency
+       Fees                  TalerAmountCurrency
+       WithdrawalStatus      common.WithdrawalOperationStatus
+       TerminalId            int64
+       ProviderTransactionId string
+       LastRetryTs           int64
+       RetryCounter          int32  `gorm:"default:0"`
+       CompletionProof       []byte `gorm:"type:blob"`
+}
+
+type TalerAmountCurrency struct {
+       Val  int64
+       Frac int32
+       Curr string `gorm:"size:12"`
+}
diff --git a/nonce2ecash/pkg/handler.go b/nonce2ecash/pkg/handler.go
index ace62fc..4a12039 100644
--- a/nonce2ecash/pkg/handler.go
+++ b/nonce2ecash/pkg/handler.go
@@ -13,7 +13,6 @@ const HTTP_METHOD_NOT_ALLOWED = 405
 func config(writer http.ResponseWriter, req *http.Request) {
 
        if isGet(req) {
-
                writer.Write([]byte("{\n\"\":\"\",\n\"\":\"\"\n}"))
                writer.WriteHeader(HTTP_OK)
        }
@@ -39,6 +38,10 @@ func handleWithdrawalRegistration(writer 
http.ResponseWriter, req *http.Request)
 
 }
 
+func handleWithdrawalStatus(writer http.ResponseWriter, req *http.Request) {
+
+}
+
 func handlePaymentNotification(writer http.ResponseWriter, req *http.Request) {
 
 }
@@ -55,5 +58,5 @@ func isPost(req *http.Request) bool {
 
 func methodNotAllowed(writer http.ResponseWriter) {
 
-       writer.WriteHeader(405)
+       writer.WriteHeader(HTTP_METHOD_NOT_ALLOWED)
 }
diff --git a/nonce2ecash/pkg/taler-bank-integration/client.go 
b/nonce2ecash/pkg/taler-bank-integration/client.go
index 1793cdd..66b823f 100644
--- a/nonce2ecash/pkg/taler-bank-integration/client.go
+++ b/nonce2ecash/pkg/taler-bank-integration/client.go
@@ -8,6 +8,7 @@ import (
 
 const WITHDRAWAL_ID_PATH_PARAM_NAME = "withdrawal_id"
 
+const TALER_BANK_INTEGRATION_CONFIG_API = "/config"
 const WITHDRAWAL_OPERATION_API = "/withdrawal-operation"
 const WITHDRAWAL_OPERATION_BY_ID_API = WITHDRAWAL_OPERATION_API + "/:" + 
WITHDRAWAL_ID_PATH_PARAM_NAME
 const WITHDRAWAL_OPERATION_ABORT_BY_ID_API = WITHDRAWAL_OPERATION_BY_ID_API + 
"/abort"
@@ -15,9 +16,10 @@ const WITHDRAWAL_OPERATION_ABORT_BY_ID_API = 
WITHDRAWAL_OPERATION_BY_ID_API + "/
 type TalerBankIntegration interface {
        init(string)
 
-       withdrawalOperationStatus(common.WithdrawalIdentifier) 
(*BankWithdrawalOperationStatus, error)
-       withdrawalOperationCreate(common.EddsaPublicKey, string) 
(*BankWithdrawalOperationPostResponse, error)
-       withdrawalOperationAbort(common.WithdrawalIdentifier) error
+       BankIntegrationConfig() (*BankIntegrationConfig, error)
+       WithdrawalOperationStatus(common.WithdrawalIdentifier) 
(*BankWithdrawalOperationStatus, error)
+       WithdrawalOperationCreate(common.EddsaPublicKey, string) 
(*BankWithdrawalOperationPostResponse, error)
+       WithdrawalOperationAbort(common.WithdrawalIdentifier) error
 }
 
 type TalerBankIntegrationImpl struct {
@@ -26,6 +28,27 @@ type TalerBankIntegrationImpl struct {
        exchangeBaseUrl string
 }
 
+func (tbi *TalerBankIntegrationImpl) BankIntegrationConfig() 
(*BankIntegrationConfig, error) {
+
+       cfg, status, err := common.HttpGet(
+               tbi.exchangeBaseUrl+TALER_BANK_INTEGRATION_CONFIG_API,
+               nil,
+               nil,
+               common.NewJsonCodec[BankIntegrationConfig](),
+       )
+
+       if err != nil {
+               return nil, err
+       }
+
+       if status == common.HTTP_OK {
+
+               return cfg, nil
+       }
+
+       return nil, fmt.Errorf("HTTP %d - unexpected", status)
+}
+
 // Initialize the taler bank integration implementation.
 // The exchangeBaseUrl will be used as target by the impl.
 func (tbi *TalerBankIntegrationImpl) init(exchangeBaseUrl string) {
@@ -34,11 +57,11 @@ func (tbi *TalerBankIntegrationImpl) init(exchangeBaseUrl 
string) {
 }
 
 // check status of withdrawal
-func (tbi *TalerBankIntegrationImpl) withdrawalOperationStatus(
+func (tbi *TalerBankIntegrationImpl) WithdrawalOperationStatus(
        id common.WithdrawalIdentifier,
 ) (*BankWithdrawalOperationStatus, error) {
 
-       withdrawalOperationStatus, status, err := common.HttpGetBodyOrError(
+       WithdrawalOperationStatus, status, err := common.HttpGet(
                tbi.exchangeBaseUrl+WITHDRAWAL_OPERATION_BY_ID_API,
                map[string]string{WITHDRAWAL_ID_PATH_PARAM_NAME: string(id)},
                nil,
@@ -51,7 +74,7 @@ func (tbi *TalerBankIntegrationImpl) 
withdrawalOperationStatus(
 
        if status == common.HTTP_OK {
 
-               return withdrawalOperationStatus, nil
+               return WithdrawalOperationStatus, nil
        }
 
        if status == common.HTTP_NOT_FOUND {
@@ -63,13 +86,13 @@ func (tbi *TalerBankIntegrationImpl) 
withdrawalOperationStatus(
 }
 
 // send parameters for reserve to exchange core.
-func (tbi *TalerBankIntegrationImpl) withdrawalOperationCreate(
+func (tbi *TalerBankIntegrationImpl) WithdrawalOperationCreate(
        id common.WithdrawalIdentifier,
        reservePubKey common.EddsaPublicKey,
        exchangPayToAddress string,
 ) (*BankWithdrawalOperationPostResponse, error) {
 
-       bankWithdrawalOperationPostResponse, status, err := 
common.HttpPostOrError(
+       bankWithdrawalOperationPostResponse, status, err := common.HttpPost(
                tbi.exchangeBaseUrl+WITHDRAWAL_OPERATION_BY_ID_API,
                map[string]string{WITHDRAWAL_ID_PATH_PARAM_NAME: string(id)},
                nil,
@@ -104,11 +127,11 @@ func (tbi *TalerBankIntegrationImpl) 
withdrawalOperationCreate(
 }
 
 // abort withdrawal
-func (tbi *TalerBankIntegrationImpl) withdrawalOperationAbort(
+func (tbi *TalerBankIntegrationImpl) WithdrawalOperationAbort(
        id common.WithdrawalIdentifier,
 ) error {
 
-       _, status, err := common.HttpPostOrError[any, any](
+       _, status, err := common.HttpPost[any, any](
                tbi.exchangeBaseUrl+WITHDRAWAL_OPERATION_BY_ID_API,
                map[string]string{WITHDRAWAL_ID_PATH_PARAM_NAME: string(id)},
                nil,
diff --git a/nonce2ecash/pkg/taler-bank-integration/model.go 
b/nonce2ecash/pkg/taler-bank-integration/model.go
index 6e2f02b..97614f1 100644
--- a/nonce2ecash/pkg/taler-bank-integration/model.go
+++ b/nonce2ecash/pkg/taler-bank-integration/model.go
@@ -2,6 +2,25 @@ package talerbankintegration
 
 import "nonce2ecash/pkg/common"
 
+// 
https://docs.taler.net/core/api-exchange.html#tsref-type-CurrencySpecification
+type CurrencySpecification struct {
+       Name                            string `json:"name"`
+       Currency                        string `json:"currency"`
+       NumFractionalInputDigits        int    
`json:"num_fractional_input_digits"`
+       NumFractionalNormalDigits       int    
`json:"num_fractional_normal_digits"`
+       NumFractionalTrailingZeroDigits int    
`json:"num_fractional_trailing_zero_digits"`
+       AltUnitNames                    string `json:"alt_unit_names"`
+}
+
+// 
https://docs.taler.net/core/api-bank-integration.html#tsref-type-BankIntegrationConfig
+type BankIntegrationConfig struct {
+       Name                  string                `json:"name"`
+       Version               string                `json:"version"`
+       Implementation        string                `json:"implementation"`
+       Currency              string                `json:"currency"`
+       CurrencySpecification CurrencySpecification 
`json:"currency_specification"`
+}
+
 // 
https://docs.taler.net/core/api-bank-integration.html#tsref-type-BankWithdrawalOperationPostRequest
 type BankWithdrawalOperationPostRequest struct {
        ReservePub       string `json:"reserve_pub"`
diff --git a/nonce2ecash/pkg/taler-wirewatch-gateway/client.go 
b/nonce2ecash/pkg/taler-wirewatch-gateway/client.go
index 5c5861e..5892475 100644
--- a/nonce2ecash/pkg/taler-wirewatch-gateway/client.go
+++ b/nonce2ecash/pkg/taler-wirewatch-gateway/client.go
@@ -1 +1,226 @@
 package talerwirewatchgateway
+
+import (
+       "errors"
+       "fmt"
+       "nonce2ecash/pkg/common"
+       "strconv"
+)
+
+const WIRE_GATEWAY_START_QUERY = "start"
+const WIRE_GATEWAY_DELTA_QUERY = "delta"
+const WIRE_GATEWAY_LONGPOLL_QUERY = "long_poll_ms"
+
+const WIRE_GATEWAY_API = ""
+const WIRE_GATEWAY_API_CONFIG = WIRE_GATEWAY_API + "/config"
+const WIRE_GATEWAY_TRANSFER_API = WIRE_GATEWAY_API + "/transfer"
+const WIRE_GATEWAY_HISTORY_INCOMING_API = WIRE_GATEWAY_API + 
"/history/incoming"
+const WIRE_GATEWAY_HISTORY_OUTGOING_API = WIRE_GATEWAY_API + 
"/history/outgoing"
+
+type TalerWirewatchGateway interface {
+       Init(string, string)
+
+       WirewatchGatewayConfig() (*WireConfig, error)
+       WirewatchGatewayTransfer(*TransferRequest) (*TransferResponse, error)
+       WirewatchGatewayHistoryIncoming(int, int, int) (*IncomingHistory, error)
+       WirewatchGatewayHistoryOutgoing(int, int, int) (*OutgoingHistory, error)
+}
+
+type TalerWirewatchGatewayImpl struct {
+       TalerWirewatchGateway
+
+       exchangeBaseUrl string
+       authToken       string
+}
+
+func NewWirewatchGateway(exchangeBaseUrl string, authToken string) 
TalerWirewatchGateway {
+
+       twg := new(TalerWirewatchGatewayImpl)
+       twg.Init(exchangeBaseUrl, authToken)
+       return twg
+}
+
+func (twg *TalerWirewatchGatewayImpl) Init(exchangeBaseUrl string, authToken 
string) {
+
+       twg.exchangeBaseUrl = exchangeBaseUrl
+       twg.authToken = authToken
+}
+
+// https://docs.taler.net/core/api-bank-wire.html#making-transactions
+func (twg *TalerWirewatchGatewayImpl) WirewatchGatewayConfig() (*WireConfig, 
error) {
+
+       res, status, err := common.HttpGet2(
+               WIRE_GATEWAY_API_CONFIG,
+               common.NewJsonCodec[WireConfig](),
+       )
+
+       if err != nil {
+               return nil, err
+       }
+
+       if status == common.HTTP_OK {
+
+               return res, nil
+       }
+
+       return nil, fmt.Errorf("HTTP %d - unexpected", status)
+}
+
+// https://docs.taler.net/core/api-bank-wire.html#making-transactions
+func (twg *TalerWirewatchGatewayImpl) WirewatchGatewayTransfer(
+       transferRequest *TransferRequest,
+) (*TransferResponse, error) {
+
+       res, status, err := common.HttpPost2(
+               WIRE_GATEWAY_TRANSFER_API,
+               transferRequest,
+               common.NewJsonCodec[TransferRequest](),
+               common.NewJsonCodec[TransferResponse](),
+       )
+
+       if err != nil {
+               return nil, err
+       }
+
+       if status == common.HTTP_OK {
+
+               return res, nil
+       }
+
+       if status == common.HTTP_BAD_REQUEST {
+
+               return nil, errors.New("request malformed")
+       }
+
+       if status == common.HTTP_UNAUTHORIZED {
+
+               return nil, errors.New("authentication failed, likely the 
credentials are wrong")
+       }
+
+       if status == common.HTTP_NOT_FOUND {
+
+               return nil, errors.New("the endpoint is wrong or the user name 
is unknown")
+       }
+
+       if status == common.HTTP_CONFLICT {
+
+               return nil, errors.New("a transaction with the same request_uid 
but different transaction details has been submitted before")
+       }
+
+       return nil, fmt.Errorf("HTTP %d - unexpected", status)
+}
+
+// 
https://docs.taler.net/core/api-bank-wire.html#querying-the-transaction-history
+func (twg *TalerWirewatchGatewayImpl) WirewatchGatewayHistoryIncoming(
+       optionalStartRow int,
+       deltaRows int,
+       optionalLongPollMsTimeout int,
+) (*IncomingHistory, error) {
+
+       res, status, err := common.HttpGet(
+               WIRE_GATEWAY_HISTORY_INCOMING_API,
+               nil,
+               buildHistoryQueryMap(optionalStartRow, deltaRows, 
optionalLongPollMsTimeout),
+               common.NewJsonCodec[IncomingHistory](),
+       )
+
+       if err != nil {
+               return nil, err
+       }
+
+       if status == common.HTTP_OK {
+
+               return res, nil
+       }
+
+       if status == common.HTTP_BAD_REQUEST {
+
+               return nil, errors.New("request malformed")
+       }
+
+       if status == common.HTTP_UNAUTHORIZED {
+
+               return nil, errors.New("authentication failed, likely the 
credentials are wrong")
+       }
+
+       if status == common.HTTP_NOT_FOUND {
+
+               return nil, errors.New("the endpoint is wrong or the user name 
is unknown")
+       }
+
+       if status == common.HTTP_CONFLICT {
+
+               return nil, errors.New("a transaction with the same request_uid 
but different transaction details has been submitted before")
+       }
+
+       return nil, fmt.Errorf("HTTP %d - unexpected", 0)
+}
+
+// 
https://docs.taler.net/core/api-bank-wire.html#querying-the-transaction-history
+func (twg *TalerWirewatchGatewayImpl) WirewatchGatewayHistoryOutgoing(
+       optionalStartRow int,
+       deltaRows int,
+       optionalLongPollMsTimeout int,
+) (*OutgoingHistory, error) {
+
+       res, status, err := common.HttpGet(
+               WIRE_GATEWAY_HISTORY_OUTGOING_API,
+               nil,
+               buildHistoryQueryMap(optionalStartRow, deltaRows, 
optionalLongPollMsTimeout),
+               common.NewJsonCodec[OutgoingHistory](),
+       )
+
+       if err != nil {
+               return nil, err
+       }
+
+       if status == common.HTTP_OK {
+
+               return res, nil
+       }
+
+       if status == common.HTTP_BAD_REQUEST {
+
+               return nil, errors.New("request malformed")
+       }
+
+       if status == common.HTTP_UNAUTHORIZED {
+
+               return nil, errors.New("authentication failed, likely the 
credentials are wrong")
+       }
+
+       if status == common.HTTP_NOT_FOUND {
+
+               return nil, errors.New("the endpoint is wrong or the user name 
is unknown")
+       }
+
+       if status == common.HTTP_CONFLICT {
+
+               return nil, errors.New("a transaction with the same request_uid 
but different transaction details has been submitted before")
+       }
+
+       return nil, fmt.Errorf("HTTP %d - unexpected", 0)
+}
+
+func buildHistoryQueryMap(
+       optionalStartRow int,
+       deltaRows int,
+       optionalLongPollMsTimeout int,
+) map[string]string {
+
+       startStr := strconv.FormatInt(int64(optionalStartRow), 10)
+       deltaRowsStr := strconv.FormatInt(int64(deltaRows), 10)
+       longPollMsStr := strconv.FormatInt(int64(optionalLongPollMsTimeout), 10)
+
+       queryParams := map[string]string{
+               WIRE_GATEWAY_DELTA_QUERY: deltaRowsStr,
+       }
+       if optionalStartRow > 0 {
+               queryParams[WIRE_GATEWAY_START_QUERY] = startStr
+       }
+       if optionalLongPollMsTimeout > 0 {
+               queryParams[WIRE_GATEWAY_LONGPOLL_QUERY] = longPollMsStr
+       }
+
+       return queryParams
+}
diff --git a/nonce2ecash/pkg/taler-wirewatch-gateway/model.go 
b/nonce2ecash/pkg/taler-wirewatch-gateway/model.go
index 5c5861e..84a9250 100644
--- a/nonce2ecash/pkg/taler-wirewatch-gateway/model.go
+++ b/nonce2ecash/pkg/taler-wirewatch-gateway/model.go
@@ -1 +1,93 @@
 package talerwirewatchgateway
+
+import "nonce2ecash/pkg/common"
+
+// https://docs.taler.net/core/api-bank-wire.html#tsref-type-WireConfig
+type WireConfig struct {
+       Name           string `json:"name"`
+       Version        string `json:"version"`
+       Currency       string `json:"currency"`
+       Implementation string `json:"implementation"`
+}
+
+// https://docs.taler.net/core/api-bank-wire.html#tsref-type-TransferRequest
+type TransferRequest struct {
+       RequestUid      common.HashCode      `json:"request_uid"`
+       Amount          common.Amount        `json:"amount"`
+       ExchangeBaseUrl string               `json:"exchange_base_url"`
+       Wtid            common.ShortHashCode `json:"wtid"`
+       CreditAccount   string               `json:"credit_account"`
+}
+
+// https://docs.taler.net/core/api-bank-wire.html#tsref-type-TransferResponse
+type TransferResponse struct {
+       Timestamp common.Timestamp `json:"timestamp"`
+       RowId     int              `json:"row_id"`
+}
+
+// 
https://docs.taler.net/core/api-bank-wire.html#tsref-type-IncomingBankTransaction
+// type IncomingBankTransaction = IncomingReserveTransaction | 
IncomingWadTransaction
+type IncomingBankTransaction struct {
+       IncomingReserveTransaction
+       IncomingWadTransaction
+}
+
+// https://docs.taler.net/core/api-bank-wire.html#tsref-type-IncomingHistory
+type IncomingHistory struct {
+       IncomingTransactions []IncomingBankTransaction 
`json:"incoming_transactions"`
+       CreditAccount        string                    `json:"credit_account"`
+}
+
+// type RESERVE | 
https://docs.taler.net/core/api-bank-wire.html#tsref-type-IncomingReserveTransaction
+type IncomingReserveTransaction struct {
+       Type         string                `json:"type"`
+       RowId        int                   `json:"row_id"`
+       Date         common.Timestamp      `json:"date"`
+       Amount       common.Amount         `json:"amount"`
+       DebitAccount string                `json:"debit_account"`
+       ReservePub   common.EddsaPublicKey `json:"reserve_pub"`
+}
+
+// type WAD | 
https://docs.taler.net/core/api-bank-wire.html#tsref-type-IncomingWadTransaction
+type IncomingWadTransaction struct {
+       Type              string           `json:"type"`
+       RowId             int              `json:"row_id"`
+       Date              common.Timestamp `json:"date"`
+       Amount            common.Amount    `json:"amount"`
+       CreditAccount     string           `json:"credit_account"`
+       DebitAccount      string           `json:"debit_account"`
+       OriginExchangeUrl string           `json:"origin_exchange_url"`
+       WadId             common.WadId     `json:"wad_id"`
+}
+
+// https://docs.taler.net/core/api-bank-wire.html#tsref-type-OutgoingHistory
+type OutgoingHistory struct {
+       OutgoingBankTransaction []OutgoingBankTransaction 
`json:"outgoing_bank_transaction"`
+       DebitAccount            string                    `json:"debit_account"`
+}
+
+// 
https://docs.taler.net/core/api-bank-wire.html#tsref-type-OutgoingBankTransaction
+type OutgoingBankTransaction struct {
+       RowId           int                  `json:"row_id"`
+       Date            common.Timestamp     `json:"date"`
+       Amount          common.Amount        `json:"amount"`
+       CreditAccount   string               `json:"credit_account"`
+       Wtid            common.ShortHashCode `json:"wtid"`
+       ExchangeBaseUrl string               `json:"exchange_base_url"`
+}
+
+// ---------------------
+// TESTING (ONLY ADMINS)
+// ---------------------
+
+// https://docs.taler.net/core/api-bank-wire.html#tsref-type-AddIncomingRequest
+type AddIncomingRequest struct {
+       Amount       common.Amount         `json:"amount"`
+       ReservcePub  common.EddsaPublicKey `json:"reserve_pub"`
+       DebitAccount string                `json:"debit_account"`
+}
+
+// 
https://docs.taler.net/core/api-bank-wire.html#tsref-type-AddIncomingResponse
+type AddIncomingResponse struct {
+       Timestamp common.Timestamp `json:"timestamp"`
+}
diff --git a/specs/api-nonce2ecash.rst b/specs/api-nonce2ecash.rst
index 0d88de5..9e69e95 100644
--- a/specs/api-nonce2ecash.rst
+++ b/specs/api-nonce2ecash.rst
@@ -1,7 +1,7 @@
 ..
   This file is part of GNU TALER.
 
-  Copyright (C) 2014-2023 Taler Systems SA
+  Copyright (C) 2014-2024 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero General Public License as published by the Free 
Software
@@ -16,15 +16,28 @@
 
   @author Joel Häberli
 
-=====================
-Taler nonce2ecash API
-=====================
+===========================
+The nonce2ecash RESTful API
+===========================
 
-This chapter describe the APIs that third party providers need to integrate 
withdrawal through indirect
-payment channels like credit cards or ATM.
+.. note::
+  
+  **This API is experimental and not yet implemented**
+
+This chapter describe the APIs that third party providers need to integrate to 
allow 
+withdrawals through indirect payment channels like credit cards or ATM.
 
 .. contents:: Table of Contents
 
+--------------
+Authentication
+--------------
+
+Terminals which authenticate against the nonce2ecash API must provide their 
respective 
+access token. Therefore they provide a ``Authorization: Bearer $ACCESS_TOKEN`` 
header, 
+where `$ACCESS_TOKEN`` is a secret authentication token configured by the 
exchange and
+must begin with the RFC 8959 prefix.
+
 ----------------------------
 Configuration of nonce2ecash
 ----------------------------
@@ -53,7 +66,6 @@ Configuration of nonce2ecash
       version: string;
     }
 
-
 -----------------------------
 Withdrawing using nonce2ecash
 -----------------------------
@@ -101,21 +113,21 @@ operation (the ``WITHDRAWAL_ID``) to interact with the 
withdrawal operation and
 
   **Response:**
 
-    .. ts:def:: CashlessWithdrawal
+  :http:statuscode:`200 Ok`:
+    The withdrawal was found and is returned in the response body as 
``Withdrawal``.
+  :http:statuscode:`404 Not found`:
+    nonce2ecash does not have a withdrawal registered with the specified 
``WITHDRAWAL_ID``.
 
-    interface CashlessWithdrawal {
+  **Details**
 
-      // Reserve public key generated by the wallet.
-      // According to TALER_ReservePublicKeyP 
(https://docs.taler.net/core/api-common.html#cryptographic-primitives)
-      reserve_pub_key: EddsaPublicKey;
-    }
+    .. ts:def:: Withdrawal
 
-  :http:statuscode:`204 No content`:
-    The withdrawal was successfully registered.
-  :http:statuscode:`404 Bad request`:
-    No withdrawal with this ``WITHDRAWAL_ID`` exists.
-  :http:statuscode:`500 Internal Server error`:
-    The registration of the withdrawal failed due to server side issues.
+      interface Withdrawal {
+
+        // Reserve public key generated by the wallet.
+        // According to TALER_ReservePublicKeyP 
(https://docs.taler.net/core/api-common.html#cryptographic-primitives)
+        reserve_pub_key: EddsaPublicKey;
+      }
 
 .. http:post:: /withdrawal-operation/$WITHDRAWAL_ID
 
diff --git a/specs/overview.plantuml b/specs/overview.plantuml
new file mode 100644
index 0000000..fcbffbd
--- /dev/null
+++ b/specs/overview.plantuml
@@ -0,0 +1,40 @@
+@startuml
+
+actor Customer
+actor "Terminal Owner" as TerminalOwner
+
+package "EBICS System" {
+    [EBICS] as EBICS
+}
+
+package "Taler Exchange" {
+    [Bank Integration API] as BankIntegrationAPI
+    [Wirewatch Gateway] as WirewatchGateway
+    [nonce2ecash] as Nonce2Ecash
+    [Wallet] as Wallet
+    database "Exchange-DB" as ExDB
+}
+
+package "Wallee System" {
+    [Wallee Terminal] as WalleeTerminal
+    [Wallee Backend] as WalleeBackend
+}
+
+Customer --> Wallet
+TerminalOwner --> WalleeTerminal
+
+Nonce2Ecash --> ExDB
+WirewatchGateway --> ExDB
+BankIntegrationAPI --> ExDB
+Nonce2Ecash --> WirewatchGateway
+Nonce2Ecash --> BankIntegrationAPI
+Wallet --> WalleeTerminal
+Wallet --> Nonce2Ecash
+Wallet --> BankIntegrationAPI
+WirewatchGateway --> EBICS
+WalleeBackend --> EBICS
+WalleeTerminal --> WalleeBackend
+WalleeTerminal --> Nonce2Ecash
+Nonce2Ecash --> WalleeBackend
+
+@enduml

-- 
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]