[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.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-cashless2ecash] branch master updated: code: add bank integration and wirewatch gateway api,
gnunet <=