gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-exchange] branch stable updated (265af18 -> 111f8f2)


From: gnunet
Subject: [GNUnet-SVN] [taler-exchange] branch stable updated (265af18 -> 111f8f2)
Date: Mon, 05 Feb 2018 10:55:04 +0100

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

marcello pushed a change to branch stable
in repository exchange.

    from 265af18  log rowids and amounts
     add cf9e5ef  use dual stack for fakebank
     add 2a62f13  use dual stack for exchange
     add f93abc2  re-enable bank /reject testing now that bank implements it 
(closes #5200)
     add 9ea3145  respect /reject API: credit_account -> account_number
     add 22eae12  logging the /reject requester in tests as the deposit 
_credit_ account
     add 0f5ef01  nicer error reporting
     add c182b96  remove _admin APIs from libtalerexchange
     add f7c9de7  remove _admin APIs from libtalerexchange
     add 1897d65  eliminate /admin/add/incoming (fixes #5172)
     add 229907c  implement taler-bank-transfer, fixes #5195
     add 7e966aa  store password in the right variable
     add ee26302  make DEBUG bulk logs
     add 8021e70  adding more bank's error codes
     add 08e0d56  update changelog
     add 346c351  redefining/adding bank error codes
     add 4f2ad10  change 'f' to 'contribution' in /deposit
     add 2d08d61  refuse endian conversion for invalid amounts
     add ff0d4bb  add TALER_EXCHANGE_refund2, make sure fee test initializes 
all amounts; update ChangeLog
     add dad63db  extending test logic to detect refund issue (commented out 
for now)
     add 41b5dde  implement select_refunds_by_coin in exchangedb plugin
     add d9f16db  add testcase for 'select_refunds_by_coin'
     add 5587732  fix auditor to properly verify wire fee signatures and more 
gracefully handle arithmetic amount issue
     add 067891b  fix audit report formatting issues
     add 7d60d3a  nicer formatting, some DCE
     add 0906696  bugfixes in auditor and rendering
     add 77a5867  comment out extended test again
     add 23cb23b  fix minor formatting issue
     add 164dd0f  fix #5234
     add 57eb859  enable test for #5234
     add acc3a41  add pay session signature
     add 9fffeee  merge changelog
     add 2526143  fix type for bool to match what postgres returns -- 1 byte 
instead of 4 bytes
     add b31d803  add timestamp for /reserve/status deposit events
     add c300b88  remove dead error code
     add bc7957f  fix fakebank memory leak
     add 146cc27  use saner log level
     add 63c96a1  remove obsolete paragraph
     add 5c3d25e  implement #5254
     add cd0f9e8  Florian is right, this should be a 409, not 500
     add 82b0eda  new test interpreter architecture, first draft
     add 6ba7747  more traits
     add bba0ec7  finish new withdraw command implementation
     add 673e418  forgotten file
     add 41cb8b4  integrate sigpipe with ain loop
     add d09beec  integrate sigpipe with ain loop
     add a252daa  factor out more helpers
     add 3f930c0  more wire transfer variants
     add 8013c4b  use CONFIG_FILE macro
     add 652bb0f  finish simplifying main by adding more helpers and macros
     add de092c2  make fakebank_url more robust
     add bb39c62  add missing cmd file
     add 9f15044  error codes for merchant /check-payment
     add afe9451  new error code for #5262
     add d126b16  serialize amounts as string instead of an object
     add e318edb  use url instead of uri consistently
     add 91690a9  missed on URI
     add 2d976fc  use url in docs consistently
     add 6f9354c  better error message
     add 3859a40  url construction helpers
     add 458b317  more tests, fix varargs invocation
     add f806eeb  oops, add missing file
     add 111f8f2  url joining: grow strings correctly

No new revisions were added by this update.

Summary of changes:
 .gitignore                                         |   3 +-
 ChangeLog                                          |  16 +
 contrib/auditor-report.tex.j2                      |  39 +-
 .../exchange-template/config/exchange-common.conf  |   2 +-
 doc/Makefile.am                                    |   3 +-
 doc/taler-bank-transfer.1                          |  50 ++
 doc/taler-exchange-reservemod.1                    |  41 --
 doc/taler-exchange.texi                            |  41 +-
 src/auditor/taler-auditor-sign.c                   |   2 +-
 src/auditor/taler-auditor.c                        |  97 ++--
 src/bank-lib/Makefile.am                           |  13 +
 src/bank-lib/bank_api_common.c                     |   2 +-
 src/bank-lib/bank_api_common.h                     |   2 +-
 src/bank-lib/bank_api_history.c                    |   4 +-
 src/bank-lib/bank_api_reject.c                     |   2 +-
 src/bank-lib/fakebank.c                            |  12 +-
 src/bank-lib/taler-bank-transfer.c                 | 264 +++++++++++
 src/bank-lib/test_bank_api.c                       |   2 -
 src/bank-lib/test_bank_interpreter.c               |   6 +-
 src/benchmark/bank_details.json                    |   2 +-
 src/benchmark/merchant_details.json                |   2 +-
 src/benchmark/taler-exchange-benchmark.c           |  44 +-
 src/benchmark/taler-exchange-benchmark.conf        |   2 +-
 .../test_benchmark_home/.config/taler/test.json    |   4 +-
 src/exchange-lib/Makefile.am                       |  56 ++-
 src/exchange-lib/exchange_api_admin.c              | 250 ----------
 src/exchange-lib/exchange_api_deposit.c            |   2 +-
 src/exchange-lib/exchange_api_handle.c             |   6 +-
 src/exchange-lib/exchange_api_handle.h             |   4 +-
 src/exchange-lib/exchange_api_refund.c             |  73 ++-
 src/exchange-lib/exchange_api_reserve.c            |  13 +-
 src/exchange-lib/test_exchange_api.c               |  91 +++-
 src/exchange-lib/test_exchange_api.conf            |   2 +-
 .../test_exchange_api_home/.config/taler/test.json |   4 +-
 .../test_exchange_api_keys_cherry_picking.conf     |   2 +-
 src/exchange-lib/test_exchange_api_new.c           | 142 ++++++
 src/exchange-lib/testing_api_cmd_exec_wirewatch.c  | 162 +++++++
 .../testing_api_cmd_fakebank_transfer.c            | 414 +++++++++++++++++
 src/exchange-lib/testing_api_cmd_withdraw.c        | 355 ++++++++++++++
 src/exchange-lib/testing_api_helpers.c             | 370 +++++++++++++++
 src/exchange-lib/testing_api_loop.c                | 492 ++++++++++++++++++++
 src/exchange-lib/testing_api_trait_blinding_key.c  |  67 +++
 src/exchange-lib/testing_api_trait_coin_priv.c     |  67 +++
 src/exchange-lib/testing_api_trait_denom_pub.c     |  67 +++
 src/exchange-lib/testing_api_trait_denom_sig.c     |  67 +++
 src/exchange-lib/testing_api_trait_process.c       |  67 +++
 src/exchange-lib/testing_api_trait_reserve_priv.c  |  65 +++
 src/exchange-lib/testing_api_traits.c              |  70 +++
 src/exchange-tools/Makefile.am                     |  18 -
 src/exchange-tools/taler-exchange-reservemod.c     | 225 ---------
 src/exchange/Makefile.am                           |   1 -
 src/exchange/exchange.conf                         |  15 -
 src/exchange/taler-config-generate                 |  28 +-
 src/exchange/taler-exchange-aggregator.c           |  81 ++++
 src/exchange/taler-exchange-httpd.c                | 241 +---------
 src/exchange/taler-exchange-httpd_admin.c          | 218 ---------
 src/exchange/taler-exchange-httpd_admin.h          |  46 --
 src/exchange/taler-exchange-httpd_deposit.c        |   6 +-
 src/exchange/taler-exchange-httpd_keystate.c       |  21 +-
 src/exchange/taler-exchange-httpd_refund.c         |   1 -
 src/exchange/taler-exchange-httpd_responses.c      |   7 +-
 src/exchange/taler-exchange-httpd_track_transfer.c |   2 +
 src/exchange/taler-exchange-wirewatch.c            |   4 +-
 .../test-taler-exchange-aggregator-postgres.conf   |   2 +-
 .../test-taler-exchange-wirewatch-postgres.conf    |   2 +-
 src/exchange/test_taler_exchange_aggregator.c      |   2 +-
 src/exchange/test_taler_exchange_httpd.conf        |   2 +-
 .../.config/taler/test.json                        |   4 +-
 src/exchangedb/plugin_exchangedb_postgres.c        | 162 ++++++-
 src/exchangedb/test_exchangedb.c                   |  96 +++-
 src/exchangedb/test_exchangedb_fees.c              |   6 +
 src/include/Makefile.am                            |   1 +
 src/include/taler_bank_service.h                   |   2 +-
 src/include/taler_error_codes.h                    | 130 +++++-
 src/include/taler_exchange_service.h               | 125 +++--
 src/include/taler_exchangedb_plugin.h              |  44 ++
 src/include/taler_signatures.h                     |  31 ++
 src/include/taler_testing_lib.h                    | 508 +++++++++++++++++++++
 src/include/taler_util.h                           |  59 +++
 src/json/json_helper.c                             |  47 +-
 src/json/test_json.c                               |   4 +-
 src/util/Makefile.am                               |  12 +-
 src/util/amount.c                                  |   4 +
 src/util/test_url.c                                |  83 ++++
 src/util/util.c                                    | 300 ++++++++++++
 src/wire/plugin_wire_template.c                    |  14 +-
 src/wire/plugin_wire_test.c                        |  67 +--
 src/wire/test_wire_plugin.c                        |   2 +-
 src/wire/test_wire_plugin.conf                     |   4 +-
 src/wire/test_wire_plugin_test.json                |   4 +-
 src/wire/test_wire_plugin_transactions_test.c      |   2 +-
 src/wire/test_wire_plugin_transactions_test.conf   |   4 +-
 src/wire/wire-test.conf                            |   4 +-
 93 files changed, 4780 insertions(+), 1424 deletions(-)
 create mode 100644 doc/taler-bank-transfer.1
 delete mode 100644 doc/taler-exchange-reservemod.1
 create mode 100644 src/bank-lib/taler-bank-transfer.c
 delete mode 100644 src/exchange-lib/exchange_api_admin.c
 create mode 100644 src/exchange-lib/test_exchange_api_new.c
 create mode 100644 src/exchange-lib/testing_api_cmd_exec_wirewatch.c
 create mode 100644 src/exchange-lib/testing_api_cmd_fakebank_transfer.c
 create mode 100644 src/exchange-lib/testing_api_cmd_withdraw.c
 create mode 100644 src/exchange-lib/testing_api_helpers.c
 create mode 100644 src/exchange-lib/testing_api_loop.c
 create mode 100644 src/exchange-lib/testing_api_trait_blinding_key.c
 create mode 100644 src/exchange-lib/testing_api_trait_coin_priv.c
 create mode 100644 src/exchange-lib/testing_api_trait_denom_pub.c
 create mode 100644 src/exchange-lib/testing_api_trait_denom_sig.c
 create mode 100644 src/exchange-lib/testing_api_trait_process.c
 create mode 100644 src/exchange-lib/testing_api_trait_reserve_priv.c
 create mode 100644 src/exchange-lib/testing_api_traits.c
 delete mode 100644 src/exchange-tools/taler-exchange-reservemod.c
 delete mode 100644 src/exchange/taler-exchange-httpd_admin.c
 delete mode 100644 src/exchange/taler-exchange-httpd_admin.h
 create mode 100644 src/include/taler_testing_lib.h
 create mode 100644 src/util/test_url.c

diff --git a/.gitignore b/.gitignore
index 5f227d1..7bf23b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,7 +50,6 @@ src/exchange-tools/taler-auditor-sign
 src/exchange-tools/taler-exchange-dbinit
 src/exchange-tools/taler-exchange-keycheck
 src/exchange-tools/taler-exchange-keyup
-src/exchange-tools/taler-exchange-reservemod
 src/exchange-tools/taler-exchange-wire
 src/exchangedb/perf-exchangedb
 src/benchmark/taler-exchange-benchmark
@@ -92,3 +91,5 @@ contrib/auditor-report.aux
 contrib/auditor-report.log
 contrib/auditor-report.tex
 contrib/auditor-report.pdf
+src/bank-lib/taler-bank-transfer
+src/exchange-lib/test_exchange_api_new
diff --git a/ChangeLog b/ChangeLog
index 2948a76..e5d3eaf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+Thu Jan  4 11:55:41 CET 2018
+       Fix issue #5234 (aggregator ignoring refunds).
+       Misc. minor fixes to the auditor. -CG
+
+Mon Jan  1 23:15:37 CET 2018
+       Add TALER_EXCHANGE_refund2() API call to libtalerexchange. -CG
+
+Thu Dec 14 15:32:50 CET 2017
+       Replaced taler-exchange-reservemod tool with new taler-bank-transfer
+       tool (#5195).  Removed /admin/add/incoming API, replaced by new
+       taler-exchange-wirewatch tooling. (#5077).
+
+Sat Dec  9 15:21:50 CET 2071
+       Implement refresh protocol optimization, bumping protocol version
+       to v2 (#5178). -CG
+
 Thu Nov  2 17:39:40 CET 2017
        Limit amount values to 2^53 as we always wanted (#5167). -CG
 
diff --git a/contrib/auditor-report.tex.j2 b/contrib/auditor-report.tex.j2
index 0fbf74c..3807330 100644
--- a/contrib/auditor-report.tex.j2
+++ b/contrib/auditor-report.tex.j2
@@ -102,8 +102,7 @@ making wire transfers that have been due.
 
 The total amount the exchange currently lags behind is
 {\bf  {{ wire.total_amount_lag.value }}.{{ wire.total_amount_lag.fraction }}
-      {{ wire.total_amount_lag.currency }}
-}.
+      {{ wire.total_amount_lag.currency }}}.
 
 Note that some lag is perfectly normal, as tiny amounts that are too small to 
be wired
 are deferred beyond the due date, hoping that additional transfers will push 
them above
@@ -210,7 +209,7 @@ the financial damage done to the customer).
 {% if data.amount_arithmetic_inconsistencies|length() == 0 %}
   {\bf No arithmetic problems detected.}
 {% else %}
-  \begin{longtable}{p{4.5cm}|l|rl|rl}
+  \begin{longtable}{p{3.5cm}|l|rl|rl}
   {\bf Operation} & {\bf Table row} & \multicolumn{2}{|c|}{ {\bf Exchange}} & 
\multicolumn{2}{|c}{ {\bf Auditor}} \\
   \hline \hline
 \endfirsthead
@@ -219,11 +218,11 @@ the financial damage done to the customer).
   \hline \hline
   {\bf Operation} & {\bf Table row} & \multicolumn{2}{|c|}{ {\bf Exchange}} & 
\multicolumn{2}{|c}{ {\bf Auditor}} \\
 \endfoot
-  \hline
-  {\bf Total} & &
-  {{ data.total_arithmetic_delta_plus.value }}.{{ 
data.total_arithmetic_delta_plus.fraction }} &
+  \hline \hline
+  \multicolumn{2}{l|}{ {\bf $\sum$ Deltas (Auditor-Exchange)} } &
+  + {{ data.total_arithmetic_delta_plus.value }}.{{ 
data.total_arithmetic_delta_plus.fraction }} &
   {{ data.total_arithmetic_delta_plus.currency }} &
-  {{ data.total_arithmetic_delta_minus.value }}.{{ 
data.total_arithmetic_delta_minus.fraction }} &
+  - {{ data.total_arithmetic_delta_minus.value }}.{{ 
data.total_arithmetic_delta_minus.fraction }} &
   {{ data.total_arithmetic_delta_minus.currency }} \\
     \caption{Arithmetic inconsistencies.}
     \label{table:amount:arithmetic:inconsistencies}
@@ -328,17 +327,17 @@ any effects on its own balance, those entries are 
excluded from the total.
 {% if data.coin_inconsistencies|length() == 0 %}
   {\bf All coin histories were unproblematic.}
 {% else %}
-  \begin{longtable}{l|p{5.5cm}|rl|rl}
-  {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf 
Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\
+  \begin{longtable}{p{1.8cm}|p{3cm}|rl|rl}
+  {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf 
Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\
   \hline \hline
 \endfirsthead
-  {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf 
Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\ \hline \hline
+  {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf 
Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\ \hline \hline
 \endhead
   \hline \hline
-  {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf 
Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\
+  {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf 
Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\
 \endfoot
   \hline
-  {\bf Total} & &
+  \multicolumn{2}{l|}{ $\sum$ {\bf Delta (Auditor-Exchange)} } &
   {{ data.total_coin_delta_plus.value }}.{{ 
data.total_coin_delta_plus.fraction }} &
   {{ data.total_coin_delta_plus.currency }} &
   - {{ data.total_coin_delta_minus.value }}.{{ 
data.total_coin_delta_minus.fraction }} &
@@ -593,7 +592,7 @@ have a clear financial impact.
   \hline
   {\bf Table} & {\bf Row} & {\bf Diagnostic} \\
   \caption{Other wire table issues found (by table and row).}
-  \label{table:misc}
+  \label{table:wire:misc}
 \endlastfoot
 {% for item in wire.row_inconsistencies %}
   \verb! {{ item.table }} ! &
@@ -687,7 +686,7 @@ impact.
 {% if data.row_inconsistencies|length() == 0 %}
   {\bf No row inconsistencies found.}
 {% else %}
-  \begin{longtable}{p{1.5cm}|l|p{5.5}}
+  \begin{longtable}{p{1.5cm}|l|p{5.5cm}}
   {\bf Table} & {\bf Row} & {\bf Diagnostic} \\
   \hline \hline
 \endfirsthead
@@ -697,13 +696,13 @@ impact.
   \hline \hline
   {\bf Table} & {\bf Row} & {\bf Diagnostic} \\
 \endfoot
-  \hline
+  \hline \hline
   {\bf Table} & {\bf Row} & {\bf Diagnostic} \\
   \caption{Other issues found (by table and row).}
   \label{table:misc}
 \endlastfoot
 {% for item in data.row_inconsistencies %}
-  {{ item.table }} &
+  \verb! {{ item.table }} ! &
   {{ item.row }} &
   {{ item.diagnostic }} \\ \hline
 {% endfor %}
@@ -728,12 +727,12 @@ reserve expired.
   {\bf All expired reserves were closed.}
 {% else %}
   \begin{longtable}{p{1.5cm}|c|rl}
-  {\bf Reserve} & {\bf Expired} & \multicolumn{2}{|c|}{ {\bf Balance}} \\ 
\hline \hline
+  {\bf Reserve} & {\bf Expired} & \multicolumn{2}{|c}{ {\bf Balance}} \\ 
\hline \hline
 \endfirsthead
-  {\bf Reserve} & {\bf Expired} & \multicolumn{2}{|c|}{ {\bf Balance}} \\ 
\hline \hline
+  {\bf Reserve} & {\bf Expired} & \multicolumn{2}{|c}{ {\bf Balance}} \\ 
\hline \hline
 \endhead
   \hline \hline
-  {\bf Reserve} & {\bf Expired} & \multicolumn{2}{|c|}{ {\bf Balance}}
+  {\bf Reserve} & {\bf Expired} & \multicolumn{2}{|c}{ {\bf Balance}}
 \endfoot
   \hline
   {\bf Sum}     & &
@@ -854,7 +853,7 @@ This section lists issues with wire transfers related to 
timestamps.
   \label{table:wire:bad_time}
 \endlastfoot
 {% for item in wire.row_minor_inconsistencies %}
-  {\tt {{ item.table }} } & {{ item.row }} & {{ item.diagnostic }} \\ \hline
+  \verb! {{ item.table }} ! & {{ item.row }} & {{ item.diagnostic }} \\ \hline
 {% endfor %}
   \end{longtable}
 {% endif %}
diff --git a/contrib/exchange-template/config/exchange-common.conf 
b/contrib/exchange-template/config/exchange-common.conf
index 78ee939..4778265 100644
--- a/contrib/exchange-template/config/exchange-common.conf
+++ b/contrib/exchange-template/config/exchange-common.conf
@@ -37,7 +37,7 @@ ENABLE = YES
 TEST_RESPONSE_FILE = "test.json"
 
 # What is the main website of the bank?
-BANK_URI = "https://bank/";
+BANK_URL = "https://bank/";
 # Into which account at the 'bank' should incoming
 # wire transfers be made?
 BANK_ACCOUNT_NUMBER = 2
diff --git a/doc/Makefile.am b/doc/Makefile.am
index fe997cc..cfcbea6 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -5,13 +5,13 @@ SUBDIRS = .
 man_MANS = \
   taler-auditor.1 \
   taler-auditor-sign.1 \
+  taler-bank-transfer.1 \
   taler-config-generate.1 \
   taler-exchange-aggregator.1 \
   taler-exchange-dbinit.1 \
   taler-exchange-httpd.1 \
   taler-exchange-keyup.1 \
   taler-exchange-keycheck.1 \
-  taler-exchange-reservemod.1 \
   taler-exchange-wire.1 \
   taler-exchange-wirewatch.1 \
   taler.conf.5
@@ -35,4 +35,3 @@ EXTRA_DIST = \
   docstyle.css \
   brown-paper.css \
   exchange-db.png
-
diff --git a/doc/taler-bank-transfer.1 b/doc/taler-bank-transfer.1
new file mode 100644
index 0000000..da13229
--- /dev/null
+++ b/doc/taler-bank-transfer.1
@@ -0,0 +1,50 @@
+.TH TALER\-BANK\-TRANSFER 1 "Dec 14, 2017" "GNU Taler"
+
+.SH NAME
+taler\-bank\-transfer \- Trigger a transfer at the bank
+
+.SH SYNOPSIS
+.B taler\-bank\-transfer
+.RI [ options ]
+.br
+
+.SH DESCRIPTION
+\fBtaler\-bank\-transfer\fP is a command line tool to trigger bank transfers.
+
+.SH OPTIONS
+.B
+.IP "\-a VALUE,  \-\-amount=VALUE"
+Amount to transfer.  Given in the Taler\-typical format of 
CURRENCY:VALUE.FRACTION
+.B
+.IP "\-b URL,  \-\-bank=URL"
+URL at which the bank is operation.
+.B
+.IP "\-c FILENAME,  \-\-config=FILENAME"
+Use the given configuration file.
+.B
+.IP "\-h, \-\-help"
+Print short help on options.
+.B
+.IP "\-D ACCOUNT,  \-\-debit=ACCOUNT"
+The money should be debited from ACCOUNT.  Specifies the number of the account.
+.B
+.IP "\-C ACCOUNT,  \-\-credit=ACCOUNT"
+The money should be credited to ACCOUNT.  Specifies the number of the account.
+.B
+.IP "\-s STRING,  \-\-subject=STRING"
+Use STRING for the wire transfer subject.
+.B
+.IP "\-u USERNAME,  \-\-user=USERNAME"
+Specifies the username for authentication.
+.B
+.IP "\-p PASSPHRASE,  \-\-pass=PASSPHRASE"
+Specifies the pass phrase for authentication.
+.B
+.IP "\-v, \-\-version"
+Print version information.
+
+.SH BUGS
+Report bugs by using Mantis <https://gnunet.org/bugs/> or by sending 
electronic mail to <address@hidden>
+
+.SH "SEE ALSO"
+\fBtaler\-bank\-manage\fP(1), \fBtaler.conf\fP(5)
diff --git a/doc/taler-exchange-reservemod.1 b/doc/taler-exchange-reservemod.1
deleted file mode 100644
index c74eafb..0000000
--- a/doc/taler-exchange-reservemod.1
+++ /dev/null
@@ -1,41 +0,0 @@
-.TH TALER\-EXCHANGE\-RESERVEMOD 1 "Apr 22, 2015" "GNU Taler"
-
-.SH NAME
-taler\-exchange\-reservemod \- Modify reserve balance in the Taler exchange 
database.
-
-.SH SYNOPSIS
-.B taler\-exchange\-reservemod
-.RI [ options ]
-.br
-
-.SH DESCRIPTION
-\fBtaler\-exchange\-reservemod\fP is a command line tool to modify reserves in 
the Taler exchange database.  Basically, it can be used to import deposits, 
either for testing or as part of the import from the list of incoming 
transactions.
-
-.SH OPTIONS
-.B
-.IP "\-a DENOM,  \-\-add=DENOM"
-Amount to add to the reserve.
-.B
-.IP "\-d DIRNAME,  \-\-exchange-dir=DIRNAME"
-Use the configuration and other resources for the exchange to operate from 
DIRNAME.
-.B
-.IP "\-h, \-\-help"
-Print short help on options.
-.B
-.IP "\-s JSON,  \-\-sender=JSON"
-JSON-formatted acount details of the sender of the wire transfer.
-.B
-.IP "\-t JSON,  \-\-transfer=JSON"
-JSON-formatted details that uniquely identify the wire transfer.
-.B
-.IP "\-R KEY,  \-\-reserve=KEY"
-Public EdDSA key of the reserve to modify.
-.B
-.IP "\-v, \-\-version"
-Print version information.
-
-.SH BUGS
-Report bugs by using Mantis <https://gnunet.org/bugs/> or by sending 
electronic mail to <address@hidden>
-
-.SH "SEE ALSO"
-\fBtaler\-exchange\-httpd\fP(1), \fBtaler\-exchange\-keyup\fP(1), 
\fBtaler\-exchange\-dbinit\fP(1), \fBtaler.conf\fP(5)
diff --git a/doc/taler-exchange.texi b/doc/taler-exchange.texi
index 558a57e..9f5fd10 100644
--- a/doc/taler-exchange.texi
+++ b/doc/taler-exchange.texi
@@ -400,13 +400,10 @@ gap. See keys-duration.
 @section Serving
 
 
-The exchange can serve HTTP over both TCP and UNIX domain socket. It
-needs this configuration @emph{twice}, because it opens one connection
-for ordinary REST calls, and one for "/admin" and "/test" REST calls,
-because the operator may want to restrict the access to "/admin".
+The exchange can serve HTTP over both TCP and UNIX domain socket.
 
-The following values are to be configured under the section
address@hidden and @cite{[exchange-admin]}:
+The following values are to be configured in the section
address@hidden:
 
 
 @itemize
@@ -425,13 +422,9 @@ The following values are to be configured under the section
 
 @item
 @cite{unixpath_mode}: number giving the mode with the access
-permissiON MASK for the @cite{unixpath} (i.e. 660 = rw-rw----).
+permission MASK for the @cite{unixpath} (i.e. 660 = rw-rw----).
 @end itemize
 
-The exchange can be started with the @cite{-D} option to disable the 
administrative
-functions entirely.  It is recommended that the administrative API is only
-accessible via a properly protected UNIX domain socket.
-
 
 @node Currency
 @section Currency
@@ -456,7 +449,7 @@ To enable the wire transfer method ``test'', you need to set
 
 The bank account where the exchange gets money from customers is
 configured under the section @cite{[exchange-wire-test]}.  It must
-contain the options ``EXCHANGE_ACCOUNT_NO'' and ``BANK_URI''.
+contain the options ``EXCHANGE_ACCOUNT_NO'' and ``BANK_URL''.
 For basic authentication, it must additionally contain the
 ``USERNAME'' and ``PASSWORD'' of the exchange's account at the bank.
 
@@ -470,7 +463,7 @@ For example, the utility may be invoked as follows:
 
 @example
 $ taler-exchange-wire -j '@{"name": "The Exchange", "account_number":
-10, "bank_uri": "https://bank.demo.taler.net";, "type": "test"@}' -t
+10, "bank_url": "https://bank.demo.taler.net";, "type": "test"@}' -t
 test -o exchange.json
 @end example
 
@@ -489,18 +482,16 @@ To enable the wire transfer method ``sepa'', you need to 
set
 ``ENABLE=YES'' in section @cite{[exchange-wire-sepa]}.
 
 
-The bank account where the exchange gets money from customers is
-configured under the section @cite{[exchange-wire-incoming-sepa]}.  This
-section contains only one option: @cite{sepa_response_file}, which takes
-the path to a text file containing the exchange's bank account details
-in JSON format.
+This section contains only one option: @cite{sepa_response_file},
+which takes the path to a text file containing the exchange's bank
+account details in JSON format.
 
 The command line tool @cite{taler-exchange-wire} is used to create such a file.
 For example, the utility may be invoked as follows:
 
 @example
 $ taler-exchange-wire -j '@{"name": "The Exchange", "account_number":
-10, "bank_uri": "https://bank.demo.taler.net";, "type": "sepa"@}' -t
+10, "bank_url": "https://bank.demo.taler.net";, "type": "sepa"@}' -t
 sepa -o exchange.json
 @end example
 
@@ -525,7 +516,7 @@ options under @cite{[exchange-wire-outgoing-sepa]}: (NOT 
YET SUPPORTED).
 @cite{exchange_account_numer}: which bank account number has the exchange
 
 @item
address@hidden: base URL of the bank hosting the exchange bank account
address@hidden: base URL of the bank hosting the exchange bank account
 @end itemize
 @end quotation
 
@@ -673,10 +664,10 @@ get signed by every auditor he wishes (or is forced to) 
work with.
 
 In a normal scenario, an auditor must have some way of receiving the blob to
 sign (Website, manual delivery, ..).  Nonetheless, the exchange admin can fake
-an auditor signature - for testing purposes - by running the following command
+an auditor signature --- for testing purposes --- by running the following 
command
 
 @example
-taler-auditor-sign -m EXCHANGE_MASTER_PUB -r BLOB -u AUDITOR_URI -o OUTPUT_FILE
+taler-auditor-sign -m EXCHANGE_MASTER_PUB -r BLOB -u AUDITOR_URL -o OUTPUT_FILE
 @end example
 
 Those arguments are all mandatory.
@@ -686,14 +677,14 @@ Those arguments are all mandatory.
 public key.  Tipically, this value lies in the configuration option
 @code{[exchange]/master_public_key}.
 @item @code{BLOB} the blob generated in the previous step.
address@hidden @code{AUDITOR_URI} the URI that identifies the auditor.
address@hidden @code{AUDITOR_URL} the URL that identifies the auditor.
 @item @code{OUTPUT_FILE} where on the disk the signed blob is to be saved.
 @end itemize
 
 @code{OUTPUT_FILE} must then be copied into the directory specified
 by the option @code{AUDITOR_BASE_DIR} under the section @code{[exchangedb]}.
 Assuming @code{AUDITOR_BASE_DIR = 
address@hidden@}/.local/share/taler/auditors}, the
-following command will "add" the auditor identified by @code{AUDITOR_URI} to
+following command will "add" the auditor identified by @code{AUDITOR_URL} to
 the exchange.
 
 @example
@@ -701,7 +692,7 @@ cp OUTPUT_FILE address@hidden@}/.local/share/taler/auditors
 @end example
 
 If the auditor has been correctly added, the exchange's @code{/keys} response
-must contain an entry in the @code{auditors} array mentioning the auditor's 
URI.
+must contain an entry in the @code{auditors} array mentioning the auditor's 
URL.
 
 
 @c FIXME: reference section about where keys are stored.
diff --git a/src/auditor/taler-auditor-sign.c b/src/auditor/taler-auditor-sign.c
index 70c4098..964480a 100644
--- a/src/auditor/taler-auditor-sign.c
+++ b/src/auditor/taler-auditor-sign.c
@@ -281,7 +281,7 @@ main (int argc,
   if (0 == dks_len)
   {
     fprintf (stderr,
-             "Denomination list has length zero, signature not produced.\n");
+             "Failed to produce auditor signature, denomination list is 
empty.\n");
     GNUNET_DISK_file_close (fh);
     GNUNET_free (eddsa_priv);
     return 2;
diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
index 870a81a..e807378 100644
--- a/src/auditor/taler-auditor.c
+++ b/src/auditor/taler-auditor.c
@@ -391,7 +391,7 @@ report_amount_arithmetic_inconsistency (const char 
*operation,
                      "profitable", (json_int_t) profitable));
   if (0 != profitable)
   {
-    target = profitable
+    target = (1 == profitable)
       ? &total_arithmetic_delta_plus
       : &total_arithmetic_delta_minus;
     GNUNET_break (GNUNET_OK ==
@@ -452,7 +452,7 @@ report_coin_arithmetic_inconsistency (const char *operation,
                      "profitable", (json_int_t) profitable));
   if (0 != profitable)
   {
-    target = profitable
+    target = (1 == profitable)
       ? &total_coin_delta_plus
       : &total_coin_delta_minus;
     GNUNET_break (GNUNET_OK ==
@@ -1664,10 +1664,15 @@ struct WireFeeInfo
   struct GNUNET_TIME_Absolute end_date;
 
   /**
-   * How high is the fee.
+   * How high is the wire fee.
    */
   struct TALER_Amount wire_fee;
 
+  /**
+   * How high is the closing fee.
+   */
+  struct TALER_Amount closing_fee;
+
 };
 
 
@@ -1698,11 +1703,6 @@ struct AggregationContext
   struct WireFeeInfo *fee_tail;
 
   /**
-   * How much did we make in aggregation fees.
-   */
-  struct TALER_Amount total_aggregation_feesX;
-
-  /**
    * Final result status.
    */
   enum GNUNET_DB_QueryStatus qs;
@@ -1798,7 +1798,6 @@ struct WireCheckContext
  * @param dki denomination information about the coin
  * @param tl_head head of transaction history to verify
  * @param[out] merchant_gain amount the coin contributes to the wire transfer 
to the merchant
- * @param[out] merchant_fees fees the exchange charged the merchant for the 
transaction(s)
  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  */
 static int
@@ -1807,14 +1806,16 @@ check_transaction_history (const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
                            const struct TALER_MerchantPublicKeyP *merchant_pub,
                            const struct 
TALER_EXCHANGEDB_DenominationKeyInformationP *dki,
                            const struct TALER_EXCHANGEDB_TransactionList 
*tl_head,
-                           struct TALER_Amount *merchant_gain,
-                           struct TALER_Amount *merchant_fees)
+                           struct TALER_Amount *merchant_gain)
 {
   struct TALER_Amount expenditures;
   struct TALER_Amount refunds;
   struct TALER_Amount spent;
   struct TALER_Amount value;
   struct TALER_Amount merchant_loss;
+  struct TALER_Amount merchant_delta;
+  const struct TALER_Amount *deposit_fee;
+  int refund_deposit_fee;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Checking transaction history of coin %s\n",
@@ -1832,14 +1833,13 @@ check_transaction_history (const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
                                         merchant_gain));
   GNUNET_assert (GNUNET_OK ==
                  TALER_amount_get_zero (currency,
-                                        merchant_fees));
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_get_zero (currency,
                                         &merchant_loss));
   /* Go over transaction history to compute totals; note that we do not
      know the order, so instead of subtracting we compute positive
      (deposit, melt) and negative (refund) values separately here,
      and then subtract the negative from the positive after the loop. */
+  refund_deposit_fee = GNUNET_NO;
+  deposit_fee = NULL;
   for (const struct TALER_EXCHANGEDB_TransactionList *tl = tl_head;NULL != 
tl;tl = tl->next)
   {
     const struct TALER_Amount *amount_with_fee;
@@ -1871,7 +1871,7 @@ check_transaction_history (const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
       {
         struct TALER_Amount amount_without_fee;
 
-        if (GNUNET_OK !=
+       if (GNUNET_OK !=
             TALER_amount_subtract (&amount_without_fee,
                                    amount_with_fee,
                                    fee))
@@ -1890,14 +1890,7 @@ check_transaction_history (const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                     "Detected applicable deposit of %s\n",
                     TALER_amount2s (&amount_without_fee));
-        if (GNUNET_OK !=
-            TALER_amount_add (merchant_fees,
-                              merchant_fees,
-                              fee))
-        {
-          GNUNET_break (0);
-          return GNUNET_SYSERR;
-        }
+       deposit_fee = fee;      
       }
       /* Check that the fees given in the transaction list and in dki match */
       TALER_amount_ntoh (&tmp,
@@ -1975,14 +1968,7 @@ check_transaction_history (const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                     "Detected applicable refund of %s\n",
                     TALER_amount2s (amount_with_fee));
-        if (GNUNET_OK !=
-            TALER_amount_add (merchant_fees,
-                              merchant_fees,
-                              fee))
-        {
-          GNUNET_break (0);
-          return GNUNET_SYSERR;
-        }
+       refund_deposit_fee = GNUNET_YES;
       }
       /* Check that the fees given in the transaction list and in dki match */
       TALER_amount_ntoh (&tmp,
@@ -2008,9 +1994,19 @@ check_transaction_history (const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
       }
       break;
     }
-
   } /* for 'tl' */
 
+  if ( (GNUNET_YES == refund_deposit_fee) &&
+       (NULL != deposit_fee) )
+  {
+    /* We had a /deposit operation AND a /refund operation,
+       and should thus not charge the merchant the /deposit fee */
+    GNUNET_assert (GNUNET_OK ==
+                  TALER_amount_add (merchant_gain,
+                                    merchant_gain,
+                                    deposit_fee));
+  }
+  
   /* Calculate total balance change, i.e. expenditures minus refunds */
   if (GNUNET_SYSERR ==
       TALER_amount_subtract (&spent,
@@ -2022,7 +2018,7 @@ check_transaction_history (const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
                                           coin_pub,
                                           &expenditures,
                                           &refunds,
-                                          0);
+                                          1);
     return GNUNET_SYSERR;
   }
 
@@ -2041,9 +2037,10 @@ check_transaction_history (const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
     return GNUNET_SYSERR;
   }
 
-  /* Finally, update @a merchant_gain by subtracting what he "lost" from 
refunds */
+  /* Finally, update @a merchant_gain by subtracting what he "lost"
+     from refunds */
   if (GNUNET_SYSERR ==
-      TALER_amount_subtract (merchant_gain,
+      TALER_amount_subtract (&merchant_delta,
                              merchant_gain,
                              &merchant_loss))
   {
@@ -2052,9 +2049,10 @@ check_transaction_history (const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
                                           coin_pub,
                                           merchant_gain,
                                           &merchant_loss,
-                                          0);
+                                          1);
     return GNUNET_SYSERR;
   }
+  *merchant_gain = merchant_delta;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Coin %s contributes %s to contract %s\n",
               TALER_B2S (coin_pub),
@@ -2077,7 +2075,8 @@ check_transaction_history (const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
  * @param h_contract_terms which proposal was this payment about
  * @param coin_pub which public key was this payment about
  * @param coin_value amount contributed by this coin in total (with fee)
- * @param coin_fee applicable fee for this coin
+ * @param deposit_fee applicable deposit fee for this coin, actual
+ *        fees charged may differ if coin was refunded
  */
 static void
 wire_transfer_information_cb (void *cls,
@@ -2089,12 +2088,11 @@ wire_transfer_information_cb (void *cls,
                               const struct GNUNET_HashCode *h_contract_terms,
                               const struct TALER_CoinSpendPublicKeyP *coin_pub,
                               const struct TALER_Amount *coin_value,
-                              const struct TALER_Amount *coin_fee)
+                              const struct TALER_Amount *deposit_fee)
 {
   struct WireCheckContext *wcc = cls;
   const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
   struct TALER_Amount computed_value;
-  struct TALER_Amount computed_fees;
   struct TALER_Amount coin_value_without_fee;
   struct TALER_EXCHANGEDB_TransactionList *tl;
   const struct TALER_CoinPublicInfo *coin;
@@ -2156,18 +2154,17 @@ wire_transfer_information_cb (void *cls,
                              merchant_pub,
                              dki,
                              tl,
-                             &computed_value,
-                             &computed_fees);
+                             &computed_value);
   if (GNUNET_SYSERR ==
       TALER_amount_subtract (&coin_value_without_fee,
                              coin_value,
-                             coin_fee))
+                             deposit_fee))
   {
     wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     report_amount_arithmetic_inconsistency ("aggregation (fee structure)",
                                             rowid,
                                             coin_value,
-                                            coin_fee,
+                                            deposit_fee,
                                             -1);
     return;
   }
@@ -2182,17 +2179,6 @@ wire_transfer_information_cb (void *cls,
                                             &computed_value,
                                             -1);
   }
-  if (0 !=
-      TALER_amount_cmp (&computed_fees,
-                        coin_fee))
-  {
-    wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
-    report_amount_arithmetic_inconsistency ("aggregation (fee)",
-                                            rowid,
-                                            coin_fee,
-                                            &computed_fees,
-                                            1);
-  }
   edb->free_coin_transaction_list (edb->cls,
                                    tl);
 
@@ -2276,6 +2262,7 @@ get_wire_fee (struct AggregationContext *ac,
                          &wfi->start_date,
                          &wfi->end_date,
                          &wfi->wire_fee,
+                        &wfi->closing_fee,
                          &master_sig))
   {
     GNUNET_break (0);
@@ -2299,6 +2286,8 @@ get_wire_fee (struct AggregationContext *ac,
     wp.end_date = GNUNET_TIME_absolute_hton (wfi->end_date);
     TALER_amount_hton (&wp.wire_fee,
                        &wfi->wire_fee);
+    TALER_amount_hton (&wp.closing_fee,
+                       &wfi->closing_fee);
     if (GNUNET_OK !=
         GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_FEES,
                                     &wp.purpose,
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am
index da8dd9d..967d95e 100644
--- a/src/bank-lib/Makefile.am
+++ b/src/bank-lib/Makefile.am
@@ -6,6 +6,19 @@ if USE_COVERAGE
   XLIB = -lgcov
 endif
 
+bin_PROGRAMS = \
+  taler-bank-transfer
+
+taler_bank_transfer_SOURCES = \
+  taler-bank-transfer.c
+taler_bank_transfer_LDADD = \
+  $(LIBGCRYPT_LIBS) \
+  $(top_builddir)/src/util/libtalerutil.la \
+  libtalerbank.la \
+  -lgnunetcurl \
+  -lgnunetutil \
+  -ljansson $(XLIB)
+
 lib_LTLIBRARIES = \
   libtalerbank.la \
   libtalerfakebank.la
diff --git a/src/bank-lib/bank_api_common.c b/src/bank-lib/bank_api_common.c
index b4b1974..ec55db8 100644
--- a/src/bank-lib/bank_api_common.c
+++ b/src/bank-lib/bank_api_common.c
@@ -91,7 +91,7 @@ TALER_BANK_make_auth_header_ (const struct 
TALER_BANK_AuthenticationData *auth)
  *
  * @param u base URL of the bank
  * @param path Taler API path (i.e. "/history")
- * @return the full URI to use with cURL
+ * @return the full URL to use with cURL
  */
 char *
 TALER_BANK_path_to_url_ (const char *u,
diff --git a/src/bank-lib/bank_api_common.h b/src/bank-lib/bank_api_common.h
index 5d6578c..fcf2029 100644
--- a/src/bank-lib/bank_api_common.h
+++ b/src/bank-lib/bank_api_common.h
@@ -44,7 +44,7 @@ TALER_BANK_make_auth_header_ (const struct 
TALER_BANK_AuthenticationData *auth);
  *
  * @param u base URL of the bank
  * @param path Taler API path (i.e. "/history")
- * @return the full URI to use with cURL
+ * @return the full URL to use with cURL
  */
 char *
 TALER_BANK_path_to_url_ (const char *u,
diff --git a/src/bank-lib/bank_api_history.c b/src/bank-lib/bank_api_history.c
index fdc1e2a..d2035e8 100644
--- a/src/bank-lib/bank_api_history.c
+++ b/src/bank-lib/bank_api_history.c
@@ -138,7 +138,7 @@ parse_account_history (struct TALER_BANK_HistoryHandle *hh,
     }
     td.account_details = json_pack ("{s:s, s:s, s:I}",
                                     "type", "test",
-                                    "bank_uri", hh->bank_base_url,
+                                    "bank_url", hh->bank_base_url,
                                     "account_number", (json_int_t) 
other_account);
     hh->hcb (hh->hcb_cls,
              MHD_HTTP_OK,
@@ -174,7 +174,7 @@ handle_history_finished (void *cls,
   switch (response_code)
   {
   case 0:
-    ec = TALER_EC_INTERNAL_INVARIANT_FAILURE;
+    ec = TALER_EC_BANK_HISTORY_HTTP_FAILURE;
     break;
   case MHD_HTTP_OK:
     if (GNUNET_OK !=
diff --git a/src/bank-lib/bank_api_reject.c b/src/bank-lib/bank_api_reject.c
index c630ccd..5318517 100644
--- a/src/bank-lib/bank_api_reject.c
+++ b/src/bank-lib/bank_api_reject.c
@@ -167,7 +167,7 @@ TALER_BANK_reject (struct GNUNET_CURL_Context *ctx,
   reject_obj = json_pack ("{s:{s:s}, s:I, s:I}",
                           "auth", "type", "basic",
                           "row_id", (json_int_t) rowid,
-                          "credit_account", (json_int_t) account_number);
+                          "account_number", (json_int_t) account_number);
   if (NULL == reject_obj)
   {
     GNUNET_break (0);
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index d83c22a..6761cb5 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -588,7 +588,7 @@ handle_reject (struct TALER_FAKEBANK_Handle *h,
     uint64_t credit_account;
     struct GNUNET_JSON_Specification spec[] = {
       GNUNET_JSON_spec_uint64 ("row_id", &row_id),
-      GNUNET_JSON_spec_uint64 ("credit_account", &credit_account),
+      GNUNET_JSON_spec_uint64 ("account_number", &credit_account),
       GNUNET_JSON_spec_end ()
     };
     if (GNUNET_OK !=
@@ -613,7 +613,7 @@ handle_reject (struct TALER_FAKEBANK_Handle *h,
   if (GNUNET_OK != found)
     return create_bank_error (connection,
                               MHD_HTTP_NOT_FOUND,
-                              TALER_EC_BANK_REJECT_NOT_FOUND,
+                              TALER_EC_BANK_REJECT_TRANSACTION_NOT_FOUND,
                               "transaction unknown");
   /* finally build regular response */
   resp = MHD_create_response_from_buffer (0,
@@ -832,8 +832,8 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
                        "wt_subject", subject);
     GNUNET_assert (NULL != trans);
     GNUNET_free (subject);
-    GNUNET_assert (0 == json_array_append (history,
-                                           trans));
+    GNUNET_assert (0 == json_array_append_new (history,
+                                               trans));
     if (count > 0)
     {
       pos = pos->next;
@@ -958,7 +958,7 @@ handle_mhd_request (void *cls,
                            connection,
                            con_cls);
 
-  /* Unexpected URI path, just close the connection. */
+  /* Unexpected URL path, just close the connection. */
   /* we're rather impolite here, but it's a testcase. */
   GNUNET_break_op (0);
   return MHD_NO;
@@ -1061,7 +1061,7 @@ TALER_FAKEBANK_start (uint16_t port)
   struct TALER_FAKEBANK_Handle *h;
 
   h = GNUNET_new (struct TALER_FAKEBANK_Handle);
-  h->mhd_bank = MHD_start_daemon (MHD_USE_DEBUG,
+  h->mhd_bank = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_DUAL_STACK,
                                   port,
                                   NULL, NULL,
                                   &handle_mhd_request, h,
diff --git a/src/bank-lib/taler-bank-transfer.c 
b/src/bank-lib/taler-bank-transfer.c
new file mode 100644
index 0000000..d354daf
--- /dev/null
+++ b/src/bank-lib/taler-bank-transfer.c
@@ -0,0 +1,264 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2017 GNUnet e.V.
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-bank-transfer.c
+ * @brief Execute wire transfer.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#include "taler_bank_service.h"
+
+/**
+ * Bank URL.
+ */
+static char *bank_url;
+
+/**
+ * Amount to transfer.
+ */
+static struct TALER_Amount amount;
+
+/**
+ * Debit account number.
+ */
+static unsigned long long debit_account_no;
+
+/**
+ * Credit account number.
+ */
+static unsigned long long credit_account_no;
+
+/**
+ * Wire transfer subject.
+ */
+static char *subject;
+
+/**
+ * Username for authentication.
+ */
+static char *username;
+
+/**
+ * Password for authentication.
+ */
+static char *password;
+
+/**
+ * Return value from main().
+ */
+static int global_ret;
+
+/**
+ * Main execution context for the main loop.
+ */
+static struct GNUNET_CURL_Context *ctx;
+
+/**
+ * Handle to access the exchange.
+ */
+static struct TALER_BANK_AdminAddIncomingHandle *op;
+
+/**
+ * Context for running the CURL event loop.
+ */
+static struct GNUNET_CURL_RescheduleContext *rc;
+
+
+/**
+ * Function run when the test terminates (good or bad).
+ * Cleans up our state.
+ *
+ * @param cls NULL
+ */
+static void
+do_shutdown (void *cls)
+{
+  if (NULL != op)
+  {
+    TALER_BANK_admin_add_incoming_cancel (op);
+    op = NULL;
+  }
+  if (NULL != ctx)
+  {
+    GNUNET_CURL_fini (ctx);
+    ctx = NULL;
+  }
+  if (NULL != rc)
+  {
+    GNUNET_CURL_gnunet_rc_destroy (rc);
+    rc = NULL;
+  }
+}
+
+
+/**
+ * Function called with the result of the operation.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
+ *                    0 if the bank's reply is bogus (fails to follow the 
protocol)
+ * @param ec detailed error code
+ * @param serial_id unique ID of the wire transfer in the bank's records; 
UINT64_MAX on error
+ * @param json detailed response from the HTTPD, or NULL if reply was not in 
JSON
+ */
+static void
+res_cb (void *cls,
+        unsigned int http_status,
+        enum TALER_ErrorCode ec,
+        uint64_t serial_id,
+        const json_t *json)
+{
+  op = NULL;
+  switch (ec)
+  {
+  case TALER_EC_NONE:
+    global_ret = 0;
+    fprintf (stdout,
+             "%llu\n",
+             (unsigned long long) serial_id);
+    break;
+  default:
+    fprintf (stderr,
+             "Operation failed with staus code %u/%u\n",
+             (unsigned int) ec,
+             http_status);
+    if (NULL != json)
+      json_dumpf (json,
+                  stderr,
+                  JSON_INDENT (2));
+    break;
+  }
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Main function that will be run.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be 
NULL!)
+ * @param cfg configuration
+ */
+static void
+run (void *cls,
+     char *const *args,
+     const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  struct TALER_BANK_AuthenticationData auth;
+
+  ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
+                          &rc);
+  GNUNET_assert (NULL != ctx);
+  rc = GNUNET_CURL_gnunet_rc_create (ctx);
+
+  auth.method = TALER_BANK_AUTH_BASIC;
+  auth.details.basic.username = username;
+  auth.details.basic.password = password;
+  op = TALER_BANK_admin_add_incoming (ctx,
+                                      bank_url,
+                                      &auth,
+                                      "https://exchange.com/legacy";,
+                                      subject,
+                                      &amount,
+                                      debit_account_no,
+                                      credit_account_no,
+                                      &res_cb,
+                                      NULL);
+  GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
+                                 NULL);
+  if (NULL == op)
+    GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * The main function of the reservemod tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+  const struct GNUNET_GETOPT_CommandLineOption options[] = {
+    GNUNET_GETOPT_option_mandatory
+    (TALER_getopt_get_amount ('a',
+                              "amount",
+                              "VALUE",
+                              "value to transfer",
+                              &amount)),
+    GNUNET_GETOPT_option_mandatory
+    (GNUNET_GETOPT_option_string ('b',
+                                  "bank",
+                                  "URL",
+                                  "base URL of the bank",
+                                  &bank_url)),
+    GNUNET_GETOPT_option_help ("Deposit funds into a Taler reserve"),
+    GNUNET_GETOPT_option_mandatory
+    (GNUNET_GETOPT_option_ulong ('C',
+                                 "credit",
+                                 "ACCOUNT",
+                                 "number of the bank account to credit",
+                                 &credit_account_no)),
+    GNUNET_GETOPT_option_mandatory
+    (GNUNET_GETOPT_option_ulong ('D',
+                                 "debit",
+                                 "ACCOUNT",
+                                 "number of the bank account to debit",
+                                 &debit_account_no)),
+    GNUNET_GETOPT_option_mandatory
+    (GNUNET_GETOPT_option_string ('s',
+                                  "subject",
+                                  "STRING",
+                                  "specifies the wire transfer subject",
+                                  &subject)),
+    GNUNET_GETOPT_option_mandatory
+    (GNUNET_GETOPT_option_string ('u',
+                                  "user",
+                                  "USERNAME",
+                                  "username to use for authentication",
+                                  &username)),
+    GNUNET_GETOPT_option_mandatory
+    (GNUNET_GETOPT_option_string ('p',
+                                  "pass",
+                                  "PASSPHRASE",
+                                  "passphrase to use for authentication",
+                                  &password)),
+    GNUNET_GETOPT_OPTION_END
+  };
+
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_log_setup ("taler-bank-transfer",
+                                   "WARNING",
+                                   NULL));
+  global_ret = 1;
+  if (GNUNET_OK !=
+      GNUNET_PROGRAM_run (argc, argv,
+                          "taler-bank-transfer",
+                         "Execute bank transfer",
+                         options,
+                         &run, NULL))
+    return 1;
+  return global_ret;
+}
+
+/* end taler-bank-transfer.c */
diff --git a/src/bank-lib/test_bank_api.c b/src/bank-lib/test_bank_api.c
index c70ae88..2a11719 100644
--- a/src/bank-lib/test_bank_api.c
+++ b/src/bank-lib/test_bank_api.c
@@ -91,7 +91,6 @@ run (void *cls)
       .details.history.direction = TALER_BANK_DIRECTION_DEBIT,
       .details.history.start_row_ref = "deposit-1",
       .details.history.num_results = 5 },
-    /* FIXME(bug #5200): re-enable once implemented in bank
     { .oc = TBI_OC_REJECT,
       .label = "reject-1",
       .details.reject.cmd_ref = "deposit-1" },
@@ -113,7 +112,6 @@ run (void *cls)
       .details.history.direction = TALER_BANK_DIRECTION_BOTH | 
TALER_BANK_DIRECTION_CANCEL,
       .details.history.start_row_ref = NULL,
       .details.history.num_results = 5 },
-    */
     { .oc = TBI_OC_END }
   };
 
diff --git a/src/bank-lib/test_bank_interpreter.c 
b/src/bank-lib/test_bank_interpreter.c
index ab41ed6..40adea5 100644
--- a/src/bank-lib/test_bank_interpreter.c
+++ b/src/bank-lib/test_bank_interpreter.c
@@ -330,7 +330,7 @@ build_history (struct InterpreterState *is,
         = json_pack ("{s:s, s:s, s:I}",
                      "type",
                      "test",
-                     "bank_uri",
+                     "bank_url",
                      "http://localhost:8080";,
                      "account_number",
                      (json_int_t) 
pos->details.admin_add_incoming.debit_account_no);
@@ -347,7 +347,7 @@ build_history (struct InterpreterState *is,
         = json_pack ("{s:s, s:s, s:I}",
                      "type",
                      "test",
-                     "bank_uri",
+                     "bank_url",
                      "http://localhost:8080";,
                      "account_number",
                      (json_int_t) 
pos->details.admin_add_incoming.credit_account_no);
@@ -869,7 +869,7 @@ interpreter_run (void *cls)
                         cmd->details.reject.cmd_ref);
     GNUNET_assert (NULL != ref);
     GNUNET_assert (TBI_OC_ADMIN_ADD_INCOMING == ref->oc);
-    switch (ref->details.admin_add_incoming.debit_account_no)
+    switch (ref->details.admin_add_incoming.credit_account_no)
     {
     case 1:
       auth.details.basic.username = "Bank";
diff --git a/src/benchmark/bank_details.json b/src/benchmark/bank_details.json
index bc46c48..23f1811 100644
--- a/src/benchmark/bank_details.json
+++ b/src/benchmark/bank_details.json
@@ -1 +1 @@
-{"type":"test", "bank_uri":"http://localhost:8082/";, "account_number":63}
+{"type":"test", "bank_url":"http://localhost:8082/";, "account_number":63}
diff --git a/src/benchmark/merchant_details.json 
b/src/benchmark/merchant_details.json
index c386916..db98326 100644
--- a/src/benchmark/merchant_details.json
+++ b/src/benchmark/merchant_details.json
@@ -1 +1 @@
-{"type":"test", "bank_uri":"http://localhost:8082/";, "account_number":64}
+{"type":"test", "bank_url":"http://localhost:8082/";, "account_number":64}
diff --git a/src/benchmark/taler-exchange-benchmark.c 
b/src/benchmark/taler-exchange-benchmark.c
index c4f5420..fbdfe82 100644
--- a/src/benchmark/taler-exchange-benchmark.c
+++ b/src/benchmark/taler-exchange-benchmark.c
@@ -288,7 +288,7 @@ static unsigned int pool_size = 100;
 static char *config_file;
 
 /**
- * Configuation object (used to get BANK_URI)
+ * Configuation object (used to get BANK_URL)
  */
 static struct GNUNET_CONFIGURATION_Handle *cfg;
 
@@ -371,15 +371,15 @@ static struct Coin *coins;
 static struct TALER_MerchantPrivateKeyP merchant_priv;
 
 /**
- * URI under which the exchange is reachable during the benchmark.
+ * URL under which the exchange is reachable during the benchmark.
  */
-static char *exchange_uri;
+static char *exchange_url;
 
 /**
- * URI under which the administrative exchange is reachable during the
+ * URL under which the administrative exchange is reachable during the
  * benchmark.
  */
-static char *exchange_admin_uri;
+static char *exchange_admin_url;
 
 /**
  * Used currency (read from /keys' output)
@@ -1641,7 +1641,7 @@ run (void *cls)
   GNUNET_assert (NULL != rc);
   fakebank = TALER_FAKEBANK_start (8082);
   exchange = TALER_EXCHANGE_connect (ctx,
-                                     exchange_uri,
+                                     exchange_url,
                                      &cert_cb, NULL,
                                      TALER_EXCHANGE_OPTION_END);
   if (NULL == exchange)
@@ -1667,15 +1667,15 @@ main (int argc,
     GNUNET_GETOPT_option_mandatory
     (GNUNET_GETOPT_option_cfgfile (&config_file)),
     GNUNET_GETOPT_option_string ('e',
-                                 "exchange-uri",
-                                 "URI",
-                                 "URI of the exchange",
-                                 &exchange_uri),
+                                 "exchange-url",
+                                 "URL",
+                                 "URL of the exchange",
+                                 &exchange_url),
     GNUNET_GETOPT_option_string ('E',
-                                 "exchange-admin-uri",
-                                 "URI",
-                                 "URI of the administrative interface of the 
exchange",
-                                 &exchange_admin_uri),
+                                 "exchange-admin-url",
+                                 "URL",
+                                 "URL of the administrative interface of the 
exchange",
+                                 &exchange_admin_url),
     GNUNET_GETOPT_option_help ("tool to benchmark the Taler exchange"),
     GNUNET_GETOPT_option_uint ('s',
                                "pool-size",
@@ -1712,14 +1712,14 @@ main (int argc,
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                "Number of iterations below WARM_THRESHOLD of %llu\n",
                WARM_THRESHOLD);
-  if ( (NULL == exchange_uri) ||
-       (0 == strlen (exchange_uri) ))
+  if ( (NULL == exchange_url) ||
+       (0 == strlen (exchange_url) ))
   {
-    GNUNET_free_non_null (exchange_uri);
-    exchange_uri = GNUNET_strdup ("http://localhost:8081/";);
+    GNUNET_free_non_null (exchange_url);
+    exchange_url = GNUNET_strdup ("http://localhost:8081/";);
   }
-  if (NULL == exchange_admin_uri)
-    exchange_admin_uri = GNUNET_strdup ("http://localhost:18080/";);
+  if (NULL == exchange_admin_url)
+    exchange_admin_url = GNUNET_strdup ("http://localhost:18080/";);
   if (run_exchange)
   {
     char *wget;
@@ -1773,8 +1773,8 @@ main (int argc,
 
     GNUNET_asprintf (&wget,
                     "wget -q -t 1 -T 1 %s%skeys -o /dev/null -O /dev/null",
-                    exchange_uri,
-                    (exchange_uri[strlen (exchange_uri)-1] == '/') ? "" : "/");
+                    exchange_url,
+                    (exchange_url[strlen (exchange_url)-1] == '/') ? "" : "/");
     cnt = 0;
     do {
       fprintf (stderr, ".");
diff --git a/src/benchmark/taler-exchange-benchmark.conf 
b/src/benchmark/taler-exchange-benchmark.conf
index 1306e50..453782c 100644
--- a/src/benchmark/taler-exchange-benchmark.conf
+++ b/src/benchmark/taler-exchange-benchmark.conf
@@ -28,7 +28,7 @@ ENABLE = YES
 
 # What is the main website of the bank?
 # (Not used unless the aggregator is run.)
-BANK_URI = "http://localhost:8082/";
+BANK_URL = "http://localhost:8082/";
 # From which account at the 'bank' should outgoing wire transfers be made?
 BANK_ACCOUNT_NUMBER = 2
 
diff --git a/src/benchmark/test_benchmark_home/.config/taler/test.json 
b/src/benchmark/test_benchmark_home/.config/taler/test.json
index be5e92c..eca3942 100644
--- a/src/benchmark/test_benchmark_home/.config/taler/test.json
+++ b/src/benchmark/test_benchmark_home/.config/taler/test.json
@@ -2,7 +2,7 @@
   "salt": 
"AZPRFVJ58NM6M7J5CZQPJAH3EW5DYM52AEZ9Y1C1ER3W94QV8D8TQKF6CK8MYQRA9QMSKDQTGZ306ZS9GQ0M6R01CJ20KPP49WFDZK8",
   "name": "The exchange",
   "account_number": 3,
-  "bank_uri": "http://localhost:8082/";,
+  "bank_url": "http://localhost:8082/";,
   "type": "test",
   "sig": 
"RPQXP9S4P8PQP7HEZQNRSZCT0ATNEP8GW0P5TPM34V5RX86FCD670V44R9NETSYDDKB8SZV7TKY9PAJYTY51D3VDWY9XXQ5BPFRXR28"
-}
\ No newline at end of file
+}
diff --git a/src/exchange-lib/Makefile.am b/src/exchange-lib/Makefile.am
index f92ea63..64a5f96 100644
--- a/src/exchange-lib/Makefile.am
+++ b/src/exchange-lib/Makefile.am
@@ -7,16 +7,15 @@ if USE_COVERAGE
 endif
 
 lib_LTLIBRARIES = \
-  libtalerexchange.la
+  libtalerexchange.la \
+  libtalertesting.la
 
 libtalerexchange_la_LDFLAGS = \
-  -version-info 3:0:0 \
+  -version-info 4:0:0 \
   -no-undefined
-
 libtalerexchange_la_SOURCES = \
   exchange_api_common.c \
   exchange_api_handle.c exchange_api_handle.h \
-  exchange_api_admin.c \
   exchange_api_deposit.c \
   exchange_api_payback.c \
   exchange_api_refresh.c \
@@ -26,7 +25,6 @@ libtalerexchange_la_SOURCES = \
   exchange_api_track_transaction.c \
   exchange_api_track_transfer.c \
   exchange_api_wire.c
-
 libtalerexchange_la_LIBADD = \
   $(top_builddir)/src/json/libtalerjson.la \
   $(top_builddir)/src/util/libtalerutil.la \
@@ -36,6 +34,34 @@ libtalerexchange_la_LIBADD = \
   -ljansson \
   $(XLIB)
 
+
+libtalertesting_la_LDFLAGS = \
+  -version-info 0:0:0 \
+  -no-undefined
+libtalertesting_la_SOURCES = \
+  testing_api_cmd_exec_wirewatch.c \
+  testing_api_cmd_fakebank_transfer.c \
+  testing_api_cmd_withdraw.c \
+  testing_api_helpers.c \
+  testing_api_loop.c \
+  testing_api_traits.c \
+  testing_api_trait_blinding_key.c \
+  testing_api_trait_coin_priv.c \
+  testing_api_trait_denom_pub.c \
+  testing_api_trait_denom_sig.c \
+  testing_api_trait_process.c \
+  testing_api_trait_reserve_priv.c
+
+libtalertesting_la_LIBADD = \
+  $(top_builddir)/src/json/libtalerjson.la \
+  $(top_builddir)/src/util/libtalerutil.la \
+  $(top_builddir)/src/bank-lib/libtalerfakebank.la \
+  -lgnunetcurl \
+  -lgnunetjson \
+  -lgnunetutil \
+  -ljansson \
+  $(XLIB)
+
 if HAVE_LIBCURL
 libtalerexchange_la_LIBADD += -lcurl
 else
@@ -46,12 +72,14 @@ endif
 
 check_PROGRAMS = \
   test_exchange_api \
-  test_exchange_api_keys_cherry_picking
+  test_exchange_api_keys_cherry_picking \
+  test_exchange_api_new
 
 AM_TESTS_ENVIRONMENT=export 
TALER_PREFIX=$${TALER_PREFIX:address@hidden@};export 
PATH=$${TALER_PREFIX:address@hidden@}/bin:$$PATH;
 
 TESTS = \
-  $(check_PROGRAMS)
+  test_exchange_api \
+  test_exchange_api_keys_cherry_picking
 
 test_exchange_api_SOURCES = \
   test_exchange_api.c
@@ -66,6 +94,20 @@ test_exchange_api_LDADD = \
   -lgnunetutil \
   -ljansson
 
+test_exchange_api_new_SOURCES = \
+  test_exchange_api_new.c
+test_exchange_api_new_LDADD = \
+  libtalertesting.la \
+  libtalerexchange.la \
+  $(LIBGCRYPT_LIBS) \
+  $(top_builddir)/src/bank-lib/libtalerfakebank.la \
+  $(top_builddir)/src/bank-lib/libtalerbank.la \
+  $(top_builddir)/src/json/libtalerjson.la \
+  $(top_builddir)/src/util/libtalerutil.la \
+  -lgnunetcurl \
+  -lgnunetutil \
+  -ljansson
+
 test_exchange_api_keys_cherry_picking_SOURCES = \
   test_exchange_api_keys_cherry_picking.c
 test_exchange_api_keys_cherry_picking_LDADD = \
diff --git a/src/exchange-lib/exchange_api_admin.c 
b/src/exchange-lib/exchange_api_admin.c
deleted file mode 100644
index 6049c54..0000000
--- a/src/exchange-lib/exchange_api_admin.c
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 GNUnet e.V.
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along with
-  TALER; see the file COPYING.  If not, see
-  <http://www.gnu.org/licenses/>
-*/
-/**
- * @file exchange-lib/exchange_api_admin.c
- * @brief Implementation of the /admin/ requests of the exchange's HTTP API
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_json_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler_json_lib.h"
-#include "taler_exchange_service.h"
-#include "exchange_api_handle.h"
-#include "taler_signatures.h"
-
-
-/**
- * @brief An admin/add/incoming Handle
- */
-struct TALER_EXCHANGE_AdminAddIncomingHandle
-{
-
-  /**
-   * The connection to exchange this request handle will use
-   */
-  struct TALER_EXCHANGE_Handle *exchange;
-
-  /**
-   * The url for this request.
-   */
-  char *url;
-
-  /**
-   * JSON encoding of the request to POST.
-   */
-  char *json_enc;
-
-  /**
-   * Handle for the request.
-   */
-  struct GNUNET_CURL_Job *job;
-
-  /**
-   * HTTP headers for the request.
-   */
-  struct curl_slist *headers;
-
-  /**
-   * Function to call with the result.
-   */
-  TALER_EXCHANGE_AdminAddIncomingResultCallback cb;
-
-  /**
-   * Closure for @a cb.
-   */
-  void *cb_cls;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP /admin/add/incoming request.
- *
- * @param cls the `struct TALER_EXCHANGE_AdminAddIncomingHandle`
- * @param response_code HTTP response code, 0 on error
- * @param json parsed JSON result, NULL on error
- */
-static void
-handle_admin_add_incoming_finished (void *cls,
-                                    long response_code,
-                                    const json_t *json)
-{
-  struct TALER_EXCHANGE_AdminAddIncomingHandle *aai = cls;
-
-  aai->job = NULL;
-  switch (response_code)
-  {
-  case 0:
-    break;
-  case MHD_HTTP_OK:
-    break;
-  case MHD_HTTP_BAD_REQUEST:
-    /* This should never happen, either us or the exchange is buggy
-       (or API version conflict); just pass JSON reply to the application */
-    break;
-  case MHD_HTTP_FORBIDDEN:
-    /* Access denied */
-    break;
-  case MHD_HTTP_UNAUTHORIZED:
-    /* Nothing really to verify, exchange says one of the signatures is
-       invalid; as we checked them, this should never happen, we
-       should pass the JSON reply to the application */
-    break;
-  case MHD_HTTP_NOT_FOUND:
-    /* Nothing really to verify, this should never
-       happen, we should pass the JSON reply to the application */
-    break;
-  case MHD_HTTP_INTERNAL_SERVER_ERROR:
-    /* Server had an internal issue; we should retry, but this API
-       leaves this to the application */
-    break;
-  default:
-    /* unexpected response code */
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u\n",
-                (unsigned int) response_code);
-    GNUNET_break (0);
-    response_code = 0;
-    break;
-  }
-  aai->cb (aai->cb_cls,
-           response_code,
-          TALER_JSON_get_error_code (json),
-           json);
-  TALER_EXCHANGE_admin_add_incoming_cancel (aai);
-}
-
-
-/**
- * Notify the exchange that we have received an incoming transaction
- * which fills a reserve.  Note that this API is an administrative
- * API and thus not accessible to typical exchange clients, but only
- * to the operators of the exchange.
- *
- * @param exchange the exchange handle; the exchange must be ready to operate
- * @param admin_url URL of the administrative interface of the exchange
- * @param reserve_pub public key of the reserve
- * @param amount amount that was deposited
- * @param execution_date when did we receive the amount
- * @param sender_account_details account information of the sender of the 
money;
- *        the receiver is always the exchange.
- * @param transfer_details details that uniquely identify the transfer;
- *        used to check for duplicate operations by the exchange
- * @param res_cb the callback to call when the final result for this request 
is available
- * @param res_cb_cls closure for the above callback
- * @return NULL
- *         if the inputs are invalid (i.e. invalid amount).
- *         In this case, the callback is not called.
- */
-struct TALER_EXCHANGE_AdminAddIncomingHandle *
-TALER_EXCHANGE_admin_add_incoming (struct TALER_EXCHANGE_Handle *exchange,
-                                   const char *admin_url,
-                                   const struct TALER_ReservePublicKeyP 
*reserve_pub,
-                                   const struct TALER_Amount *amount,
-                                   struct GNUNET_TIME_Absolute execution_date,
-                                   const json_t *sender_account_details,
-                                   const json_t *transfer_details,
-                                   
TALER_EXCHANGE_AdminAddIncomingResultCallback res_cb,
-                                   void *res_cb_cls)
-{
-  struct TALER_EXCHANGE_AdminAddIncomingHandle *aai;
-  struct GNUNET_CURL_Context *ctx;
-  json_t *admin_obj;
-  CURL *eh;
-
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_TIME_round_abs (&execution_date));
-  if (GNUNET_YES !=
-      MAH_handle_is_ready (exchange))
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  admin_obj = json_pack ("{s:o, s:o," /* reserve_pub/amount */
-                         " s:o, s:O, s:O}", /* execution_Date/sender/transfer 
*/
-                         "reserve_pub", GNUNET_JSON_from_data_auto 
(reserve_pub),
-                         "amount", TALER_JSON_from_amount (amount),
-                         "execution_date", GNUNET_JSON_from_time_abs 
(execution_date),
-                         "sender_account_details", sender_account_details,
-                         "transfer_details", transfer_details);
-  if (NULL == admin_obj)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  aai = GNUNET_new (struct TALER_EXCHANGE_AdminAddIncomingHandle);
-  aai->exchange = exchange;
-  aai->cb = res_cb;
-  aai->cb_cls = res_cb_cls;
-  aai->url = MAH_path_to_url2 (admin_url,
-                               "/admin/add/incoming");
-
-  eh = curl_easy_init ();
-  GNUNET_assert (NULL != (aai->json_enc =
-                          json_dumps (admin_obj,
-                                      JSON_COMPACT)));
-  json_decref (admin_obj);
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_URL,
-                                   aai->url));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDS,
-                                   aai->json_enc));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDSIZE,
-                                   strlen (aai->json_enc)));
-  ctx = MAH_handle_to_context (exchange);
-  aai->job = GNUNET_CURL_job_add (ctx,
-                                  eh,
-                                  GNUNET_YES,
-                                  &handle_admin_add_incoming_finished,
-                                  aai);
-  return aai;
-}
-
-
-/**
- * Cancel an add incoming.  This function cannot be used on a request
- * handle if a response is already served for it.
- *
- * @param aai the admin add incoming request handle
- */
-void
-TALER_EXCHANGE_admin_add_incoming_cancel (struct 
TALER_EXCHANGE_AdminAddIncomingHandle *aai)
-{
-  if (NULL != aai->job)
-  {
-    GNUNET_CURL_job_cancel (aai->job);
-    aai->job = NULL;
-  }
-  curl_slist_free_all (aai->headers);
-  GNUNET_free (aai->url);
-  GNUNET_free (aai->json_enc);
-  GNUNET_free (aai);
-}
-
-
-/* end of exchange_api_admin.c */
diff --git a/src/exchange-lib/exchange_api_deposit.c 
b/src/exchange-lib/exchange_api_deposit.c
index d90b1aa..76e3e4d 100644
--- a/src/exchange-lib/exchange_api_deposit.c
+++ b/src/exchange-lib/exchange_api_deposit.c
@@ -454,7 +454,7 @@ TALER_EXCHANGE_deposit (struct TALER_EXCHANGE_Handle 
*exchange,
                            " s:o," /* merchant_pub */
                            " s:o, s:o," /* refund_deadline, wire_deadline */
                            " s:o}",     /* coin_sig */
-                           "f", TALER_JSON_from_amount (amount),
+                           "contribution", TALER_JSON_from_amount (amount),
                            "wire", wire_details,
                            "H_wire", GNUNET_JSON_from_data_auto (&h_wire),
                            "h_contract_terms", GNUNET_JSON_from_data_auto 
(h_contract_terms),
diff --git a/src/exchange-lib/exchange_api_handle.c 
b/src/exchange-lib/exchange_api_handle.c
index 22e64b9..e3f8314 100644
--- a/src/exchange-lib/exchange_api_handle.c
+++ b/src/exchange-lib/exchange_api_handle.c
@@ -575,6 +575,8 @@ decode_keys_json (const json_t *resp_obj,
                                    &pub),
       GNUNET_JSON_spec_absolute_time ("list_issue_date",
                                       &list_issue_date),
+      GNUNET_JSON_spec_relative_time ("reserve_closing_delay",
+                                      &key_data->reserve_closing_delay),
       GNUNET_JSON_spec_end()
     };
 
@@ -954,7 +956,7 @@ MAH_handle_is_ready (struct TALER_EXCHANGE_Handle *h)
  *
  * @param h handle for the exchange
  * @param path Taler API path (i.e. "/reserve/withdraw")
- * @return the full URI to use with cURL
+ * @return the full URL to use with cURL
  */
 char *
 MAH_path_to_url (struct TALER_EXCHANGE_Handle *h,
@@ -970,7 +972,7 @@ MAH_path_to_url (struct TALER_EXCHANGE_Handle *h,
  *
  * @param base_url base URL of the exchange (i.e. "http://exchange/";)
  * @param path Taler API path (i.e. "/reserve/withdraw")
- * @return the full URI to use with cURL
+ * @return the full URL to use with cURL
  */
 char *
 MAH_path_to_url2 (const char *base_url,
diff --git a/src/exchange-lib/exchange_api_handle.h 
b/src/exchange-lib/exchange_api_handle.h
index d4aa52f..7d59ce4 100644
--- a/src/exchange-lib/exchange_api_handle.h
+++ b/src/exchange-lib/exchange_api_handle.h
@@ -49,7 +49,7 @@ MAH_handle_is_ready (struct TALER_EXCHANGE_Handle *h);
  *
  * @param h the exchange handle to query
  * @param path Taler API path (i.e. "/reserve/withdraw")
- * @return the full URI to use with cURL
+ * @return the full URL to use with cURL
  */
 char *
 MAH_path_to_url (struct TALER_EXCHANGE_Handle *h,
@@ -61,7 +61,7 @@ MAH_path_to_url (struct TALER_EXCHANGE_Handle *h,
  *
  * @param base_url base URL of the exchange (i.e. "http://exchange/";)
  * @param path Taler API path (i.e. "/reserve/withdraw")
- * @return the full URI to use with cURL
+ * @return the full URL to use with cURL
  */
 char *
 MAH_path_to_url2 (const char *base_url,
diff --git a/src/exchange-lib/exchange_api_refund.c 
b/src/exchange-lib/exchange_api_refund.c
index a39dd23..ef1d66b 100644
--- a/src/exchange-lib/exchange_api_refund.c
+++ b/src/exchange-lib/exchange_api_refund.c
@@ -243,12 +243,8 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle 
*exchange,
                       TALER_EXCHANGE_RefundResultCallback cb,
                       void *cb_cls)
 {
-  struct TALER_EXCHANGE_RefundHandle *rh;
-  struct GNUNET_CURL_Context *ctx;
   struct TALER_RefundRequestPS rr;
   struct TALER_MerchantSignatureP merchant_sig;
-  json_t *refund_obj;
-  CURL *eh;
 
   GNUNET_assert (GNUNET_YES ==
                 MAH_handle_is_ready (exchange));
@@ -267,7 +263,68 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle 
*exchange,
                  GNUNET_CRYPTO_eddsa_sign (&merchant_priv->eddsa_priv,
                                            &rr.purpose,
                                            &merchant_sig.eddsa_sig));
-  refund_obj = json_pack ("{s:o, s:o," /* amount/fee */
+  return TALER_EXCHANGE_refund2 (exchange,
+                                amount,
+                                refund_fee,
+                                h_contract_terms,
+                                coin_pub,
+                                rtransaction_id,
+                                &rr.merchant,
+                                &merchant_sig,
+                                cb,
+                                cb_cls);
+}
+
+  
+/**
+ * Submit a refund request to the exchange and get the exchange's
+ * response.  This API is used by a merchant.  Note that
+ * while we return the response verbatim to the caller for further
+ * processing, we do already verify that the response is well-formed
+ * (i.e. that signatures included in the response are all valid).  If
+ * the exchange's reply is not well-formed, we return an HTTP status code
+ * of zero to @a cb.
+ *
+ * The @a exchange must be ready to operate (i.e.  have
+ * finished processing the /keys reply).  If this check fails, we do
+ * NOT initiate the transaction with the exchange and instead return NULL.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param amount the amount to be refunded; must be larger than the refund fee
+ *        (as that fee is still being subtracted), and smaller than the amount
+ *        (with deposit fee) of the original deposit contribution of this coin
+ * @param refund_fee fee applicable to this coin for the refund
+ * @param h_contract_terms hash of the contact of the merchant with the 
customer that is being refunded
+ * @param coin_pub coin’s public key of the coin from the original deposit 
operation
+ * @param rtransaction_id transaction id for the transaction between merchant 
and customer (of refunding operation);
+ *                        this is needed as we may first do a partial refund 
and later a full refund.  If both
+ *                        refunds are also over the same amount, we need the 
@a rtransaction_id to make the disjoint
+ *                        refund requests different (as requests are 
idempotent and otherwise the 2nd refund might not work).
+ * @param merchant_pub public key of the merchant
+ * @param merchant_sig signature affirming the refund from the merchant
+ * @param cb the callback to call when a reply for this request is available
+ * @param cb_cls closure for the above callback
+ * @return a handle for this request; NULL if the inputs are invalid (i.e.
+ *         signatures fail to verify).  In this case, the callback is not 
called.
+ */
+struct TALER_EXCHANGE_RefundHandle *
+TALER_EXCHANGE_refund2 (struct TALER_EXCHANGE_Handle *exchange,
+                      const struct TALER_Amount *amount,
+                      const struct TALER_Amount *refund_fee,
+                      const struct GNUNET_HashCode *h_contract_terms,
+                      const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                      uint64_t rtransaction_id,
+                      const struct TALER_MerchantPublicKeyP *merchant_pub,
+                      const struct TALER_MerchantSignatureP *merchant_sig,
+                      TALER_EXCHANGE_RefundResultCallback cb,
+                      void *cb_cls)
+{
+  struct TALER_EXCHANGE_RefundHandle *rh;
+  struct GNUNET_CURL_Context *ctx;
+  json_t *refund_obj;
+  CURL *eh;
+
+refund_obj = json_pack ("{s:o, s:o," /* amount/fee */
                          " s:o, s:o," /* h_contract_terms, coin_pub */
                          " s:I," /* rtransaction id */
                          " s:o, s:o}", /* merchant_pub, merchant_sig */
@@ -276,8 +333,8 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle 
*exchange,
                          "h_contract_terms", GNUNET_JSON_from_data_auto 
(h_contract_terms),
                          "coin_pub", GNUNET_JSON_from_data_auto (coin_pub),
                          "rtransaction_id", (json_int_t) rtransaction_id,
-                         "merchant_pub", GNUNET_JSON_from_data_auto 
(&rr.merchant),
-                         "merchant_sig", GNUNET_JSON_from_data_auto 
(&merchant_sig)
+                         "merchant_pub", GNUNET_JSON_from_data_auto 
(merchant_pub),
+                         "merchant_sig", GNUNET_JSON_from_data_auto 
(merchant_sig)
                          );
   if (NULL == refund_obj)
   {
@@ -294,7 +351,7 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle 
*exchange,
   rh->depconf.purpose.purpose = htonl 
(TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND);
   rh->depconf.h_contract_terms = *h_contract_terms;
   rh->depconf.coin_pub = *coin_pub;
-  rh->depconf.merchant = rr.merchant;
+  rh->depconf.merchant = *merchant_pub;
   rh->depconf.rtransaction_id = GNUNET_htonll (rtransaction_id);
   TALER_amount_hton (&rh->depconf.refund_amount,
                      amount);
diff --git a/src/exchange-lib/exchange_api_reserve.c 
b/src/exchange-lib/exchange_api_reserve.c
index 35fd0e0..72429d4 100644
--- a/src/exchange-lib/exchange_api_reserve.c
+++ b/src/exchange-lib/exchange_api_reserve.c
@@ -142,11 +142,14 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle 
*exchange,
       json_t *wire_account;
       void *wire_reference;
       size_t wire_reference_size;
+      struct GNUNET_TIME_Absolute timestamp;
 
       struct GNUNET_JSON_Specification withdraw_spec[] = {
         GNUNET_JSON_spec_varsize ("wire_reference",
                                   &wire_reference,
                                   &wire_reference_size),
+        GNUNET_JSON_spec_absolute_time ("timestamp",
+                                        &timestamp),
         GNUNET_JSON_spec_json ("sender_account_details",
                                &wire_account),
         GNUNET_JSON_spec_end()
@@ -173,6 +176,7 @@ parse_reserve_history (struct TALER_EXCHANGE_Handle 
*exchange,
       rhistory[off].details.in_details.sender_account_details = wire_account;
       rhistory[off].details.in_details.wire_reference = wire_reference;
       rhistory[off].details.in_details.wire_reference_size = 
wire_reference_size;
+      rhistory[off].details.in_details.timestamp = timestamp;
       /* end type==DEPOSIT */
     }
     else if (0 == strcasecmp (type,
@@ -566,12 +570,15 @@ handle_reserve_status_finished (void *cls,
     break;
   }
   if (NULL != rsh->cb)
+  {
     rsh->cb (rsh->cb_cls,
              response_code,
             TALER_JSON_get_error_code (json),
              json,
              NULL,
              0, NULL);
+    rsh->cb = NULL;
+  }
   TALER_EXCHANGE_reserve_status_cancel (rsh);
 }
 
@@ -741,7 +748,8 @@ reserve_withdraw_ok (struct 
TALER_EXCHANGE_ReserveWithdrawHandle *wsh,
   struct GNUNET_CRYPTO_RsaSignature *blind_sig;
   struct TALER_FreshCoin fc;
   struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_rsa_signature ("ev_sig", &blind_sig),
+    GNUNET_JSON_spec_rsa_signature ("ev_sig",
+                                    &blind_sig),
     GNUNET_JSON_spec_end()
   };
 
@@ -957,11 +965,14 @@ handle_reserve_withdraw_finished (void *cls,
     break;
   }
   if (NULL != wsh->cb)
+  {
     wsh->cb (wsh->cb_cls,
              response_code,
             TALER_JSON_get_error_code (json),
              NULL,
              json);
+    wsh->cb = NULL;
+  }
   TALER_EXCHANGE_reserve_withdraw_cancel (wsh);
 }
 
diff --git a/src/exchange-lib/test_exchange_api.c 
b/src/exchange-lib/test_exchange_api.c
index 475fbde..81c8542 100644
--- a/src/exchange-lib/test_exchange_api.c
+++ b/src/exchange-lib/test_exchange_api.c
@@ -1497,13 +1497,12 @@ static const struct TALER_EXCHANGE_DenomPublicKey *
 find_pk (const struct TALER_EXCHANGE_Keys *keys,
          const struct TALER_Amount *amount)
 {
-  unsigned int i;
   struct GNUNET_TIME_Absolute now;
   struct TALER_EXCHANGE_DenomPublicKey *pk;
   char *str;
 
   now = GNUNET_TIME_absolute_get ();
-  for (i=0;i<keys->num_denom_keys;i++)
+  for (unsigned int i=0;i<keys->num_denom_keys;i++)
   {
     pk = &keys->denom_keys[i];
     if ( (0 == TALER_amount_cmp (amount,
@@ -1514,7 +1513,7 @@ find_pk (const struct TALER_EXCHANGE_Keys *keys,
   }
   /* do 2nd pass to check if expiration times are to blame for failure */
   str = TALER_amount_to_string (amount);
-  for (i=0;i<keys->num_denom_keys;i++)
+  for (unsigned int i=0;i<keys->num_denom_keys;i++)
   {
     pk = &keys->denom_keys[i];
     if ( (0 == TALER_amount_cmp (amount,
@@ -2870,13 +2869,12 @@ do_shutdown (void *cls)
 {
   struct InterpreterState *is = cls;
   struct Command *cmd;
-  unsigned int i;
 
   fprintf (stderr,
            "Executing shutdown at `%s'\n",
            is->commands[is->ip].label);
 
-  for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
+  for (unsigned int i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
   {
     switch (cmd->oc)
     {
@@ -3254,7 +3252,7 @@ run (void *cls)
       .expected_response_code = MHD_HTTP_OK,
       .details.deposit.amount = "EUR:5",
       .details.deposit.coin_ref = "withdraw-coin-1",
-      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42  }",
+      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_url\":\"http://localhost:8082/\";, \"account_number\":42  }",
       .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice 
cream\", \"value\":1 } ] }" },
 
     /* Try to overdraw funds ... */
@@ -3270,7 +3268,7 @@ run (void *cls)
       .expected_response_code = MHD_HTTP_FORBIDDEN,
       .details.deposit.amount = "EUR:5",
       .details.deposit.coin_ref = "withdraw-coin-1",
-      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":43  }",
+      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_url\":\"http://localhost:8082/\";, \"account_number\":43  }",
       .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice 
cream\", \"value\":1 } ] }" },
     /* Try to double-spend the 5 EUR coin at the same merchant (but different
        transaction ID) */
@@ -3279,7 +3277,7 @@ run (void *cls)
       .expected_response_code = MHD_HTTP_FORBIDDEN,
       .details.deposit.amount = "EUR:5",
       .details.deposit.coin_ref = "withdraw-coin-1",
-      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42  }",
+      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_url\":\"http://localhost:8082/\";, \"account_number\":42  }",
       .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice 
cream\", \"value\":1 } ] }" },
     /* Try to double-spend the 5 EUR coin at the same merchant (but different
        proposal) */
@@ -3288,7 +3286,7 @@ run (void *cls)
       .expected_response_code = MHD_HTTP_FORBIDDEN,
       .details.deposit.amount = "EUR:5",
       .details.deposit.coin_ref = "withdraw-coin-1",
-      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42  }",
+      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_url\":\"http://localhost:8082/\";, \"account_number\":42  }",
       .details.deposit.contract_terms = "{ \"items\":[{ \"name\":\"ice 
cream\", \"value\":2 } ] }" },
 
     /* ***************** /refresh testing ******************** */
@@ -3318,7 +3316,7 @@ run (void *cls)
       .expected_response_code = MHD_HTTP_OK,
       .details.deposit.amount = "EUR:1",
       .details.deposit.coin_ref = "refresh-withdraw-coin-1",
-      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42  }",
+      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_url\":\"http://localhost:8082/\";, \"account_number\":42  }",
       .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice 
cream\", \"value\":\"EUR:1\" } ] }" },
 
     /* Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x 
EUR:0.13) */
@@ -3359,7 +3357,7 @@ run (void *cls)
       .details.deposit.amount = "EUR:1",
       .details.deposit.coin_ref = "refresh-reveal-1-idempotency",
       .details.deposit.coin_idx = 0,
-      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42  }",
+      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_url\":\"http://localhost:8082/\";, \"account_number\":42  }",
       .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice 
cream\", \"value\":3 } ] }" },
 
     /* Test successfully spending coins from the refresh operation:
@@ -3370,7 +3368,7 @@ run (void *cls)
       .details.deposit.amount = "EUR:0.1",
       .details.deposit.coin_ref = "refresh-reveal-1",
       .details.deposit.coin_idx = 4,
-      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":43  }",
+      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_url\":\"http://localhost:8082/\";, \"account_number\":43  }",
       .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"ice 
cream\", \"value\":3 } ] }" },
 
     /* Test running a failing melt operation (same operation again must fail) 
*/
@@ -3509,7 +3507,7 @@ run (void *cls)
       .expected_response_code = MHD_HTTP_OK,
       .details.deposit.amount = "EUR:5",
       .details.deposit.coin_ref = "withdraw-coin-r1",
-      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42  }",
+      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_url\":\"http://localhost:8082/\";, \"account_number\":42  }",
       .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice 
cream\", \"value\":\"EUR:5\" } ] }",
       .details.deposit.refund_deadline = { 60LL * 1000 * 1000 } /* 60 s */,
     },
@@ -3541,7 +3539,7 @@ run (void *cls)
       .expected_response_code = MHD_HTTP_OK,
       .details.deposit.amount = "EUR:4.99",
       .details.deposit.coin_ref = "withdraw-coin-r1",
-      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42  }",
+      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_url\":\"http://localhost:8082/\";, \"account_number\":42  }",
       .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"more ice 
cream\", \"value\":\"EUR:5\" } ] }",
     },
     /* Run transfers. This will do the transfer as refund deadline was 0 */
@@ -3563,6 +3561,65 @@ run (void *cls)
       .details.refund.fee = "EUR:0.01",
       .details.refund.deposit_ref = "deposit-refund-2",
     },
+    { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
+      .label = "check-empty-after-refund" },
+
+
+    /* Test refunded coins are never executed, even past
+       refund deadline */
+    { .oc = OC_ADMIN_ADD_INCOMING,
+      .label = "create-reserve-rb",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.admin_add_incoming.debit_account_no = 42,
+      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
+      .details.admin_add_incoming.auth_username = "user42",
+      .details.admin_add_incoming.auth_password = "pass42",
+      .details.admin_add_incoming.amount = "EUR:5.01" },
+    /* Run wirewatch to observe /admin/add/incoming */
+    { .oc = OC_RUN_WIREWATCH,
+      .label = "wirewatch-3b" },
+    /* Withdraw a 5 EUR coin, at fee of 1 ct */
+    { .oc = OC_WITHDRAW_SIGN,
+      .label = "withdraw-coin-rb",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.reserve_withdraw.reserve_reference = "create-reserve-rb",
+      .details.reserve_withdraw.amount = "EUR:5" },
+    /* Spend 5 EUR of the 5 EUR coin (in full)
+       (merchant would receive EUR:4.99 due to 1 ct deposit fee) */
+    { .oc = OC_DEPOSIT,
+      .label = "deposit-refund-1b",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.deposit.amount = "EUR:5",
+      .details.deposit.coin_ref = "withdraw-coin-rb",
+      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_url\":\"http://localhost:8082/\";, \"account_number\":42  }",
+      .details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice 
cream\", \"value\":\"EUR:5\" } ] }",
+      .details.deposit.refund_deadline = { 0 },
+    },
+    { .oc = OC_CHECK_BANK_TRANSFER,
+      .label = "check_bank_transfer-aai-3b",
+      .details.check_bank_transfer.exchange_base_url = "https://exchange.com/";,
+      .details.check_bank_transfer.amount = "EUR:5.01",
+      .details.check_bank_transfer.account_debit = 42,
+      .details.check_bank_transfer.account_credit = 2
+    },
+    /* Trigger refund (before aggregator had a chance to execute
+       deposit, even though refund deadline was zero) */
+    { .oc = OC_REFUND,
+      .label = "refund-ok-fast",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.refund.amount = "EUR:5",
+      .details.refund.fee = "EUR:0.01",
+      .details.refund.deposit_ref = "deposit-refund-1b",
+    },
+    /* Run transfers. This will do the transfer as refund deadline
+       was 0, except of course because the refund succeeded, the
+       transfer should no longer be done. */
+    { .oc = OC_RUN_AGGREGATOR,
+      .label = "run-aggregator-3b" },
+    /* check that aggregator didn't do anything, as expected */
+    { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
+      .label = "check-refund-fast-not-run" },
+
 
     /* ************** End of refund API testing************* */
 
@@ -3638,7 +3695,7 @@ run (void *cls)
       .expected_response_code = MHD_HTTP_OK,
       .details.deposit.amount = "EUR:0.5",
       .details.deposit.coin_ref = "payback-withdraw-coin-2a",
-      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42  }",
+      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_url\":\"http://localhost:8082/\";, \"account_number\":42  }",
       .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"more ice 
cream\", \"value\":1 } ] }" },
     { .oc = OC_REVOKE,
       .label = "revoke-2",
@@ -3659,7 +3716,7 @@ run (void *cls)
       .expected_response_code = MHD_HTTP_NOT_FOUND,
       .details.deposit.amount = "EUR:1",
       .details.deposit.coin_ref = "payback-withdraw-coin-2b",
-      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42  }",
+      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_url\":\"http://localhost:8082/\";, \"account_number\":42  }",
       .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"more ice 
cream\", \"value\":1 } ] }" },
 
     /* Test deposit fails after payback, with proof in payback */
@@ -3670,7 +3727,7 @@ run (void *cls)
       .expected_response_code = MHD_HTTP_NOT_FOUND,
       .details.deposit.amount = "EUR:0.5",
       .details.deposit.coin_ref = "payback-withdraw-coin-2a",
-      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42  }",
+      .details.deposit.wire_details = "{ \"type\":\"test\", 
\"bank_url\":\"http://localhost:8082/\";, \"account_number\":42  }",
       .details.deposit.contract_terms = "{ \"items\": [ { \"name\":\"extra ice 
cream\", \"value\":1 } ] }" },
 
 
diff --git a/src/exchange-lib/test_exchange_api.conf 
b/src/exchange-lib/test_exchange_api.conf
index 6539928..35267e3 100644
--- a/src/exchange-lib/test_exchange_api.conf
+++ b/src/exchange-lib/test_exchange_api.conf
@@ -98,7 +98,7 @@ CLOSING-FEE-2026 = EUR:0.01
 TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json
 
 # What is the main website of the bank?
-BANK_URI = "http://localhost:8082/";
+BANK_URL = "http://localhost:8082/";
 # From which account at the 'bank' should outgoing wire transfers be made?
 BANK_ACCOUNT_NUMBER = 2
 
diff --git a/src/exchange-lib/test_exchange_api_home/.config/taler/test.json 
b/src/exchange-lib/test_exchange_api_home/.config/taler/test.json
index be5e92c..eca3942 100644
--- a/src/exchange-lib/test_exchange_api_home/.config/taler/test.json
+++ b/src/exchange-lib/test_exchange_api_home/.config/taler/test.json
@@ -2,7 +2,7 @@
   "salt": 
"AZPRFVJ58NM6M7J5CZQPJAH3EW5DYM52AEZ9Y1C1ER3W94QV8D8TQKF6CK8MYQRA9QMSKDQTGZ306ZS9GQ0M6R01CJ20KPP49WFDZK8",
   "name": "The exchange",
   "account_number": 3,
-  "bank_uri": "http://localhost:8082/";,
+  "bank_url": "http://localhost:8082/";,
   "type": "test",
   "sig": 
"RPQXP9S4P8PQP7HEZQNRSZCT0ATNEP8GW0P5TPM34V5RX86FCD670V44R9NETSYDDKB8SZV7TKY9PAJYTY51D3VDWY9XXQ5BPFRXR28"
-}
\ No newline at end of file
+}
diff --git a/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf 
b/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf
index c6b8ef9..2b94dba 100644
--- a/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf
+++ b/src/exchange-lib/test_exchange_api_keys_cherry_picking.conf
@@ -98,7 +98,7 @@ CLOSING-FEE-2026 = EUR:0.01
 TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json
 
 # What is the main website of the bank?
-BANK_URI = "http://localhost:8082/";
+BANK_URL = "http://localhost:8082/";
 # From which account at the 'bank' should outgoing wire transfers be made?
 BANK_ACCOUNT_NUMBER = 2
 
diff --git a/src/exchange-lib/test_exchange_api_new.c 
b/src/exchange-lib/test_exchange_api_new.c
new file mode 100644
index 0000000..f9d6024
--- /dev/null
+++ b/src/exchange-lib/test_exchange_api_new.c
@@ -0,0 +1,142 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange/test_exchange_api_new.c
+ * @brief testcase to test exchange's HTTP API interface
+ * @author Sree Harsha Totakura <address@hidden>
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_signatures.h"
+#include "taler_exchange_service.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler_bank_service.h"
+#include "taler_fakebank_lib.h"
+#include "taler_testing_lib.h"
+
+/**
+ * Configuration file we use.  One (big) configuration is used
+ * for the various components for this test.
+ */
+#define CONFIG_FILE "test_exchange_api.conf"
+
+/**
+ * URL of the fakebank.  Obtained from CONFIG_FILE's 
"exchange-wire-test:BANK_URL" option.
+ */
+static char *fakebank_url;
+
+
+/**
+ * Account number of the exchange at the bank.
+ */
+#define EXCHANGE_ACCOUNT_NO 2
+
+/**
+ * Account number of some user.
+ */
+#define USER_ACCOUNT_NO 42
+
+/**
+ * User name. Never checked by fakebank.
+ */
+#define USER_LOGIN_NAME "user42"
+
+/**
+ * User password. Never checked by fakebank.
+ */
+#define USER_LOGIN_PASS "pass42"
+
+/**
+ * Execute the taler-exchange-wirewatch command with
+ * our configuration file.
+ *
+ * @param label label to use for the command.
+ */
+#define CMD_EXEC_WIREWATCH(label) \
+   TALER_TESTING_cmd_exec_wirewatch (label, CONFIG_FILE)
+
+/**
+ * Run wire transfer of funds from some user's account to the
+ * exchange.
+ *
+ * @param label label to use for the command.
+ * @param amount amount to transfer, i.e. "EUR:1"
+ */
+#define CMD_TRANSFER_TO_EXCHANGE(label,amount) \
+   TALER_TESTING_cmd_fakebank_transfer (label, amount, \
+     fakebank_url, USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO, \
+     USER_LOGIN_NAME, USER_LOGIN_PASS)
+
+
+/**
+ * Main function that will tell the interpreter what commands to run.
+ *
+ * @param cls closure
+ */
+static void
+run (void *cls,
+     struct TALER_TESTING_Interpreter *is)
+{
+  struct TALER_TESTING_Command commands[] = {
+    CMD_TRANSFER_TO_EXCHANGE ("create-reserve-1",
+                              "EUR:5.01"),
+    CMD_EXEC_WIREWATCH ("exec-wirewatch-1"),
+    TALER_TESTING_cmd_end ()
+  };
+
+  TALER_TESTING_run_with_fakebank (is,
+                                   commands,
+                                   fakebank_url);
+}
+
+
+int
+main (int argc,
+      char * const *argv)
+{
+  /* These environment variables get in the way... */
+  unsetenv ("XDG_DATA_HOME");
+  unsetenv ("XDG_CONFIG_HOME");
+  GNUNET_log_setup ("test-exchange-api-new",
+                    "INFO",
+                    NULL);
+  if (NULL == (fakebank_url = TALER_TESTING_prepare_fakebank (CONFIG_FILE)))
+    return 77;
+  TALER_TESTING_cleanup_files (CONFIG_FILE);
+  switch (TALER_TESTING_prepare_exchange (CONFIG_FILE))
+  {
+  case GNUNET_SYSERR:
+    GNUNET_break (0);
+    return 1;
+  case GNUNET_NO:
+    return 77;
+  case GNUNET_OK:
+    if (GNUNET_OK !=
+        TALER_TESTING_setup_with_exchange (&run,
+                                           NULL,
+                                           CONFIG_FILE))
+      return 1;
+  default:
+    GNUNET_break (0);
+    return 1;
+  }
+  return 0;
+}
+
+/* end of test_exchange_api_new.c */
diff --git a/src/exchange-lib/testing_api_cmd_exec_wirewatch.c 
b/src/exchange-lib/testing_api_cmd_exec_wirewatch.c
new file mode 100644
index 0000000..7b95afb
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_exec_wirewatch.c
@@ -0,0 +1,162 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange-lib/testing_api_cmd_exec_wirewatch.c
+ * @brief run the taler-exchange-wirewatch command
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+
+struct WirewatchState
+{
+
+  /**
+   * Process for the wirewatcher.
+   */
+  struct GNUNET_OS_Process *wirewatch_proc;
+
+  /**
+   * Which configuration file should we pass to the process?
+   */
+  const char *config_filename;
+
+};
+
+
+/**
+ * Runs the command.  Note that upon return, the interpreter
+ * will not automatically run the next command, as the command
+ * may continue asynchronously in other scheduler tasks.  Thus,
+ * the command must ensure to eventually call
+ * #TALER_TESTING_interpreter_next() or
+ * #TALER_TESTING_interpreter_fail().
+ *
+ * @param is interpreter state
+ */
+static void
+wirewatch_run (void *cls,
+              const struct TALER_TESTING_Command *cmd,
+              struct TALER_TESTING_Interpreter *is)
+{
+  struct WirewatchState *ws = cls;
+
+  ws->wirewatch_proc
+    = GNUNET_OS_start_process (GNUNET_NO,
+                               GNUNET_OS_INHERIT_STD_ALL,
+                               NULL, NULL, NULL,
+                               "taler-exchange-wirewatch",
+                               "taler-exchange-wirewatch",
+                               "-c", ws->config_filename,
+                               "-t", "test", /* use Taler's bank/fakebank */
+                               "-T", /* exit when done */
+                               NULL);
+  if (NULL == ws->wirewatch_proc)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  TALER_TESTING_wait_for_sigchld (is);
+}
+
+
+/**
+ * Clean up after the command.  Run during forced termination
+ * (CTRL-C) or test failure or test success.
+ *
+ * @param cls closure
+ */
+static void
+wirewatch_cleanup (void *cls,
+                  const struct TALER_TESTING_Command *cmd)
+{
+  struct WirewatchState *ws = cls;
+
+  if (NULL != ws->wirewatch_proc)
+  {
+    GNUNET_break (0 ==
+                  GNUNET_OS_process_kill (ws->wirewatch_proc,
+                                          SIGKILL));
+    GNUNET_OS_process_wait (ws->wirewatch_proc);
+    GNUNET_OS_process_destroy (ws->wirewatch_proc);
+    ws->wirewatch_proc = NULL;
+  }
+  GNUNET_free (ws);
+}
+
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *                 to return in case there were multiple generated
+ *                 by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+wirewatch_traits (void *cls,
+                  void **ret,
+                  const char *trait,
+                  const char *selector)
+{
+  struct WirewatchState *ws = cls;
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_process (NULL,
+                                      &ws->wirewatch_proc),
+    TALER_TESTING_trait_end ()
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  selector);
+}
+
+
+/**
+ * Execute taler-exchange-wirewatch process.
+ *
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_wirewatch (const char *label,
+                                  const char *config_filename)
+{
+  struct TALER_TESTING_Command cmd;
+  struct WirewatchState *ws;
+
+  ws = GNUNET_new (struct WirewatchState);
+  ws->config_filename = config_filename;
+  cmd.cls = ws;
+  cmd.label = label;
+  cmd.run = &wirewatch_run;
+  cmd.cleanup = &wirewatch_cleanup;
+  cmd.traits = &wirewatch_traits;
+  return cmd;
+}
+
+/* end of testing_api_cmd_exec_wirewatch.c */
diff --git a/src/exchange-lib/testing_api_cmd_fakebank_transfer.c 
b/src/exchange-lib/testing_api_cmd_fakebank_transfer.c
new file mode 100644
index 0000000..6b1a99d
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_fakebank_transfer.c
@@ -0,0 +1,414 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange-lib/testing_api_cmd_fakebank_transfer.c
+ * @brief implementation of a fakebank wire transfer command
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_bank_service.h"
+#include "taler_fakebank_lib.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+/**
+ *
+ */
+struct FakebankTransferState
+{
+
+  /**
+   * Label to another admin_add_incoming command if we
+   * should deposit into an existing reserve, NULL if
+   * a fresh reserve should be created.
+   */
+  const char *reserve_reference;
+
+  /**
+   * String describing the amount to add to the reserve.
+   */
+  struct TALER_Amount amount;
+
+  /**
+   * Wire transfer subject. NULL to use public key corresponding
+   * to @e reserve_priv or @e reserve_reference.  Should only be
+   * set manually to test invalid wire transfer subjects.
+   */
+  const char *subject;
+
+  /**
+   * URL to use for the bank.
+   */
+  const char *bank_url;
+
+  /**
+   * Sender (debit) account number.
+   */
+  uint64_t debit_account_no;
+
+  /**
+   * Receiver (credit) account number.
+   */
+  uint64_t credit_account_no;
+
+  /**
+   * Username to use for authentication.
+   */
+  const char *auth_username;
+
+  /**
+   * Password to use for authentication.
+   */
+  const char *auth_password;
+
+  /**
+   * Set (by the interpreter) to the reserve's private key
+   * we used to fill the reserve.
+   */
+  struct TALER_ReservePrivateKeyP reserve_priv;
+
+  /**
+   * Set to the API's handle during the operation.
+   */
+  struct TALER_BANK_AdminAddIncomingHandle *aih;
+
+  /**
+   * Interpreter state while command is running.
+   */
+  struct TALER_TESTING_Interpreter *is;
+
+  /**
+   * Set to the wire transfer's unique ID.
+   */
+  uint64_t serial_id;
+
+};
+
+
+/**
+ * Function called upon completion of our /admin/add/incoming request.
+ *
+ * @param cls closure with the interpreter state
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
+ *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
+ * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param serial_id unique ID of the wire transfer
+ * @param full_response full response from the exchange (for logging, in case 
of errors)
+ */
+static void
+add_incoming_cb (void *cls,
+                 unsigned int http_status,
+                enum TALER_ErrorCode ec,
+                 uint64_t serial_id,
+                 const json_t *full_response)
+{
+  struct FakebankTransferState *fts = cls;
+  struct TALER_TESTING_Interpreter *is = fts->is;
+
+  fts->aih = NULL;
+  fts->serial_id = serial_id;
+  if (MHD_HTTP_OK != http_status)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  TALER_TESTING_interpreter_next (is);
+}
+
+
+/**
+ * Runs the command.  Note that upon return, the interpreter
+ * will not automatically run the next command, as the command
+ * may continue asynchronously in other scheduler tasks.  Thus,
+ * the command must ensure to eventually call
+ * #TALER_TESTING_interpreter_next() or
+ * #TALER_TESTING_interpreter_fail().
+ *
+ * @param is interpreter state
+ */
+static void
+fakebank_transfer_run (void *cls,
+                       const struct TALER_TESTING_Command *cmd,
+                       struct TALER_TESTING_Interpreter *is)
+{
+  struct FakebankTransferState *fts = cls;
+  char *subject;
+  struct TALER_BANK_AuthenticationData auth;
+  struct TALER_ReservePublicKeyP reserve_pub;
+
+  if (NULL != fts->subject)
+  {
+    subject = GNUNET_strdup (fts->subject);
+  }
+  else
+  {
+    /* Use reserve public key as subject */
+    if (NULL != fts->reserve_reference)
+    {
+      const struct TALER_TESTING_Command *ref;
+      struct TALER_ReservePrivateKeyP *reserve_priv;
+
+      ref = TALER_TESTING_interpreter_lookup_command (is,
+                                                      fts->reserve_reference);
+      if (NULL == ref)
+      {
+        GNUNET_break (0);
+        TALER_TESTING_interpreter_fail (is);
+        return;
+      }
+      if (GNUNET_OK !=
+          TALER_TESTING_get_trait_reserve_priv (ref,
+                                                NULL,
+                                                &reserve_priv))
+      {
+        GNUNET_break (0);
+        TALER_TESTING_interpreter_fail (is);
+        return;
+      }
+    }
+    else
+    {
+      struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
+
+      priv = GNUNET_CRYPTO_eddsa_key_create ();
+      fts->reserve_priv.eddsa_priv = *priv;
+      GNUNET_free (priv);
+    }
+    GNUNET_CRYPTO_eddsa_key_get_public (&fts->reserve_priv.eddsa_priv,
+                                        &reserve_pub.eddsa_pub);
+    subject = GNUNET_STRINGS_data_to_string_alloc (&reserve_pub,
+                                                   sizeof (reserve_pub));
+  }
+
+  auth.method = TALER_BANK_AUTH_BASIC;
+  auth.details.basic.username = (char *) fts->auth_username;
+  auth.details.basic.password = (char *) fts->auth_password;
+  fts->is = is;
+  fts->aih
+    = TALER_BANK_admin_add_incoming (TALER_TESTING_interpreter_get_context 
(is),
+                                     fts->bank_url,
+                                     &auth,
+                                     "https://exchange.com/";, /* exchange URL: 
FIXME */
+                                     subject,
+                                     &fts->amount,
+                                     fts->debit_account_no,
+                                     fts->credit_account_no,
+                                     &add_incoming_cb,
+                                     fts);
+  GNUNET_free (subject);
+  if (NULL == fts->aih)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+}
+
+
+/**
+ * Clean up after the command.  Run during forced termination
+ * (CTRL-C) or test failure or test success.
+ *
+ * @param cls closure
+ */
+static void
+fakebank_transfer_cleanup (void *cls,
+                           const struct TALER_TESTING_Command *cmd)
+{
+  struct FakebankTransferState *fts = cls;
+
+  if (NULL != fts->aih)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Command %s did not complete\n",
+                cmd->label);
+    TALER_BANK_admin_add_incoming_cancel (fts->aih);
+  }
+  GNUNET_free (fts);
+}
+
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *                 to return in case there were multiple generated
+ *                 by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+fakebank_transfer_traits (void *cls,
+                          void **ret,
+                          const char *trait,
+                          const char *selector)
+{
+  struct FakebankTransferState *fts = cls;
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_reserve_priv (NULL,
+                                           &fts->reserve_priv),
+    TALER_TESTING_trait_end ()
+  };
+
+  if (NULL != fts->subject)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR; /* we do NOT create a reserve private key */
+  }
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  selector);
+}
+
+
+/**
+ * Create fakebank_transfer command.
+ *
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_fakebank_transfer (const char *label,
+                                     const char *amount,
+                                     const char *bank_url,
+                                     uint64_t debit_account_no,
+                                     uint64_t credit_account_no,
+                                     const char *auth_username,
+                                     const char *auth_password)
+{
+  struct TALER_TESTING_Command cmd;
+  struct FakebankTransferState *fts;
+
+  fts = GNUNET_new (struct FakebankTransferState);
+  fts->bank_url = bank_url;
+  fts->credit_account_no = credit_account_no;
+  fts->debit_account_no = debit_account_no;
+  fts->auth_username = auth_username;
+  fts->auth_password = auth_password;
+  if (GNUNET_OK !=
+      TALER_string_to_amount (amount,
+                              &fts->amount))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to parse amount `%s' at %s\n",
+                amount,
+                label);
+    GNUNET_assert (0);
+  }
+  cmd.cls = fts;
+  cmd.label = label;
+  cmd.run = &fakebank_transfer_run;
+  cmd.cleanup = &fakebank_transfer_cleanup;
+  cmd.traits = &fakebank_transfer_traits;
+  return cmd;
+}
+
+
+/**
+ * Create fakebank_transfer command with custom subject.
+ *
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label,
+                                                  const char *amount,
+                                                  const char *bank_url,
+                                                  uint64_t debit_account_no,
+                                                  uint64_t credit_account_no,
+                                                  const char *auth_username,
+                                                  const char *auth_password,
+                                                  const char *subject)
+{
+  struct TALER_TESTING_Command cmd;
+  struct FakebankTransferState *fts;
+
+  fts = GNUNET_new (struct FakebankTransferState);
+  fts->bank_url = bank_url;
+  fts->credit_account_no = credit_account_no;
+  fts->debit_account_no = debit_account_no;
+  fts->auth_username = auth_username;
+  fts->auth_password = auth_password;
+  fts->subject = subject;
+  if (GNUNET_OK !=
+      TALER_string_to_amount (amount,
+                              &fts->amount))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to parse amount `%s' at %s\n",
+                amount,
+                label);
+    GNUNET_assert (0);
+  }
+  cmd.cls = fts;
+  cmd.label = label;
+  cmd.run = &fakebank_transfer_run;
+  cmd.cleanup = &fakebank_transfer_cleanup;
+  cmd.traits = &fakebank_transfer_traits;
+  return cmd;
+}
+
+
+/**
+ * Create fakebank_transfer command with custom subject.
+ *
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label,
+                                              const char *amount,
+                                              const char *bank_url,
+                                              uint64_t debit_account_no,
+                                              uint64_t credit_account_no,
+                                              const char *auth_username,
+                                              const char *auth_password,
+                                              const char *ref)
+{
+  struct TALER_TESTING_Command cmd;
+  struct FakebankTransferState *fts;
+
+  fts = GNUNET_new (struct FakebankTransferState);
+  fts->bank_url = bank_url;
+  fts->credit_account_no = credit_account_no;
+  fts->debit_account_no = debit_account_no;
+  fts->auth_username = auth_username;
+  fts->auth_password = auth_password;
+  fts->reserve_reference = ref;
+  if (GNUNET_OK !=
+      TALER_string_to_amount (amount,
+                              &fts->amount))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to parse amount `%s' at %s\n",
+                amount,
+                label);
+    GNUNET_assert (0);
+  }
+  cmd.cls = fts;
+  cmd.label = label;
+  cmd.run = &fakebank_transfer_run;
+  cmd.cleanup = &fakebank_transfer_cleanup;
+  cmd.traits = &fakebank_transfer_traits;
+  return cmd;
+}
+
+
+/* end of testing_api_cmd_fakebank_transfer.c */
diff --git a/src/exchange-lib/testing_api_cmd_withdraw.c 
b/src/exchange-lib/testing_api_cmd_withdraw.c
new file mode 100644
index 0000000..642722b
--- /dev/null
+++ b/src/exchange-lib/testing_api_cmd_withdraw.c
@@ -0,0 +1,355 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange-lib/testing_api_cmd_withdraw.c
+ * @brief main interpreter loop for testcases
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+
+struct WithdrawState
+{
+
+  /**
+   * Which reserve should we withdraw from?
+   */
+  const char *reserve_reference;
+
+  /**
+   * String describing the denomination value we should withdraw.
+   * A corresponding denomination key must exist in the exchange's
+   * offerings.  Can be NULL if @e pk is set instead.
+   */
+  struct TALER_Amount amount;
+
+  /**
+   * If @e amount is NULL, this specifies the denomination key to
+   * use.  Otherwise, this will be set (by the interpreter) to the
+   * denomination PK matching @e amount.
+   */
+  const struct TALER_EXCHANGE_DenomPublicKey *pk;
+
+  /**
+   * Exchange we should use for the withdrawal.
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * Interpreter state (during command).
+   */
+  struct TALER_TESTING_Interpreter *is;
+
+  /**
+   * Set (by the interpreter) to the exchange's signature over the
+   * coin's public key.
+   */
+  struct TALER_DenominationSignature sig;
+
+  /**
+   * Private key material of the coin, set by the interpreter.
+   */
+  struct TALER_PlanchetSecretsP ps;
+
+  /**
+   * Withdraw handle (while operation is running).
+   */
+  struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh;
+
+  /**
+   * Expected HTTP response code to the request.
+   */
+  unsigned int expected_response_code;
+
+};
+
+
+/**
+ * Function called upon completion of our /reserve/withdraw request.
+ *
+ * @param cls closure with the withdraw state
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
+ *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
+ * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param sig signature over the coin, NULL on error
+ * @param full_response full response from the exchange (for logging, in case 
of errors)
+ */
+static void
+reserve_withdraw_cb (void *cls,
+                     unsigned int http_status,
+                    enum TALER_ErrorCode ec,
+                     const struct TALER_DenominationSignature *sig,
+                     const json_t *full_response)
+{
+  struct WithdrawState *ws = cls;
+  struct TALER_TESTING_Interpreter *is = ws->is;
+
+  ws->wsh = NULL;
+  if (ws->expected_response_code != http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u to command %s\n",
+                http_status,
+                TALER_TESTING_interpreter_get_current_label (is));
+    json_dumpf (full_response,
+                stderr,
+                0);
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  switch (http_status)
+  {
+  case MHD_HTTP_OK:
+    if (NULL == sig)
+    {
+      GNUNET_break (0);
+      TALER_TESTING_interpreter_fail (is);
+      return;
+    }
+    ws->sig.rsa_signature
+      = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature);
+    break;
+  case MHD_HTTP_FORBIDDEN:
+    /* nothing to check */
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* nothing to check */
+    break;
+  default:
+    /* Unsupported status code (by test harness) */
+    GNUNET_break (0);
+    break;
+  }
+  TALER_TESTING_interpreter_next (is);
+}
+
+
+/**
+ * Runs the command.  Note that upon return, the interpreter
+ * will not automatically run the next command, as the command
+ * may continue asynchronously in other scheduler tasks.  Thus,
+ * the command must ensure to eventually call
+ * #TALER_TESTING_interpreter_next() or
+ * #TALER_TESTING_interpreter_fail().
+ *
+ * @param is interpreter state
+ */
+static void
+withdraw_run (void *cls,
+              const struct TALER_TESTING_Command *cmd,
+              struct TALER_TESTING_Interpreter *is)
+{
+  struct WithdrawState *ws = cls;
+  struct TALER_ReservePrivateKeyP *rp;
+  const struct TALER_TESTING_Command *create_reserve;
+
+  create_reserve
+    = TALER_TESTING_interpreter_lookup_command (is,
+                                                ws->reserve_reference);
+  if (NULL == create_reserve)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  if (GNUNET_OK !=
+      TALER_TESTING_get_trait_reserve_priv (create_reserve,
+                                            NULL,
+                                            &rp))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  TALER_planchet_setup_random (&ws->ps);
+  ws->is = is;
+  ws->wsh
+    = TALER_EXCHANGE_reserve_withdraw (ws->exchange,
+                                       ws->pk,
+                                       rp,
+                                       &ws->ps,
+                                       &reserve_withdraw_cb,
+                                       ws);
+  if (NULL == ws->wsh)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+}
+
+
+/**
+ * Clean up after the command.  Run during forced termination
+ * (CTRL-C) or test failure or test success.
+ *
+ * @param cls closure
+ */
+static void
+withdraw_cleanup (void *cls,
+                  const struct TALER_TESTING_Command *cmd)
+{
+  struct WithdrawState *ws = cls;
+
+  if (NULL != ws->wsh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Command %s did not complete\n",
+                cmd->label);
+    TALER_EXCHANGE_reserve_withdraw_cancel (ws->wsh);
+    ws->wsh = NULL;
+  }
+  if (NULL != ws->sig.rsa_signature)
+  {
+    GNUNET_CRYPTO_rsa_signature_free (ws->sig.rsa_signature);
+    ws->sig.rsa_signature = NULL;
+  }
+  GNUNET_free (ws);
+}
+
+
+/**
+ * Extract information from a command that is useful for other
+ * commands.
+ *
+ * @param cls closure
+ * @param ret[out] result (could be anything)
+ * @param trait name of the trait
+ * @param selector more detailed information about which object
+ *                 to return in case there were multiple generated
+ *                 by the command
+ * @return #GNUNET_OK on success
+ */
+static int
+withdraw_traits (void *cls,
+                 void **ret,
+                 const char *trait,
+                 const char *selector)
+{
+  struct WithdrawState *ws = cls;
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_coin_priv (NULL /* only one coin */,
+                                        &ws->ps.coin_priv),
+    TALER_TESTING_make_trait_blinding_key (NULL /* only one coin */,
+                                           &ws->ps.blinding_key),
+    TALER_TESTING_make_trait_denom_pub (NULL /* only one coin */,
+                                        ws->pk),
+    TALER_TESTING_make_trait_denom_sig (NULL /* only one coin */,
+                                        &ws->sig),
+    TALER_TESTING_trait_end ()
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  selector);
+}
+
+
+/**
+ * Create withdraw command.
+ *
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_withdraw_amount (const char *label,
+                                   struct TALER_EXCHANGE_Handle *exchange,
+                                   const char *reserve_reference,
+                                   const char *amount,
+                                   unsigned int expected_response_code)
+{
+  struct TALER_TESTING_Command cmd;
+  struct WithdrawState *ws;
+
+  ws = GNUNET_new (struct WithdrawState);
+  ws->reserve_reference = reserve_reference;
+
+  if (GNUNET_OK !=
+      TALER_string_to_amount (amount,
+                              &ws->amount))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to parse amount `%s' at %s\n",
+                amount,
+                label);
+    GNUNET_assert (0);
+  }
+  ws->pk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (exchange),
+                                  &ws->amount);
+  if (NULL == ws->pk)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to determine denomination key at %s\n",
+                label);
+    GNUNET_assert (0);
+  }
+  ws->expected_response_code = expected_response_code;
+  ws->exchange = exchange;
+  cmd.cls = ws;
+  cmd.label = label;
+  cmd.run = &withdraw_run;
+  cmd.cleanup = &withdraw_cleanup;
+  cmd.traits = &withdraw_traits;
+  return cmd;
+}
+
+
+/**
+ * Create withdraw command.
+ *
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_withdraw_denomination (const char *label,
+                                         struct TALER_EXCHANGE_Handle 
*exchange,
+                                         const char *reserve_reference,
+                                         const struct 
TALER_EXCHANGE_DenomPublicKey *dk,
+                                         unsigned int expected_response_code)
+{
+  struct TALER_TESTING_Command cmd;
+  struct WithdrawState *ws;
+
+  if (NULL == dk)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Denomination key not specified at %s\n",
+                label);
+    GNUNET_assert (0);
+  }
+  ws = GNUNET_new (struct WithdrawState);
+  ws->reserve_reference = reserve_reference;
+  ws->pk = dk;
+  ws->expected_response_code = expected_response_code;
+  ws->exchange = exchange;
+  cmd.cls = ws;
+  cmd.label = label;
+  cmd.run = &withdraw_run;
+  cmd.cleanup = &withdraw_cleanup;
+  cmd.traits = &withdraw_traits;
+  return cmd;
+}
+
+
+
+
+
+/* end of testing_api_cmd_withdraw.c */
diff --git a/src/exchange-lib/testing_api_helpers.c 
b/src/exchange-lib/testing_api_helpers.c
new file mode 100644
index 0000000..1721b3e
--- /dev/null
+++ b/src/exchange-lib/testing_api_helpers.c
@@ -0,0 +1,370 @@
+
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange-lib/testing_api_helpers.c
+ * @brief helper functions
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+
+/**
+ * Remove files from previous runs
+ */
+void
+TALER_TESTING_cleanup_files (const char *config_name)
+{
+  struct GNUNET_CONFIGURATION_Handle *cfg;
+  char *dir;
+
+  cfg = GNUNET_CONFIGURATION_create ();
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_load (cfg,
+                                 config_name))
+  {
+    GNUNET_break (0);
+    GNUNET_CONFIGURATION_destroy (cfg);
+    exit (77);
+  }
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                                          "exchange",
+                                                          "keydir",
+                                                          &dir));
+  if (GNUNET_YES ==
+      GNUNET_DISK_directory_test (dir,
+                                  GNUNET_NO))
+    GNUNET_break (GNUNET_OK ==
+                  GNUNET_DISK_directory_remove (dir));
+  GNUNET_free (dir);
+  GNUNET_CONFIGURATION_destroy (cfg);
+}
+
+
+/**
+ * Prepare launching an exchange.  Checks that the configured
+ * port is available, runs taler-exchange-keyup,
+ * taler-auditor-sign and taler-exchange-dbinit.  Does not
+ * launch the exchange process itself.
+ *
+ * @param config_filename configuration file to use
+ * @return #GNUNET_OK on success, #GNUNET_NO if test should be skipped,
+ *         #GNUNET_SYSERR on test failure
+ */
+int
+TALER_TESTING_prepare_exchange (const char *config_filename)
+{
+  struct GNUNET_OS_Process *proc;
+  enum GNUNET_OS_ProcessStatusType type;
+  unsigned long code;
+  struct GNUNET_CONFIGURATION_Handle *cfg;
+  unsigned long long port;
+
+  cfg = GNUNET_CONFIGURATION_create ();
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_load (cfg,
+                                 config_filename))
+    return GNUNET_NO;
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_number (cfg,
+                                             "exchange",
+                                             "PORT",
+                                             &port))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "exchange",
+                               "PORT");
+    GNUNET_CONFIGURATION_destroy (cfg);
+    return GNUNET_NO;
+  }
+  GNUNET_CONFIGURATION_destroy (cfg);
+  if (GNUNET_OK !=
+      GNUNET_NETWORK_test_port_free (IPPROTO_TCP,
+                                    (uint16_t) port))
+  {
+    fprintf (stderr,
+             "Required port %llu not available, skipping.\n",
+            port);
+    return GNUNET_NO;
+  }
+
+  proc = GNUNET_OS_start_process (GNUNET_NO,
+                                  GNUNET_OS_INHERIT_STD_ALL,
+                                  NULL, NULL, NULL,
+                                  "taler-exchange-keyup",
+                                  "taler-exchange-keyup",
+                                  "-c", "test_exchange_api.conf",
+                                  "-o", "auditor.in",
+                                  NULL);
+  if (NULL == proc)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Failed to run `taler-exchange-keyup`, is your PATH 
correct?\n");
+    return GNUNET_NO;
+  }
+  GNUNET_OS_process_wait (proc);
+  GNUNET_OS_process_destroy (proc);
+
+  proc = GNUNET_OS_start_process (GNUNET_NO,
+                                  GNUNET_OS_INHERIT_STD_ALL,
+                                  NULL, NULL, NULL,
+                                  "taler-auditor-sign",
+                                  "taler-auditor-sign",
+                                  "-c", "test_exchange_api.conf",
+                                  "-u", "http://auditor/";,
+                                  "-m", 
"98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG",
+                                  "-r", "auditor.in",
+                                  "-o", 
"test_exchange_api_home/.local/share/taler/auditors/auditor.out",
+                                  NULL);
+  if (NULL == proc)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Failed to run `taler-exchange-keyup`, is your PATH 
correct?\n");
+    return GNUNET_NO;
+  }
+  GNUNET_OS_process_wait (proc);
+  GNUNET_OS_process_destroy (proc);
+
+  proc = GNUNET_OS_start_process (GNUNET_NO,
+                                  GNUNET_OS_INHERIT_STD_ALL,
+                                  NULL, NULL, NULL,
+                                  "taler-exchange-dbinit",
+                                  "taler-exchange-dbinit",
+                                  "-c", "test_exchange_api.conf",
+                                  "-r",
+                                  NULL);
+  if (NULL == proc)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Failed to run `taler-exchange-dbinit`, is your PATH 
correct?\n");
+    return GNUNET_NO;
+  }
+  if (GNUNET_SYSERR ==
+      GNUNET_OS_process_wait_status (proc,
+                                     &type,
+                                     &code))
+  {
+    GNUNET_break (0);
+    GNUNET_OS_process_destroy (proc);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_OS_process_destroy (proc);
+  if ( (type == GNUNET_OS_PROCESS_EXITED) &&
+       (0 != code) )
+  {
+    fprintf (stderr,
+             "Failed to setup database\n");
+    return GNUNET_NO;
+  }
+  if ( (type != GNUNET_OS_PROCESS_EXITED) ||
+       (0 != code) )
+  {
+    fprintf (stderr,
+             "Unexpected error running `taler-exchange-dbinit'!\n");
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Find denomination key matching the given amount.
+ *
+ * @param keys array of keys to search
+ * @param amount coin value to look for
+ * @return NULL if no matching key was found
+ */
+const struct TALER_EXCHANGE_DenomPublicKey *
+TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,
+                       const struct TALER_Amount *amount)
+{
+  struct GNUNET_TIME_Absolute now;
+  struct TALER_EXCHANGE_DenomPublicKey *pk;
+  char *str;
+
+  now = GNUNET_TIME_absolute_get ();
+  for (unsigned int i=0;i<keys->num_denom_keys;i++)
+  {
+    pk = &keys->denom_keys[i];
+    if ( (0 == TALER_amount_cmp (amount,
+                                 &pk->value)) &&
+         (now.abs_value_us >= pk->valid_from.abs_value_us) &&
+         (now.abs_value_us < pk->withdraw_valid_until.abs_value_us) )
+      return pk;
+  }
+  /* do 2nd pass to check if expiration times are to blame for failure */
+  str = TALER_amount_to_string (amount);
+  for (unsigned int i=0;i<keys->num_denom_keys;i++)
+  {
+    pk = &keys->denom_keys[i];
+    if ( (0 == TALER_amount_cmp (amount,
+                                 &pk->value)) &&
+         ( (now.abs_value_us < pk->valid_from.abs_value_us) ||
+           (now.abs_value_us > pk->withdraw_valid_until.abs_value_us) ) )
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Have denomination key for `%s', but with wrong expiration 
range %llu vs [%llu,%llu)\n",
+                  str,
+                  (unsigned long long) now.abs_value_us,
+                  (unsigned long long) pk->valid_from.abs_value_us,
+                  (unsigned long long) pk->withdraw_valid_until.abs_value_us);
+      GNUNET_free (str);
+      return NULL;
+    }
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "No denomination key for amount %s found\n",
+              str);
+  GNUNET_free (str);
+  return NULL;
+}
+
+
+/**
+ * Initialize scheduler loop and curl context for the testcase
+ * including starting and stopping the exchange using the given
+ * configuration file.
+ */
+int
+TALER_TESTING_setup_with_exchange (TALER_TESTING_Main main_cb,
+                                   void *main_cb_cls,
+                                   const char *config_file)
+{
+  int result;
+  unsigned int iter;
+  struct GNUNET_OS_Process *exchanged;
+
+  exchanged = GNUNET_OS_start_process (GNUNET_NO,
+                                       GNUNET_OS_INHERIT_STD_ALL,
+                                       NULL, NULL, NULL,
+                                       "taler-exchange-httpd",
+                                       "taler-exchange-httpd",
+                                       "-c", config_file,
+                                       "-i",
+                                       NULL);
+  /* give child time to start and bind against the socket */
+  fprintf (stderr,
+           "Waiting for `taler-exchange-httpd' to be ready");
+  iter = 0;
+  do
+    {
+      if (10 == iter)
+      {
+       fprintf (stderr,
+                "Failed to launch `taler-exchange-httpd' (or `wget')\n");
+       GNUNET_OS_process_kill (exchanged,
+                               SIGTERM);
+       GNUNET_OS_process_wait (exchanged);
+       GNUNET_OS_process_destroy (exchanged);
+       return 77;
+      }
+      fprintf (stderr, ".");
+      sleep (1);
+      iter++;
+    }
+  while (0 != system ("wget -q -t 1 -T 1 http://127.0.0.1:8081/keys -o 
/dev/null -O /dev/null"));
+  fprintf (stderr, "\n");
+
+  result = TALER_TESTING_setup (main_cb,
+                                main_cb_cls);
+  GNUNET_break (0 ==
+                GNUNET_OS_process_kill (exchanged,
+                                        SIGTERM));
+  GNUNET_break (GNUNET_OK ==
+                GNUNET_OS_process_wait (exchanged));
+  GNUNET_OS_process_destroy (exchanged);
+  return result;
+}
+
+
+/**
+ * Test port in URL string for availability.
+ */
+int
+TALER_TESTING_url_port_free (const char *url)
+{
+  const char *port;
+  long pnum;
+
+  port = strrchr (url,
+                  (unsigned char) ':');
+  if (NULL == port)
+    pnum = 80;
+  else
+    pnum = strtol (port + 1, NULL, 10);
+  if (GNUNET_OK !=
+      GNUNET_NETWORK_test_port_free (IPPROTO_TCP,
+                                    pnum))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Port %u not available.\n",
+                (unsigned int) pnum);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Prepare launching a fakebank.  Check that the configuration
+ * file has the right option, and that the port is avaiable.
+ * If everything is OK, return the configured URL of the fakebank.
+ *
+ * @param config_filename configuration file to use
+ * @return NULL on error, fakebank URL otherwise
+ */
+char *
+TALER_TESTING_prepare_fakebank (const char *config_filename)
+{
+  struct GNUNET_CONFIGURATION_Handle *cfg;
+  char *fakebank_url;
+
+  cfg = GNUNET_CONFIGURATION_create ();
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_load (cfg,
+                                 config_filename))
+    return NULL;
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             "exchange-wire-test",
+                                             "BANK_URL",
+                                             &fakebank_url))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
+                               "exchange-wire-test",
+                               "BANK_URL");
+    GNUNET_CONFIGURATION_destroy (cfg);
+    return NULL;
+  }
+  GNUNET_CONFIGURATION_destroy (cfg);
+  if (GNUNET_OK !=
+      TALER_TESTING_url_port_free (fakebank_url))
+  {
+    GNUNET_free (fakebank_url);
+    return NULL;
+  }
+  return fakebank_url;
+}
+
+/* end of testing_api_helpers.c */
diff --git a/src/exchange-lib/testing_api_loop.c 
b/src/exchange-lib/testing_api_loop.c
new file mode 100644
index 0000000..8c23649
--- /dev/null
+++ b/src/exchange-lib/testing_api_loop.c
@@ -0,0 +1,492 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange-lib/testing_api_loop.c
+ * @brief main interpreter loop for testcases
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+#include "taler_fakebank_lib.h"
+
+
+/**
+ * Global state of the interpreter, used by a command
+ * to access information about other commands.
+ */
+struct TALER_TESTING_Interpreter
+{
+
+  /**
+   * Commands the interpreter will run.
+   */
+  struct TALER_TESTING_Command *commands;
+
+  /**
+   * Interpreter task (if one is scheduled).
+   */
+  struct GNUNET_SCHEDULER_Task *task;
+
+  /**
+   * ID of task called whenever we get a SIGCHILD.
+   * Used for #TALER_TESTING_wait_for_sigchld().
+   */
+  struct GNUNET_SCHEDULER_Task *child_death_task;
+
+  /**
+   * Main execution context for the main loop.
+   */
+  struct GNUNET_CURL_Context *ctx;
+
+  /**
+   * Context for running the CURL event loop.
+   */
+  struct GNUNET_CURL_RescheduleContext *rc;
+
+  /**
+   * Handle to our fakebank, if #TALER_TESTING_run_with_fakebank() was used.
+   * Otherwise NULL.
+   */
+  struct TALER_FAKEBANK_Handle *fakebank;
+
+  /**
+   * Task run on timeout.
+   */
+  struct GNUNET_SCHEDULER_Task *timeout_task;
+
+  /**
+   * Instruction pointer.  Tells #interpreter_run() which
+   * instruction to run next.
+   */
+  unsigned int ip;
+
+  /**
+   * Result of the testcases, #GNUNET_OK on success
+   */
+  int result;
+
+};
+
+
+/**
+ * Pipe used to communicate child death via signal.
+ * Must be global, as used in signal handler!
+ */
+static struct GNUNET_DISK_PipeHandle *sigpipe;
+
+
+
+/**
+ * Lookup command by label.
+ *
+ * @param is interpreter state to search
+ * @param label label to look for
+ * @return NULL if command was not found
+ */
+const struct TALER_TESTING_Command *
+TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is,
+                                          const char *label)
+{
+  const struct TALER_TESTING_Command *cmd;
+
+  if (NULL == label)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Attempt to lookup command for empty label\n");
+    return NULL;
+  }
+  for (unsigned int i=0;NULL != (cmd = &is->commands[i])->label;i++)
+    if ( (NULL != cmd->label) &&
+         (0 == strcmp (cmd->label,
+                       label)) )
+      return cmd;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Command not found: %s\n",
+              label);
+  return NULL;
+
+}
+
+
+/**
+ * Obtain main execution context for the main loop.
+ */
+struct GNUNET_CURL_Context *
+TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is)
+{
+  return is->ctx;
+}
+
+
+struct TALER_FAKEBANK_Handle *
+TALER_TESTING_interpreter_get_fakebank (struct TALER_TESTING_Interpreter *is)
+{
+  return is->fakebank;
+}
+
+
+void
+TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is,
+                                 struct TALER_TESTING_Command *commands,
+                                 const char *bank_url)
+{
+  const char *port;
+  long pnum;
+
+  port = strrchr (bank_url,
+                  (unsigned char) ':');
+  if (NULL == port)
+    pnum = 80;
+  else
+    pnum = strtol (port + 1, NULL, 10);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Staring Fakebank on port %u (%s)\n",
+              (unsigned int) pnum,
+              bank_url);
+  is->fakebank = TALER_FAKEBANK_start ((uint16_t) pnum);
+  if (NULL == is->fakebank)
+  {
+    GNUNET_break (0);
+    is->result = GNUNET_SYSERR;
+    return;
+  }
+  TALER_TESTING_run (is,
+                     commands);
+}
+
+
+/**
+ * Run the main interpreter loop that performs exchange operations.
+ *
+ * @param cls contains the `struct InterpreterState`
+ */
+static void
+interpreter_run (void *cls);
+
+
+/**
+ * Current command is done, run the next one.
+ */
+void
+TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *i)
+{
+  if (GNUNET_SYSERR == i->result)
+    return; /* ignore, we already failed! */
+  i->ip++;
+  i->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+                                      i);
+}
+
+
+/**
+ * Current command failed, clean up and fail the test case.
+ */
+void
+TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *i)
+{
+  i->result = GNUNET_SYSERR;
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Create command array terminator.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_end (void)
+{
+  static struct TALER_TESTING_Command cmd;
+
+  return cmd;
+}
+
+
+/**
+ * Obtain current label.
+ */
+const char *
+TALER_TESTING_interpreter_get_current_label (struct TALER_TESTING_Interpreter 
*is)
+{
+  struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
+
+  return cmd->label;
+}
+
+
+/**
+ * Run the main interpreter loop that performs exchange operations.
+ *
+ * @param cls contains the `struct TALER_TESTING_Interpreter`
+ */
+static void
+interpreter_run (void *cls)
+{
+  struct TALER_TESTING_Interpreter *is = cls;
+  struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
+
+  is->task = NULL;
+
+  if (NULL == cmd->label)
+  {
+    is->result = GNUNET_OK;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Running command `%s'\n",
+              cmd->label);
+  cmd->run (cmd->cls,
+            cmd,
+            is);
+}
+
+
+/**
+ * Function run when the test terminates (good or bad).
+ * Cleans up our state.
+ *
+ * @param cls the interpreter state.
+ */
+static void
+do_shutdown (void *cls)
+{
+  struct TALER_TESTING_Interpreter *is = cls;
+  struct TALER_TESTING_Command *cmd;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Executing shutdown at `%s'\n",
+              is->commands[is->ip].label);
+  for (unsigned int j=0;NULL != (cmd = &is->commands[j])->label;j++)
+    cmd->cleanup (cmd->cls,
+                  cmd);
+  if (NULL != is->task)
+  {
+    GNUNET_SCHEDULER_cancel (is->task);
+    is->task = NULL;
+  }
+  if (NULL != is->ctx)
+  {
+    GNUNET_CURL_fini (is->ctx);
+    is->ctx = NULL;
+  }
+  if (NULL != is->rc)
+  {
+    GNUNET_CURL_gnunet_rc_destroy (is->rc);
+    is->rc = NULL;
+  }
+  if (NULL != is->timeout_task)
+  {
+    GNUNET_SCHEDULER_cancel (is->timeout_task);
+    is->timeout_task = NULL;
+  }
+  if (NULL != is->child_death_task)
+  {
+    GNUNET_SCHEDULER_cancel (is->child_death_task);
+    is->child_death_task = NULL;
+  }
+  if (NULL != is->fakebank)
+  {
+    TALER_FAKEBANK_stop (is->fakebank);
+    is->fakebank = NULL;
+  }
+  GNUNET_free_non_null (is->commands);
+}
+
+
+/**
+ * Function run when the test terminates (good or bad) with timeout.
+ *
+ * @param cls NULL
+ */
+static void
+do_timeout (void *cls)
+{
+  struct TALER_TESTING_Interpreter *is = cls;
+
+  is->timeout_task = NULL;
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Task triggered whenever we receive a SIGCHLD (child
+ * process died).
+ *
+ * @param cls closure
+ */
+static void
+maint_child_death (void *cls)
+{
+  struct TALER_TESTING_Interpreter *is = cls;
+  struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
+  const struct GNUNET_DISK_FileHandle *pr;
+  struct GNUNET_OS_Process **processp;
+  char c[16];
+
+  is->child_death_task = NULL;
+  pr = GNUNET_DISK_pipe_handle (sigpipe,
+                                GNUNET_DISK_PIPE_END_READ);
+  GNUNET_break (0 <
+                GNUNET_DISK_file_read (pr,
+                                       &c,
+                                       sizeof (c)));
+  if (GNUNET_OK !=
+      TALER_TESTING_get_trait_process (cmd,
+                                       NULL,
+                                       &processp))
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  GNUNET_OS_process_wait (*processp);
+  GNUNET_OS_process_destroy (*processp);
+  *processp = NULL;
+  TALER_TESTING_interpreter_next (is);
+}
+
+
+/**
+ * Wait until we receive SIGCHLD signal.
+ * Then obtain the process trait of the current
+ * command, wait on the the zombie and continue
+ * with the next command.
+ */
+void
+TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is)
+{
+  const struct GNUNET_DISK_FileHandle *pr;
+
+  GNUNET_assert (NULL == is->child_death_task);
+  pr = GNUNET_DISK_pipe_handle (sigpipe,
+                                GNUNET_DISK_PIPE_END_READ);
+  is->child_death_task
+    = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+                                      pr,
+                                      &maint_child_death,
+                                      is);
+
+}
+
+
+void
+TALER_TESTING_run (struct TALER_TESTING_Interpreter *is,
+                   struct TALER_TESTING_Command *commands)
+{
+  unsigned int i;
+
+  for (i=0;NULL != commands[i].label;i++) ;
+  is->commands = GNUNET_new_array (i + 1,
+                                   struct TALER_TESTING_Command);
+  memcpy (is->commands,
+          commands,
+          sizeof (struct TALER_TESTING_Command) * i);
+  is->timeout_task
+    = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+                                    (GNUNET_TIME_UNIT_SECONDS, 300),
+                                    &do_timeout,
+                                    is);
+  GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
+                                 is);
+  is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+                                      is);
+}
+
+
+struct MainContext
+{
+  TALER_TESTING_Main main_cb;
+
+  void *main_cb_cls;
+
+  struct TALER_TESTING_Interpreter *is;
+
+};
+
+
+/**
+ * Signal handler called for SIGCHLD.  Triggers the
+ * respective handler by writing to the trigger pipe.
+ */
+static void
+sighandler_child_death ()
+{
+  static char c;
+  int old_errno = errno;       /* back-up errno */
+
+  GNUNET_break (1 ==
+               GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
+                                       (sigpipe,
+                                         GNUNET_DISK_PIPE_END_WRITE),
+                                       &c, sizeof (c)));
+  errno = old_errno;           /* restore errno */
+}
+
+
+static void
+main_wrapper (void *cls)
+{
+  struct MainContext *main_ctx = cls;
+  struct TALER_TESTING_Interpreter *is = main_ctx->is;
+
+  is->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
+                              &is->rc);
+  GNUNET_assert (NULL != is->ctx);
+  is->rc = GNUNET_CURL_gnunet_rc_create (is->ctx);
+  main_ctx->main_cb (main_ctx->main_cb_cls,
+                     main_ctx->is);
+}
+
+
+/**
+ * Initialize scheduler loop and curl context for the testcase.
+ */
+int
+TALER_TESTING_setup (TALER_TESTING_Main main_cb,
+                     void *main_cb_cls)
+{
+  struct TALER_TESTING_Interpreter is;
+  struct MainContext main_ctx = {
+    .main_cb = main_cb,
+    .main_cb_cls = main_cb_cls,
+    .is = &is
+  };
+  struct GNUNET_SIGNAL_Context *shc_chld;
+
+  memset (&is,
+          0,
+          sizeof (is));
+  sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO,
+                              GNUNET_NO, GNUNET_NO);
+  GNUNET_assert (NULL != sigpipe);
+  shc_chld = GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD,
+                                            &sighandler_child_death);
+  GNUNET_SCHEDULER_run (&main_wrapper,
+                        &main_ctx);
+  GNUNET_SIGNAL_handler_uninstall (shc_chld);
+  GNUNET_DISK_pipe_close (sigpipe);
+  sigpipe = NULL;
+  return is.result;
+}
+
+
+/* end of testing_api_loop.c */
diff --git a/src/exchange-lib/testing_api_trait_blinding_key.c 
b/src/exchange-lib/testing_api_trait_blinding_key.c
new file mode 100644
index 0000000..d23fd93
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_blinding_key.c
@@ -0,0 +1,67 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange-lib/testing_api_trait_blinding_key.c
+ * @brief main interpreter loop for testcases
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+#define TALER_TESTING_TRAIT_BLINDING_KEY "blinding-key"
+
+
+/**
+ * Obtain a blinding key from a @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on offer
+ * @param blinding_key[out] set to the blinding key of the coin
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_blinding_key (const struct TALER_TESTING_Command *cmd,
+                                      const char *selector,
+                                      struct TALER_DenominationBlindingKeyP 
**blinding_key)
+{
+  return cmd->traits (cmd->cls,
+                      (void **) blinding_key,
+                      TALER_TESTING_TRAIT_BLINDING_KEY,
+                      selector);
+}
+
+
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_blinding_key (const char *selector,
+                                       const struct 
TALER_DenominationBlindingKeyP *blinding_key)
+{
+  struct TALER_TESTING_Trait ret = {
+    .selector = selector,
+    .trait_name = TALER_TESTING_TRAIT_BLINDING_KEY,
+    .ptr = (const void *) blinding_key
+  };
+
+  return ret;
+}
+
+
+/* end of testing_api_trait_blinding_key.c */
diff --git a/src/exchange-lib/testing_api_trait_coin_priv.c 
b/src/exchange-lib/testing_api_trait_coin_priv.c
new file mode 100644
index 0000000..6ec4170
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_coin_priv.c
@@ -0,0 +1,67 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange-lib/testing_api_trait_coin_priv.c
+ * @brief main interpreter loop for testcases
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+#define TALER_TESTING_TRAIT_COIN_PRIVATE_KEY "coin-private-key"
+
+
+/**
+ * Obtain a coin private key from a @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on offer
+ * @param coin_priv[out] set to the private key of the coin
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_coin_priv (const struct TALER_TESTING_Command *cmd,
+                                   const char *selector,
+                                   struct TALER_CoinSpendPrivateKeyP 
**coin_priv)
+{
+  return cmd->traits (cmd->cls,
+                      (void **) coin_priv,
+                      TALER_TESTING_TRAIT_COIN_PRIVATE_KEY,
+                      selector);
+}
+
+
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_coin_priv (const char *selector,
+                                    const struct TALER_CoinSpendPrivateKeyP 
*coin_priv)
+{
+  struct TALER_TESTING_Trait ret = {
+    .selector = selector,
+    .trait_name = TALER_TESTING_TRAIT_COIN_PRIVATE_KEY,
+    .ptr = (const void *) coin_priv
+  };
+
+  return ret;
+}
+
+
+/* end of testing_api_trait_coin_priv.c */
diff --git a/src/exchange-lib/testing_api_trait_denom_pub.c 
b/src/exchange-lib/testing_api_trait_denom_pub.c
new file mode 100644
index 0000000..3113818
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_denom_pub.c
@@ -0,0 +1,67 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange-lib/testing_api_trait_denom_pub.c
+ * @brief main interpreter loop for testcases
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+#define TALER_TESTING_TRAIT_DENOM_PUB "denomination-public-key"
+
+
+/**
+ * Obtain a denomination public key from a @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on offer
+ * @param denom_pub[out] set to the blinding key of the coin
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_denom_pub (const struct TALER_TESTING_Command *cmd,
+                                   const char *selector,
+                                   struct TALER_EXCHANGE_DenomPublicKey 
**denom_pub)
+{
+  return cmd->traits (cmd->cls,
+                      (void **) denom_pub,
+                      TALER_TESTING_TRAIT_DENOM_PUB,
+                      selector);
+}
+
+
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_denom_pub (const char *selector,
+                                    const struct TALER_EXCHANGE_DenomPublicKey 
*denom_pub)
+{
+  struct TALER_TESTING_Trait ret = {
+    .selector = selector,
+    .trait_name = TALER_TESTING_TRAIT_DENOM_PUB,
+    .ptr = (const void *) denom_pub
+  };
+
+  return ret;
+}
+
+
+/* end of testing_api_trait_denom_pub.c */
diff --git a/src/exchange-lib/testing_api_trait_denom_sig.c 
b/src/exchange-lib/testing_api_trait_denom_sig.c
new file mode 100644
index 0000000..66785c7
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_denom_sig.c
@@ -0,0 +1,67 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange-lib/testing_api_trait_denom_sig.c
+ * @brief main interpreter loop for testcases
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+#define TALER_TESTING_TRAIT_DENOM_SIG "denomination-signature"
+
+
+/**
+ * Obtain a denomination signature from a @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which signature to pick if @a cmd has multiple on offer
+ * @param denom_sig[out] set to the signature
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_denom_sig (const struct TALER_TESTING_Command *cmd,
+                                   const char *selector,
+                                   struct TALER_DenominationSignature 
**denom_sig)
+{
+  return cmd->traits (cmd->cls,
+                      (void **) denom_sig,
+                      TALER_TESTING_TRAIT_DENOM_SIG,
+                      selector);
+}
+
+
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_denom_sig (const char *selector,
+                                    const struct TALER_DenominationSignature 
*denom_sig)
+{
+  struct TALER_TESTING_Trait ret = {
+    .selector = selector,
+    .trait_name = TALER_TESTING_TRAIT_DENOM_SIG,
+    .ptr = (const void *) denom_sig
+  };
+
+  return ret;
+}
+
+
+/* end of testing_api_trait_denom_sig.c */
diff --git a/src/exchange-lib/testing_api_trait_process.c 
b/src/exchange-lib/testing_api_trait_process.c
new file mode 100644
index 0000000..877eca9
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_process.c
@@ -0,0 +1,67 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange-lib/testing_api_trait_process.c
+ * @brief trait for a command that launches a process
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+#define TALER_TESTING_TRAIT_PROCESS "process"
+
+
+/**
+ * Obtain location where a command stores a pointer to a process
+ *
+ * @param cmd command to extract trait from
+ * @param selector which process to pick if @a cmd has multiple on offer
+ * @param coin_priv[out] set to address of the pointer to the process
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_process (const struct TALER_TESTING_Command *cmd,
+                                 const char *selector,
+                                 struct GNUNET_OS_Process ***processp)
+{
+  return cmd->traits (cmd->cls,
+                      (void **) processp,
+                      TALER_TESTING_TRAIT_PROCESS,
+                      selector);
+}
+
+
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_process (const char *selector,
+                                  struct GNUNET_OS_Process **processp)
+{
+  struct TALER_TESTING_Trait ret = {
+    .selector = selector,
+    .trait_name = TALER_TESTING_TRAIT_PROCESS,
+    .ptr = (const void *) processp
+  };
+
+  return ret;
+}
+
+
+/* end of testing_api_trait_process.c */
diff --git a/src/exchange-lib/testing_api_trait_reserve_priv.c 
b/src/exchange-lib/testing_api_trait_reserve_priv.c
new file mode 100644
index 0000000..a849f76
--- /dev/null
+++ b/src/exchange-lib/testing_api_trait_reserve_priv.c
@@ -0,0 +1,65 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange-lib/testing_api_trait_reserve_priv.c
+ * @brief implements reserve private key trait
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+#define TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY "reserve-private-key"
+
+/**
+ * Obtain a reserve private key from a @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on offer
+ * @param reserve_priv[out] set to the private key of the reserve
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_reserve_priv (const struct TALER_TESTING_Command *cmd,
+                                      const char *selector,
+                                      struct TALER_ReservePrivateKeyP 
**reserve_priv)
+{
+  return cmd->traits (cmd->cls,
+                      (void **) reserve_priv,
+                      TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY,
+                      selector);
+}
+
+
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_reserve_priv (const char *selector,
+                                       const struct TALER_ReservePrivateKeyP 
*reserve_priv)
+{
+  struct TALER_TESTING_Trait ret = {
+    .selector = selector,
+    .trait_name = TALER_TESTING_TRAIT_RESERVE_PRIVATE_KEY,
+    .ptr = (const void *) reserve_priv
+  };
+  return ret;
+}
+
+
+/* end of testing_api_trait_reserve_priv.c */
diff --git a/src/exchange-lib/testing_api_traits.c 
b/src/exchange-lib/testing_api_traits.c
new file mode 100644
index 0000000..3983d32
--- /dev/null
+++ b/src/exchange-lib/testing_api_traits.c
@@ -0,0 +1,70 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange-lib/testing_api_traits.c
+ * @brief loop for trait resolution
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+
+struct TALER_TESTING_Trait
+TALER_TESTING_trait_end ()
+{
+  struct TALER_TESTING_Trait end = {
+    .selector = NULL,
+    .trait_name = NULL,
+    .ptr = NULL
+  };
+
+  return end;
+}
+
+
+int
+TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
+                         void **ret,
+                         const char *trait,
+                         const char *selector)
+{
+  for (unsigned int i=0;
+       NULL != traits[i].trait_name;
+       i++)
+  {
+    if ( (0 == strcmp (trait,
+                       traits[i].trait_name)) &&
+         ( (NULL == selector) ||
+           (0 == strcasecmp (selector,
+                             traits[i].selector) ) ) )
+    {
+      *ret = (void *) traits[i].ptr;
+      return GNUNET_OK;
+    }
+  }
+  /* FIXME: log */
+  return GNUNET_SYSERR;
+}
+
+
+
+/* end of testing_api_traits.c */
diff --git a/src/exchange-tools/Makefile.am b/src/exchange-tools/Makefile.am
index 8eb2ac2..8d00415 100644
--- a/src/exchange-tools/Makefile.am
+++ b/src/exchange-tools/Makefile.am
@@ -15,7 +15,6 @@ endif
 bin_PROGRAMS = \
   taler-exchange-keyup \
   taler-exchange-keycheck \
-  taler-exchange-reservemod \
   taler-exchange-wire \
   taler-exchange-dbinit
 
@@ -51,23 +50,6 @@ taler_exchange_keycheck_LDADD = \
   -lgnunetutil  $(XLIB)
 taler_exchange_keycheck_LDFLAGS = $(POSTGRESQL_LDFLAGS)
 
-taler_exchange_reservemod_SOURCES = \
-  taler-exchange-reservemod.c
-taler_exchange_reservemod_LDADD = \
-  $(LIBGCRYPT_LIBS) \
-  $(top_builddir)/src/util/libtalerutil.la \
-  $(top_builddir)/src/pq/libtalerpq.la \
-  $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
-  -lgnunetjson \
-  -lgnunetutil \
-  -ljansson $(XLIB)
-taler_exchange_reservemod_LDFLAGS = \
-  $(POSTGRESQL_LDFLAGS)
-taler_exchange_reservemod_CPPFLAGS = \
-  -I$(top_srcdir)/src/include \
-  -I$(top_srcdir)/src/pq/ \
-  $(POSTGRESQL_CPPFLAGS)
-
 taler_exchange_dbinit_SOURCES = \
   taler-exchange-dbinit.c
 taler_exchange_dbinit_LDADD = \
diff --git a/src/exchange-tools/taler-exchange-reservemod.c 
b/src/exchange-tools/taler-exchange-reservemod.c
deleted file mode 100644
index e901b69..0000000
--- a/src/exchange-tools/taler-exchange-reservemod.c
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014-2017 GNUnet e.V.
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along with
-  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-reservemod.c
- * @brief Modify reserves.  Allows manipulation of reserve balances.
- * @author Florian Dold
- * @author Benedikt Mueller
- */
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_json_lib.h>
-#include <libpq-fe.h>
-#include <jansson.h>
-#include "taler_exchangedb_plugin.h"
-
-/**
- * Director of the exchange, containing the keys.
- */
-static char *exchange_directory;
-
-/**
- * Our DB plugin.
- */
-static struct TALER_EXCHANGEDB_Plugin *plugin;
-
-/**
- * Public key of the reserve.
- */
-static struct TALER_ReservePublicKeyP reserve_pub;
-
-/**
- * Amount to add.  Invalid if not initialized.
- */
-static struct TALER_Amount add_value;
-
-/**
- * Details about the sender account in JSON format.
- */
-static json_t *sender_details;
-
-/**
- * Details about the wire transfer in JSON format.
- */
-static json_t *transfer_details;
-
-/**
- * Return value from main().
- */
-static int global_ret;
-
-
-/**
- * Run the database transaction.
- *
- * @param reserve_pub public key of the reserve to use
- * @param add_value value to add
- * @param jdetails JSON details about sender
- * @param tdetails JSON details about transfer
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on hard error,
- *         #GNUNET_NO if record exists
- */
-static int
-run_transaction (const struct TALER_ReservePublicKeyP *reserve_pub,
-                 const struct TALER_Amount *add_value,
-                 json_t *jdetails,
-                 json_t *tdetails)
-{
-  int ret;
-  struct TALER_EXCHANGEDB_Session *session;
-  void *json_str;
-  struct GNUNET_TIME_Absolute now;
-
-  session = plugin->get_session (plugin->cls);
-  if (NULL == session)
-  {
-    fprintf (stderr,
-             "Failed to initialize DB session\n");
-    return GNUNET_SYSERR;
-  }
-  /* FIXME: maybe allow passing timestamp via command-line? */
-  json_str = json_dumps (tdetails,
-                         JSON_INDENT(2));
-  if (NULL == json_str)
-  {
-    GNUNET_break (0); /* out of memory? */
-    return GNUNET_SYSERR;
-  }
-  now = GNUNET_TIME_absolute_get ();
-  (void) GNUNET_TIME_round_abs (&now);
-  ret = plugin->reserves_in_insert (plugin->cls,
-                                    session,
-                                    reserve_pub,
-                                    add_value,
-                                    now,
-                                    jdetails,
-                                    json_str,
-                                    strlen (json_str));
-  free (json_str);
-  if (GNUNET_SYSERR == ret)
-  {
-    fprintf (stderr,
-             "Failed to update reserve.\n");
-  }
-  if (GNUNET_NO == ret)
-  {
-    fprintf (stderr,
-             "Record exists, reserve not updated.\n");
-  }
-  return ret;
-}
-
-
-/**
- * Main function that will be run.
- *
- * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be 
NULL!)
- * @param cfg configuration
- */
-static void
-run (void *cls,
-     char *const *args,
-     const char *cfgfile,
-     const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (cfg,
-                                               "exchange",
-                                               "KEYDIR",
-                                               &exchange_directory))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "exchange",
-                               "KEYDIR");
-    global_ret = 1;
-    return;
-  }
-  if (NULL ==
-      (plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
-  {
-    fprintf (stderr,
-             "Failed to initialize database plugin.\n");
-    global_ret = 1;
-    return;
-  }
-  if (GNUNET_SYSERR ==
-      run_transaction (&reserve_pub,
-                       &add_value,
-                       sender_details,
-                       transfer_details))
-    global_ret = 1;
-  TALER_EXCHANGEDB_plugin_unload (plugin);
-  json_decref (transfer_details);
-  json_decref (sender_details);
-}
-
-
-/**
- * The main function of the reservemod tool
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
- */
-int
-main (int argc, char *const *argv)
-{
-  const struct GNUNET_GETOPT_CommandLineOption options[] = {
-    GNUNET_GETOPT_option_mandatory
-    (TALER_getopt_get_amount ('a',
-                              "add",
-                              "DENOM",
-                              "value to add",
-                              &add_value)),
-    GNUNET_GETOPT_option_mandatory
-    (GNUNET_JSON_getopt ('s',
-                         "sender",
-                         "JSON",
-                         "details about the sender's bank account",
-                         &sender_details)),
-    GNUNET_GETOPT_option_mandatory
-    (GNUNET_JSON_getopt ('t',
-                         "transfer",
-                         "JSON",
-                         "details that uniquely identify the bank transfer",
-                         &transfer_details)),
-    GNUNET_GETOPT_option_help ("Deposit funds into a Taler reserve"),
-    GNUNET_GETOPT_option_mandatory
-    (GNUNET_GETOPT_option_base32_auto ('R',
-                                       "reserve",
-                                       "KEY",
-                                       "reserve (public key) to modify",
-                                       &reserve_pub)),
-    GNUNET_GETOPT_OPTION_END
-  };
-
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_log_setup ("taler-exchange-reservemod",
-                                   "WARNING",
-                                   NULL));
-  if (GNUNET_OK !=
-      GNUNET_PROGRAM_run (argc, argv,
-                          "taler-exchange-reservemod",
-                         "Deposit funds into a Taler reserve",
-                         options,
-                         &run, NULL))
-    return 1;
-  return global_ret;
-}
-
-/* end taler-exchange-reservemod.c */
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 9ff98d1..b5419a7 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -43,7 +43,6 @@ taler_exchange_wirewatch_LDADD = \
 
 taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd.c taler-exchange-httpd.h \
-  taler-exchange-httpd_admin.c taler-exchange-httpd_admin.h \
   taler-exchange-httpd_db.c taler-exchange-httpd_db.h \
   taler-exchange-httpd_deposit.c taler-exchange-httpd_deposit.h \
   taler-exchange-httpd_keystate.c taler-exchange-httpd_keystate.h \
diff --git a/src/exchange/exchange.conf b/src/exchange/exchange.conf
index 4c10b31..39c496e 100644
--- a/src/exchange/exchange.conf
+++ b/src/exchange/exchange.conf
@@ -36,18 +36,3 @@ PORT = 8081
 # Required for wire transfers as we need to include it in the wire
 # transfers to enable tracking.
 BASE_URL = http://localhost:8081/
-
-
-[exchange-admin]
-# Network configuration for the /admin HTTP server
-
-# serve via tcp socket (on PORT)
-SERVE = tcp
-
-# Unix domain socket to listen on,
-# only effective with "SERVE = unix"
-UNIXPATH = ${TALER_RUNTIME_DIR}/exchange-admin.http
-UNIXPATH_MODE = 660
-
-# HTTP port the exchange listens to
-PORT = 18080
diff --git a/src/exchange/taler-config-generate 
b/src/exchange/taler-config-generate
index 8af0828..248faa0 100755
--- a/src/exchange/taler-config-generate
+++ b/src/exchange/taler-config-generate
@@ -12,7 +12,7 @@
 # -m, --merchant (generate configuration for the merchant)
 # -t, --trusted (generate configuration for exchange and merchant, with 
exchange set as trusted with merchant)
 # -w WIREFORMAT, --wire=WIREFORMAT (which wire plugin should we use)
-# --bank-uri=URI (only for WIREFORMAT='test')
+# --bank-url=URL (only for WIREFORMAT='test')
 # --exchange-bank-account=NUMBER (only for WIREFORMAT='test')
 # --merchant-bank-account=NUMBER (only for WIREFORMAT='test')
 
@@ -27,13 +27,13 @@ ARG_JM=
 ARG_M=0
 ARG_T=0
 ARG_W=test
-ARG_BANK_URI=
+ARG_BANK_URL=
 ARG_EXCHANGE_BANK_ACCOUNT=
 ARG_MERCHANT_BANK_ACCOUNT=
 
 ##################################
 # read the options
-TEMP=`getopt -o c:C:ef:hj:J:mtw: --long 
config:,currency:,exchange,wirefee:,help,wire-json-exchange:,wire-json-merchant:,merchant,trusted,wire:,bank-uri:,exchange-bank-account:,merchant-bank-account:
 -n 'taler-config-generate' -- "$@"`
+TEMP=`getopt -o c:C:ef:hj:J:mtw: --long 
config:,currency:,exchange,wirefee:,help,wire-json-exchange:,wire-json-merchant:,merchant,trusted,wire:,bank-url:,exchange-bank-account:,merchant-bank-account:
 -n 'taler-config-generate' -- "$@"`
 eval set -- "$TEMP"
 
 ####################################################
@@ -70,8 +70,8 @@ while true ; do
         -w|--wire)
             ARG_W="$2"
             shift 2 ;;
-        --bank-uri)
-            ARG_BANK_URI="$2"
+        --bank-url)
+            ARG_BANK_URL="$2"
             shift 2 ;;
         --exchange-bank-account)
             ARG_EXCHANGE_BANK_ACCOUNT="$2"
@@ -111,19 +111,19 @@ fi
 # Assemble JSON description of wireformat for "test" if we can
 if (test "test" = "$ARG_W")
 then
-  if (test ! -z "$ARG_BANK_URI" -a ! -z "$ARG_MERCHANT_BANK_ACCOUNT")
+  if (test ! -z "$ARG_BANK_URL" -a ! -z "$ARG_MERCHANT_BANK_ACCOUNT")
   then
-    
ARG_JM="{\"type\":\"test\",\"bank_uri\":\"$ARG_BANK_URI\",\"account_number\":$ARG_MERCHANT_BANK_ACCOUNT}"
+    
ARG_JM="{\"type\":\"test\",\"bank_url\":\"$ARG_BANK_URL\",\"account_number\":$ARG_MERCHANT_BANK_ACCOUNT}"
 #    echo "Account detail: $ARG_JM"
   else
-    echo "Bank URI or account not given, skipping JSON generation for merchant"
+    echo "Bank URL or account not given, skipping JSON generation for merchant"
   fi
-  if (test ! -z "$ARG_BANK_URI" -a ! -z "$ARG_EXCHANGE_BANK_ACCOUNT")
+  if (test ! -z "$ARG_BANK_URL" -a ! -z "$ARG_EXCHANGE_BANK_ACCOUNT")
   then
-    
ARG_JE="{\"type\":\"test\",\"bank_uri\":\"$ARG_BANK_URI\",\"account_number\":$ARG_EXCHANGE_BANK_ACCOUNT}"
+    
ARG_JE="{\"type\":\"test\",\"bank_url\":\"$ARG_BANK_URL\",\"account_number\":$ARG_EXCHANGE_BANK_ACCOUNT}"
 #    echo "Account detail: $ARG_JE"
   else
-    echo "Bank URI or account not given, skipping JSON generation for exchange"
+    echo "Bank URL or account not given, skipping JSON generation for exchange"
   fi
 else
   echo "Wire format is not 'test', not auto-generating JSON"
@@ -185,9 +185,9 @@ then
     $CS -s exchange-wire-$WMETHOD -o ENABLE -V YES || exit 1
 
 # If possible, initialize outgoing wire account details ('test' method only)
-    if (test "test" = "$WMETHOD" -a ! -z "$ARG_BANK_URI")
+    if (test "test" = "$WMETHOD" -a ! -z "$ARG_BANK_URL")
     then
-      $CS -s exchange-wire-test -o BANK_URI -V "$ARG_BANK_URI" || exit 1
+      $CS -s exchange-wire-test -o BANK_URL -V "$ARG_BANK_URL" || exit 1
     else
       echo "Skipped generating wire account details for exchange"
     fi
@@ -236,7 +236,7 @@ then
   if (test 1 = "$ARG_E")
   then
     EPORT=`$CS -s exchange -o PORT`
-    $CS -s merchant-exchange-test -o URI -V "http://localhost:$EPORT/"; || exit
+    $CS -s merchant-exchange-test -o URL -V "http://localhost:$EPORT/"; || exit
     $CS -s merchant-exchange-test -o MASTER_KEY -V `$CS -s exchange -o 
MASTER_PUBLIC_KEY`
   else
     echo "Need to be configuring exchange as well for -t to be useful."
diff --git a/src/exchange/taler-exchange-aggregator.c 
b/src/exchange/taler-exchange-aggregator.c
index 3de5630..d5d4305 100644
--- a/src/exchange/taler-exchange-aggregator.c
+++ b/src/exchange/taler-exchange-aggregator.c
@@ -124,6 +124,11 @@ struct AggregationUnit
   struct GNUNET_HashCode h_wire;
 
   /**
+   * Hash code of contract we are currently looking into.
+   */
+  const struct GNUNET_HashCode *h_contract;
+
+  /**
    * Wire transfer identifier we use.
    */
   struct TALER_WireTransferIdentifierRawP wtid;
@@ -374,6 +379,7 @@ update_fees (struct WirePlugin *wp,
                                     p->start_date,
                                     p->end_date,
                                     &p->wire_fee,
+                                    &p->closing_fee,
                                     &p->master_sig);
     if (qs < 0)
     {
@@ -568,6 +574,53 @@ exchange_serve_process_config ()
 
 
 /**
+ * Callback invoked with information about refunds applicable
+ * to a particular coin.  Subtract refunded amount(s) from
+ * the aggregation unit's total amount.
+ *
+ * @param cls closure with a `struct AggregationUnit *`
+ * @param merchant_pub public key of merchant who authorized refund
+ * @param merchant_sig signature of merchant authorizing refund
+ * @param h_contract hash of contract being refunded
+ * @param rtransaction_id refund transaction ID
+ * @param amount_with_fee amount being refunded
+ * @param refund_fee fee the exchange keeps for the refund processing
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ */
+static int
+refund_by_coin_cb (void *cls,
+                  const struct TALER_MerchantPublicKeyP *merchant_pub,
+                  const struct TALER_MerchantSignatureP *merchant_sig,
+                  const struct GNUNET_HashCode *h_contract,
+                  uint64_t rtransaction_id,
+                  const struct TALER_Amount *amount_with_fee,
+                  const struct TALER_Amount *refund_fee)
+{
+  struct AggregationUnit *au = cls;
+
+  /* TODO: potential optimization: include these conditions
+     in the SELECT! */
+  if (0 != memcmp (merchant_pub,
+                  &au->merchant_pub,
+                  sizeof (struct TALER_MerchantPublicKeyP)))
+    return GNUNET_OK; /* different merchant */
+  if (0 != memcmp (h_contract,
+                  au->h_contract,
+                  sizeof (struct GNUNET_HashCode)))
+    return GNUNET_OK; /* different contract */
+  if (GNUNET_OK !=
+      TALER_amount_subtract (&au->total_amount,
+                            &au->total_amount,
+                            amount_with_fee))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+                                               
+
+/**
  * Function called with details about deposits that have been made,
  * with the goal of executing the corresponding wire transaction.
  *
@@ -609,6 +662,20 @@ deposit_cb (void *cls,
     return GNUNET_DB_STATUS_HARD_ERROR;
   }
   au->row_id = row_id;
+
+  au->h_contract = h_contract_terms;
+  qs = db_plugin->select_refunds_by_coin (db_plugin->cls,
+                                         au->session,
+                                         coin_pub,
+                                         &refund_by_coin_cb,
+                                         au);
+  au->h_contract = NULL;
+  if (0 > qs)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
+  }
+  
   GNUNET_assert (NULL == au->wire);
   au->wire = json_incref ((json_t *) wire);
   if (GNUNET_OK !=
@@ -730,6 +797,20 @@ aggregate_cb (void *cls,
     /* Skip this one, but keep going! */
     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   }
+
+  au->h_contract = h_contract_terms;
+  qs = db_plugin->select_refunds_by_coin (db_plugin->cls,
+                                         au->session,
+                                         coin_pub,
+                                         &refund_by_coin_cb,
+                                         au);
+  au->h_contract = NULL;
+  if (0 > qs)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
+  }
+  
   if (au->rows_offset >= aggregation_limit)
   {
     /* Bug: we asked for at most #aggregation_limit results! */
diff --git a/src/exchange/taler-exchange-httpd.c 
b/src/exchange/taler-exchange-httpd.c
index 0dd3f96..823f124 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -28,7 +28,6 @@
 #include <pthread.h>
 #include "taler-exchange-httpd_parsing.h"
 #include "taler-exchange-httpd_mhd.h"
-#include "taler-exchange-httpd_admin.h"
 #include "taler-exchange-httpd_deposit.h"
 #include "taler-exchange-httpd_refund.h"
 #include "taler-exchange-httpd_reserve_status.h"
@@ -96,16 +95,6 @@ static unsigned int connection_timeout = 30;
 static struct MHD_Daemon *mhd;
 
 /**
- * The HTTP Daemon for /admin-requests.
- */
-static struct MHD_Daemon *mhd_admin;
-
-/**
- * Do not offer /admin API.
- */
-static int no_admin;
-
-/**
  * Initialize the database by creating tables and indices.
  */
 static int init_db;
@@ -116,32 +105,16 @@ static int init_db;
 static uint16_t serve_port;
 
 /**
- * Port to run the admin daemon on.
- */
-static uint16_t serve_admin_port;
-
-/**
  * Path for the unix domain-socket
  * to run the daemon on.
  */
 static char *serve_unixpath;
 
 /**
- * Path for the unix domain-socket
- * to run the admin daemon on.
- */
-static char *serve_admin_unixpath;
-
-/**
  * File mode for unix-domain socket.
  */
 static mode_t unixpath_mode;
 
-/**
- * File mode for unix-domain socket.
- */
-static mode_t unixpath_admin_mode;
-
 
 /**
  * Function called whenever MHD is done with a request.  If the
@@ -307,76 +280,6 @@ handle_mhd_request (void *cls,
         "Only GET is allowed", 0,
         &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
 
-      { NULL, NULL, NULL, NULL, 0, 0 }
-    };
-  static struct TEH_RequestHandler h404 =
-    {
-      "", NULL, "text/html",
-      "<html><title>404: not found</title></html>", 0,
-      &TEH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
-    };
-  struct TEH_RequestHandler *rh;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Handling request for URL '%s'\n",
-              url);
-  if (0 == strcasecmp (method,
-                       MHD_HTTP_METHOD_HEAD))
-    method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the 
rest */
-  for (unsigned int i=0;NULL != handlers[i].url;i++)
-  {
-    rh = &handlers[i];
-    if ( (0 == strcasecmp (url,
-                           rh->url)) &&
-         ( (NULL == rh->method) ||
-           (0 == strcasecmp (method,
-                             rh->method)) ) )
-      return rh->handler (rh,
-                          connection,
-                          con_cls,
-                          upload_data,
-                          upload_data_size);
-  }
-  return TEH_MHD_handler_static_response (&h404,
-                                          connection,
-                                          con_cls,
-                                          upload_data,
-                                          upload_data_size);
-}
-
-
-/**
- * Handle incoming administrative HTTP request.
- *
- * @param cls closure for MHD daemon (unused)
- * @param connection the connection
- * @param url the requested url
- * @param method the method (POST, GET, ...)
- * @param version HTTP version (ignored)
- * @param upload_data request data
- * @param upload_data_size size of @a upload_data in bytes
- * @param con_cls closure for request (a `struct Buffer *`)
- * @return MHD result code
- */
-static int
-handle_mhd_admin_request (void *cls,
-                          struct MHD_Connection *connection,
-                          const char *url,
-                          const char *method,
-                          const char *version,
-                          const char *upload_data,
-                          size_t *upload_data_size,
-                          void **con_cls)
-{
-  static struct TEH_RequestHandler handlers[] =
-    {
-      { "/admin/add/incoming", MHD_HTTP_METHOD_POST, "application/json",
-        NULL, 0,
-        &TEH_ADMIN_handler_admin_add_incoming, MHD_HTTP_OK },
-      { "/admin/add/incoming", NULL, "text/plain",
-        "Only POST is allowed", 0,
-        &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
 #if HAVE_DEVELOPER
       /* Client crypto-interoperability test functions */
       { "/test", MHD_HTTP_METHOD_POST, "application/json",
@@ -452,12 +355,14 @@ handle_mhd_admin_request (void *cls,
       &TEH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
     };
   struct TEH_RequestHandler *rh;
-  unsigned int i;
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Handling request for URL '%s'\n",
               url);
-  for (i=0;NULL != handlers[i].url;i++)
+  if (0 == strcasecmp (method,
+                       MHD_HTTP_METHOD_HEAD))
+    method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the 
rest */
+  for (unsigned int i=0;NULL != handlers[i].url;i++)
   {
     rh = &handlers[i];
     if ( (0 == strcasecmp (url,
@@ -691,15 +596,6 @@ exchange_serve_process_config ()
     TEH_VALIDATION_done ();
     return GNUNET_SYSERR;
   }
-  if (GNUNET_OK !=
-      parse_port_config ("exchange-admin",
-                         &serve_admin_port,
-                         &serve_admin_unixpath,
-                         &unixpath_admin_mode))
-  {
-    TEH_VALIDATION_done ();
-    return GNUNET_SYSERR;
-  }
   return GNUNET_OK;
 }
 
@@ -832,7 +728,7 @@ handle_mhd_logs (void *cls,
              sizeof (buf),
              fm,
              ap);
-  GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
+  GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
                    "libmicrohttpd",
                    "%s",
                    buf);
@@ -948,23 +844,19 @@ main (int argc,
   char *logfile = NULL;
   const struct GNUNET_GETOPT_CommandLineOption options[] = {
     GNUNET_GETOPT_option_flag ('C',
-                                  "connection-close",
-                                  "force HTTP connections to be closed after 
each request",
-                                  &TEH_exchange_connection_close),
+                               "connection-close",
+                               "force HTTP connections to be closed after each 
request",
+                               &TEH_exchange_connection_close),
     GNUNET_GETOPT_option_cfgfile (&cfgfile),
-    GNUNET_GETOPT_option_flag ('D',
-                                  "disable-admin",
-                                  "do not run the /admin-HTTP server",
-                                  &no_admin),
     GNUNET_GETOPT_option_flag ('i',
-                                  "init-db",
-                                  "create database tables and indicies if 
necessary",
-                                  &init_db),
+                               "init-db",
+                               "create database tables and indicies if 
necessary",
+                               &init_db),
    GNUNET_GETOPT_option_uint ('t',
-                                  "timeout",
-                                  "SECONDS",
-                                  "after how long do connections timeout by 
default (in seconds)",
-                                  &connection_timeout),
+                              "timeout",
+                              "SECONDS",
+                              "after how long do connections timeout by 
default (in seconds)",
+                              &connection_timeout),
 #if HAVE_DEVELOPER
    GNUNET_GETOPT_option_filename ('f',
                                   "file-input",
@@ -982,7 +874,6 @@ main (int argc,
   const char *listen_pid;
   const char *listen_fds;
   int fh = -1;
-  int fh_admin = -1;
 
   if (0 >=
       GNUNET_GETOPT_run ("taler-exchange-httpd",
@@ -1018,12 +909,9 @@ main (int argc,
        (getpid() == strtol (listen_pid,
                             NULL,
                             10)) &&
-       ( (1 == strtoul (listen_fds,
-                        NULL,
-                        10)) ||
-         (2 == strtoul (listen_fds,
-                        NULL,
-                        10)) ) )
+       (1 == strtoul (listen_fds,
+                      NULL,
+                      10)) )
   {
     int flags;
 
@@ -1044,29 +932,6 @@ main (int argc,
                       flags)) )
       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
                            "fcntl");
-
-    if (2 == strtoul (listen_fds,
-                      NULL,
-                      10))
-    {
-      fh_admin = 4;
-      flags = fcntl (fh_admin,
-                     F_GETFD);
-      if ( (-1 == flags) &&
-           (EBADF == errno) )
-      {
-        fprintf (stderr,
-                 "Bad listen socket passed, ignored\n");
-        fh_admin = -1;
-      }
-      flags |= FD_CLOEXEC;
-      if ( (-1 != fh_admin) &&
-           (0 != fcntl (fh_admin,
-                        F_SETFD,
-                        flags)) )
-        GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
-                             "fcntl");
-    }
   }
 
   /* consider unix path */
@@ -1078,22 +943,9 @@ main (int argc,
     if (-1 == fh)
       return 1;
   }
-  if ( (-1 == fh_admin) &&
-       (0 == no_admin) &&
-       (NULL != serve_admin_unixpath) )
-  {
-    fh_admin = open_unix_path (serve_admin_unixpath,
-                               unixpath_admin_mode);
-    if (-1 == fh_admin)
-    {
-      if (-1 != fh)
-        GNUNET_break (0 == close (fh));
-      return 1;
-    }
-  }
 
   mhd
-    = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN 
| MHD_USE_DEBUG,
+    = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN 
| MHD_USE_DEBUG | MHD_USE_DUAL_STACK,
                         (-1 == fh) ? serve_port : 0,
                         NULL, NULL,
                         &handle_mhd_request, NULL,
@@ -1112,30 +964,6 @@ main (int argc,
     return 1;
   }
 
-  if (0 == no_admin)
-  {
-    mhd_admin
-      = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | 
MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG,
-                          (-1 == fh) ? serve_admin_port : 0,
-                          NULL, NULL,
-                          &handle_mhd_admin_request, NULL,
-                          MHD_OPTION_LISTEN_SOCKET, fh_admin,
-                          MHD_OPTION_EXTERNAL_LOGGER, &handle_mhd_logs, NULL,
-                          MHD_OPTION_NOTIFY_COMPLETED, 
&handle_mhd_completion_callback, NULL,
-                          MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout,
-#if HAVE_DEVELOPER
-                          MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL,
-#endif
-                          MHD_OPTION_END);
-    if (NULL == mhd_admin)
-    {
-      fprintf (stderr,
-               "Failed to start administrative HTTP server.\n");
-      MHD_stop_daemon (mhd);
-      return 1;
-    }
-  }
-
 #if HAVE_DEVELOPER
   if (NULL != input_filename)
   {
@@ -1157,23 +985,14 @@ main (int argc,
   case GNUNET_OK:
   case GNUNET_SYSERR:
     MHD_stop_daemon (mhd);
-    if (NULL != mhd_admin)
-      MHD_stop_daemon (mhd_admin);
     break;
   case GNUNET_NO:
     {
       MHD_socket sock = MHD_quiesce_daemon (mhd);
-      MHD_socket admin_sock;
-      int admin_sock_opened = GNUNET_NO;
       pid_t chld;
       int flags;
 
       /* Set flags to make 'sock' inherited by child */
-      if (NULL != mhd_admin)
-      {
-        admin_sock = MHD_quiesce_daemon (mhd_admin);
-        admin_sock_opened = GNUNET_YES;
-      }
       flags = fcntl (sock, F_GETFD);
       GNUNET_assert (-1 != flags);
       flags &= ~FD_CLOEXEC;
@@ -1197,20 +1016,13 @@ main (int argc,
                                "dup2");
           _exit (1);
         }
-        if ( (GNUNET_YES == admin_sock_opened) &&
-             (4 != dup2 (admin_sock, 4)) )
-        {
-          GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
-                               "dup2");
-          _exit (1);
-        }
         /* Tell the child that it is the desired recipient for FD #3 */
         GNUNET_snprintf (pids,
                          sizeof (pids),
                          "%u",
                          getpid ());
         setenv ("LISTEN_PID", pids, 1);
-        setenv ("LISTEN_FDS", (NULL != mhd_admin) ? "2" : "1", 1);
+        setenv ("LISTEN_FDS", "1", 1);
         /* Finally, exec the (presumably) more recent exchange binary */
         execvp ("taler-exchange-httpd",
                 argv);
@@ -1222,25 +1034,16 @@ main (int argc,
          before exiting; as the listen socket is no longer used,
          close it here */
       GNUNET_break (0 == close (sock));
-      if (GNUNET_YES == admin_sock_opened)
-        GNUNET_break (0 == close (admin_sock));
-      while ( (0 != MHD_get_daemon_info (mhd,
-                                         
MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections) ||
-              ( (NULL != mhd_admin) &&
-                (0 != MHD_get_daemon_info (mhd_admin,
-                                           
MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections) ) )
+      while (0 != MHD_get_daemon_info (mhd,
+                                       
MHD_DAEMON_INFO_CURRENT_CONNECTIONS)->num_connections)
         sleep (1);
       /* Now we're really done, practice clean shutdown */
       MHD_stop_daemon (mhd);
-      if (NULL != mhd_admin)
-        MHD_stop_daemon (mhd_admin);
     }
     break;
   default:
     GNUNET_break (0);
     MHD_stop_daemon (mhd);
-    if (NULL != mhd_admin)
-      MHD_stop_daemon (mhd_admin);
     break;
   }
   TALER_EXCHANGEDB_plugin_unload (TEH_plugin);
diff --git a/src/exchange/taler-exchange-httpd_admin.c 
b/src/exchange/taler-exchange-httpd_admin.c
deleted file mode 100644
index f65b9c3..0000000
--- a/src/exchange/taler-exchange-httpd_admin.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014, 2015 GNUnet e.V.
-
-  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
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
-
-  You should have received a copy of the GNU Affero General Public License 
along with
-  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_admin.c
- * @brief Handle /admin/ requests
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <jansson.h>
-#include "taler-exchange-httpd_admin.h"
-#include "taler-exchange-httpd_parsing.h"
-#include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_validation.h"
-
-
-/**
- * Closure for #admin_add_incoming_transaction()
- */
-struct AddIncomingContext
-{
-  /**
-   * public key of the reserve
-   */
-  struct TALER_ReservePublicKeyP reserve_pub;
-
-  /**
-   * amount to add to the reserve
-   */
-  struct TALER_Amount amount;
-
-  /**
-   * When did we receive the wire transfer
-   */
-  struct GNUNET_TIME_Absolute execution_time;
-
-  /**
-   * which account send the funds
-   */
-  json_t *sender_account_details;
-
-  /**
-   * Information that uniquely identifies the transfer
-   */
-  json_t *transfer_details;
-
-  /**
-   * Set to the transaction status.
-   */
-  enum GNUNET_DB_QueryStatus qs;
-};
-
-
-/**
- * Add an incoming transaction to the database.  Checks if the
- * transaction is fresh (not a duplicate) and if so adds it to the
- * database.
- *
- * If it returns a non-error code, the transaction logic MUST
- * NOT queue a MHD response.  IF it returns an hard error, the
- * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF
- * it returns the soft error code, the function MAY be called again to
- * retry and MUST not queue a MHD response.
- *
- * @param cls closure with the `struct AddIncomingContext *`
- * @param connection MHD request which triggered the transaction
- * @param session database session to use
- * @param[out] mhd_ret set to MHD response status for @a connection,
- *             if transaction failed (!)
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-admin_add_incoming_transaction (void *cls,
-                               struct MHD_Connection *connection,
-                               struct TALER_EXCHANGEDB_Session *session,
-                               int *mhd_ret)
-{
-  struct AddIncomingContext *aic = cls;
-  void *json_str;
-
-  json_str = json_dumps (aic->transfer_details,
-                         JSON_INDENT(2));
-  if (NULL == json_str)
-  {
-    GNUNET_break (0);
-    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
-                                                    
TALER_EC_PARSER_OUT_OF_MEMORY);
-    return GNUNET_DB_STATUS_HARD_ERROR;
-  }
-  aic->qs = TEH_plugin->reserves_in_insert (TEH_plugin->cls,
-                                           session,
-                                           &aic->reserve_pub,
-                                           &aic->amount,
-                                           aic->execution_time,
-                                           aic->sender_account_details,
-                                           json_str,
-                                           strlen (json_str));
-  free (json_str);
-
-  if (GNUNET_DB_STATUS_HARD_ERROR == aic->qs)
-  {
-    GNUNET_break (0);
-    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
-                                                    
TALER_EC_ADMIN_ADD_INCOMING_DB_STORE);
-    return GNUNET_DB_STATUS_HARD_ERROR;
-  }
-  return aic->qs;
-}
-
-
-/**
- * Handle a "/admin/add/incoming" request.  Parses the
- * given "reserve_pub", "amount", "transaction" and "h_wire"
- * details and adds the respective transaction to the database.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
-  */
-int
-TEH_ADMIN_handler_admin_add_incoming (struct TEH_RequestHandler *rh,
-                                      struct MHD_Connection *connection,
-                                      void **connection_cls,
-                                      const char *upload_data,
-                                      size_t *upload_data_size)
-{
-  struct AddIncomingContext aic;
-  enum TALER_ErrorCode ec;
-  char *emsg;
-  json_t *root;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_fixed_auto ("reserve_pub", &aic.reserve_pub),
-    TALER_JSON_spec_amount ("amount", &aic.amount),
-    GNUNET_JSON_spec_absolute_time ("execution_date", &aic.execution_time),
-    GNUNET_JSON_spec_json ("sender_account_details", 
&aic.sender_account_details),
-    GNUNET_JSON_spec_json ("transfer_details", &aic.transfer_details),
-    GNUNET_JSON_spec_end ()
-  };
-  int res;
-  int mhd_ret;
-
-  res = TEH_PARSE_post_json (connection,
-                             connection_cls,
-                             upload_data,
-                             upload_data_size,
-                             &root);
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  if ( (GNUNET_NO == res) ||
-       (NULL == root) )
-    return MHD_YES;
-  res = TEH_PARSE_json_data (connection,
-                             root,
-                             spec);
-  json_decref (root);
-  if (GNUNET_OK != res)
-  {
-    GNUNET_break_op (0);
-    json_decref (root);
-    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-  }
-  if (TALER_EC_NONE !=
-      (ec = TEH_json_validate_wireformat (aic.sender_account_details,
-                                          GNUNET_NO,
-                                          &emsg)))
-  {
-    GNUNET_JSON_parse_free (spec);
-    mhd_ret = TEH_RESPONSE_reply_external_error (connection,
-                                                ec,
-                                                emsg);
-    GNUNET_free (emsg);
-    return mhd_ret;
-  }
-  if (0 != strcasecmp (aic.amount.currency,
-                       TEH_exchange_currency_string))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Exchange uses currency `%s', but /admin/add/incoming tried to 
use currency `%s'\n",
-                TEH_exchange_currency_string,
-                aic.amount.currency);
-    GNUNET_JSON_parse_free (spec);
-    return TEH_RESPONSE_reply_arg_invalid (connection,
-                                          
TALER_EC_ADMIN_ADD_INCOMING_CURRENCY_UNSUPPORTED,
-                                           "amount:currency");
-  }
-  res = TEH_DB_run_transaction (connection,
-                               &mhd_ret,
-                               &admin_add_incoming_transaction,
-                               &aic);
-  GNUNET_JSON_parse_free (spec);
-  if (GNUNET_OK != res)
-    return mhd_ret;
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:s}",
-                                       "status",
-                                       (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 
aic.qs)
-                                       ? "NEW"
-                                       : "DUP");
-}
-
-/* end of taler-exchange-httpd_admin.c */
diff --git a/src/exchange/taler-exchange-httpd_admin.h 
b/src/exchange/taler-exchange-httpd_admin.h
deleted file mode 100644
index 8ed1e3d..0000000
--- a/src/exchange/taler-exchange-httpd_admin.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014 GNUnet e.V.
-
-  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
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
-
-  You should have received a copy of the GNU Affero General Public License 
along with
-  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_admin.h
- * @brief Handle /admin/ requests
- * @author Christian Grothoff
- */
-#ifndef TALER_EXCHANGE_HTTPD_ADMIN_H
-#define TALER_EXCHANGE_HTTPD_ADMIN_H
-
-#include <microhttpd.h>
-#include "taler-exchange-httpd.h"
-
-/**
- * Handle a "/admin/add/incoming" request.  Parses the
- * given "reserve_pub", "amount", "transaction" and "h_wire"
- * details and adds the respective transaction to the database.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
-  */
-int
-TEH_ADMIN_handler_admin_add_incoming (struct TEH_RequestHandler *rh,
-                                      struct MHD_Connection *connection,
-                                      void **connection_cls,
-                                      const char *upload_data,
-                                      size_t *upload_data_size);
-
-#endif
diff --git a/src/exchange/taler-exchange-httpd_deposit.c 
b/src/exchange/taler-exchange-httpd_deposit.c
index b7fb345..542c56c 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -149,6 +149,8 @@ deposit_transaction (void *cls,
   {
     struct TALER_Amount amount_without_fee;
 
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+               "/deposit replay, accepting again!\n");
     GNUNET_assert (GNUNET_OK ==
                    TALER_amount_subtract (&amount_without_fee,
                                           &deposit->amount_with_fee,
@@ -191,6 +193,8 @@ deposit_transaction (void *cls,
   if (0 < TALER_amount_cmp (&spent,
                             &dc->value))
   {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               "Deposited coin has insufficient funds left!\n");
     *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
                                                           
TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS,
                                                           tl);
@@ -376,7 +380,7 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
   struct GNUNET_HashCode my_h_wire;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_json ("wire", &wire),
-    TALER_JSON_spec_amount ("f", &deposit.amount_with_fee),
+    TALER_JSON_spec_amount ("contribution", &deposit.amount_with_fee),
     TALER_JSON_spec_denomination_public_key ("denom_pub", 
&deposit.coin.denom_pub),
     TALER_JSON_spec_denomination_signature ("ub_sig", &deposit.coin.denom_sig),
     GNUNET_JSON_spec_fixed_auto ("coin_pub", &deposit.coin.coin_pub),
diff --git a/src/exchange/taler-exchange-httpd_keystate.c 
b/src/exchange/taler-exchange-httpd_keystate.c
index 801d6fe..f57451a 100644
--- a/src/exchange/taler-exchange-httpd_keystate.c
+++ b/src/exchange/taler-exchange-httpd_keystate.c
@@ -1183,6 +1183,7 @@ build_keys_response (const struct ResponseFactoryContext 
*rfc,
   struct TALER_ExchangeKeySetPS ks;
   struct TALER_ExchangeSignatureP sig;
   char *keys_json;
+  struct GNUNET_TIME_Relative reserve_closing_delay;
   void *keys_jsonz;
   size_t keys_jsonz_size;
   int comp;
@@ -1287,13 +1288,29 @@ build_keys_response (const struct 
ResponseFactoryContext *rfc,
                  GNUNET_CRYPTO_eddsa_sign 
(&rfc->key_state->current_sign_key_issue.signkey_priv.eddsa_priv,
                                            &ks.purpose,
                                            &sig.eddsa_signature));
-
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_time (cfg,
+                                           "exchangedb",
+                                           "IDLE_RESERVE_EXPIRATION_TIME",
+                                           &reserve_closing_delay))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "exchangedb",
+                               "IDLE_RESERVE_EXPIRATION_TIME");
+    /* use default */
+    reserve_closing_delay = GNUNET_TIME_relative_multiply 
(GNUNET_TIME_UNIT_WEEKS,
+                                                           4);
+  }
   /* Build /keys response */
-  keys = json_pack ("{s:s, s:o, s:O, s:O, s:o, s:o, s:o, s:o, s:o}",
+  keys = json_pack ("{s:s, s:o, s:o, s:O, s:O,"
+                    " s:o, s:o, s:o, s:o, s:o}",
+                    /* 1-5 */
                     "version", TALER_PROTOCOL_VERSION,
                     "master_public_key", GNUNET_JSON_from_data_auto 
(&TEH_master_public_key),
+                    "reserve_closing_delay", GNUNET_JSON_from_time_rel 
(reserve_closing_delay),
                     "signkeys", rfc->sign_keys_array,
                     "payback", rfc->payback_array,
+                    /* 6-10 */
                     "denoms", rbc.denom_keys_array,
                     "auditors", rbc.auditors_array,
                     "list_issue_date", GNUNET_JSON_from_time_abs 
(rfc->key_state->reload_time),
diff --git a/src/exchange/taler-exchange-httpd_refund.c 
b/src/exchange/taler-exchange-httpd_refund.c
index f0aaa65..986c9d3 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -285,7 +285,6 @@ refund_transaction (void *cls,
   }
 
   /* check if we already send the money for the /deposit */
-  // FIXME: DB API...
   qs = TEH_plugin->test_deposit_done (TEH_plugin->cls,
                                      session,
                                      dep);
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index 8965e2a..c7874ed 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -711,7 +711,9 @@ TEH_RESPONSE_compile_reserve_history (const struct 
TALER_EXCHANGEDB_ReserveHisto
 
   json_history = json_array ();
   ret = 0;
-  for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh; NULL != pos; 
pos = pos->next)
+  for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh;
+       NULL != pos;
+       pos = pos->next)
   {
     switch (pos->type)
     {
@@ -731,8 +733,9 @@ TEH_RESPONSE_compile_reserve_history (const struct 
TALER_EXCHANGEDB_ReserveHisto
       ret |= 1;
       GNUNET_assert (0 ==
                      json_array_append_new (json_history,
-                                            json_pack ("{s:s, s:O, s:o, s:o}",
+                                            json_pack ("{s:s, s:o, s:O, s:o, 
s:o}",
                                                        "type", "DEPOSIT",
+                                                       "timestamp", 
GNUNET_JSON_from_time_abs (pos->details.bank->execution_date),
                                                        
"sender_account_details", pos->details.bank->sender_account_details,
                                                        "wire_reference", 
GNUNET_JSON_from_data (pos->details.bank->wire_reference,
                                                                                
                 pos->details.bank->wire_reference_size),
diff --git a/src/exchange/taler-exchange-httpd_track_transfer.c 
b/src/exchange/taler-exchange-httpd_track_transfer.c
index 4d28096..38c6c29 100644
--- a/src/exchange/taler-exchange-httpd_track_transfer.c
+++ b/src/exchange/taler-exchange-httpd_track_transfer.c
@@ -352,6 +352,7 @@ track_transfer_transaction (void *cls,
   struct GNUNET_TIME_Absolute wire_fee_start_date;
   struct GNUNET_TIME_Absolute wire_fee_end_date;
   struct TALER_MasterSignatureP wire_fee_master_sig;
+  struct TALER_Amount closing_fee;
 
   ctx->is_valid = GNUNET_NO;
   ctx->wdd_head = NULL;
@@ -393,6 +394,7 @@ track_transfer_transaction (void *cls,
                                 &wire_fee_start_date,
                                 &wire_fee_end_date,
                                 &ctx->wire_fee,
+                                &closing_fee,
                                 &wire_fee_master_sig);
   if (0 >= qs)
   {
diff --git a/src/exchange/taler-exchange-wirewatch.c 
b/src/exchange/taler-exchange-wirewatch.c
index e878594..e45b424 100644
--- a/src/exchange/taler-exchange-wirewatch.c
+++ b/src/exchange/taler-exchange-wirewatch.c
@@ -316,7 +316,7 @@ history_cb (void *cls,
                   "Error fetching history: %u!\n",
                   (unsigned int) ec);
     }
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "End of list. Committing progress!\n");
     qs = db_plugin->commit (db_plugin->cls,
                            session);
@@ -427,7 +427,7 @@ find_transfers (void *cls)
   enum GNUNET_DB_QueryStatus qs;
 
   task = NULL;
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Checking for incoming wire transfers\n");
 
   if (NULL == (session = db_plugin->get_session (db_plugin->cls)))
diff --git a/src/exchange/test-taler-exchange-aggregator-postgres.conf 
b/src/exchange/test-taler-exchange-aggregator-postgres.conf
index f609c0a..a5f3519 100644
--- a/src/exchange/test-taler-exchange-aggregator-postgres.conf
+++ b/src/exchange/test-taler-exchange-aggregator-postgres.conf
@@ -64,7 +64,7 @@ CLOSING-FEE-2026 = EUR:0.01
 
 
 # What is the main website of the bank?
-BANK_URI = "http://localhost:8082/";
+BANK_URL = "http://localhost:8082/";
 
 # From which account at the 'bank' should outgoing
 # wire transfers be made?
diff --git a/src/exchange/test-taler-exchange-wirewatch-postgres.conf 
b/src/exchange/test-taler-exchange-wirewatch-postgres.conf
index 2e846d8..cc614fc 100644
--- a/src/exchange/test-taler-exchange-wirewatch-postgres.conf
+++ b/src/exchange/test-taler-exchange-wirewatch-postgres.conf
@@ -66,7 +66,7 @@ CLOSING-FEE-2026 = EUR:0.01
 
 
 # What is the main website of the bank?
-BANK_URI = "http://localhost:8082/";
+BANK_URL = "http://localhost:8082/";
 
 # From which account at the 'bank' should outgoing
 # wire transfers be made?
diff --git a/src/exchange/test_taler_exchange_aggregator.c 
b/src/exchange/test_taler_exchange_aggregator.c
index 178b5aa..3265bf2 100644
--- a/src/exchange/test_taler_exchange_aggregator.c
+++ b/src/exchange/test_taler_exchange_aggregator.c
@@ -434,7 +434,7 @@ do_deposit (struct Command *cmd)
   /* Build JSON for wire details */
   deposit.receiver_wire_account = json_pack ("{s:s, s:s, s:I}",
                                              "type", "test",
-                                             "bank_uri", 
"http://localhost:8082/";,
+                                             "bank_url", 
"http://localhost:8082/";,
                                              "account_number", (json_int_t) 
cmd->details.deposit.merchant_account);
   GNUNET_assert (GNUNET_OK ==
                  TALER_JSON_hash (deposit.receiver_wire_account,
diff --git a/src/exchange/test_taler_exchange_httpd.conf 
b/src/exchange/test_taler_exchange_httpd.conf
index 2204f8d..7df8d9b 100644
--- a/src/exchange/test_taler_exchange_httpd.conf
+++ b/src/exchange/test_taler_exchange_httpd.conf
@@ -37,7 +37,7 @@ DB_CONN_STR = "postgres:///talercheck"
 ENABLE = YES
 
 # What is the main website of the bank?
-BANK_URI = "http://localhost:8082/";
+BANK_URL = "http://localhost:8082/";
 
 # From which account at the 'bank' should outgoing
 # wire transfers be made?
diff --git 
a/src/exchange/test_taler_exchange_httpd_home/.config/taler/test.json 
b/src/exchange/test_taler_exchange_httpd_home/.config/taler/test.json
index be5e92c..eca3942 100644
--- a/src/exchange/test_taler_exchange_httpd_home/.config/taler/test.json
+++ b/src/exchange/test_taler_exchange_httpd_home/.config/taler/test.json
@@ -2,7 +2,7 @@
   "salt": 
"AZPRFVJ58NM6M7J5CZQPJAH3EW5DYM52AEZ9Y1C1ER3W94QV8D8TQKF6CK8MYQRA9QMSKDQTGZ306ZS9GQ0M6R01CJ20KPP49WFDZK8",
   "name": "The exchange",
   "account_number": 3,
-  "bank_uri": "http://localhost:8082/";,
+  "bank_url": "http://localhost:8082/";,
   "type": "test",
   "sig": 
"RPQXP9S4P8PQP7HEZQNRSZCT0ATNEP8GW0P5TPM34V5RX86FCD670V44R9NETSYDDKB8SZV7TKY9PAJYTY51D3VDWY9XXQ5BPFRXR28"
-}
\ No newline at end of file
+}
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 36ae3e5..a9d5db7 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -383,6 +383,9 @@ postgres_create_tables (void *cls)
                            ",wire_fee_val INT8 NOT NULL"
                            ",wire_fee_frac INT4 NOT NULL"
                            ",wire_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") 
NOT NULL"
+                           ",closing_fee_val INT8 NOT NULL"
+                           ",closing_fee_frac INT4 NOT NULL"
+                           ",closing_fee_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
                            ",master_sig BYTEA NOT NULL CHECK 
(LENGTH(master_sig)=64)"
                            ",PRIMARY KEY (wire_method, start_date)" /* this 
combo must be unique */
                            ");"),
@@ -1170,6 +1173,9 @@ postgres_prepare (PGconn *db_conn)
                             ",wire_fee_val"
                             ",wire_fee_frac"
                             ",wire_fee_curr"
+                            ",closing_fee_val"
+                            ",closing_fee_frac"
+                            ",closing_fee_curr"
                             ",master_sig"
                             " FROM wire_fee"
                             " WHERE wire_method=$1"
@@ -1185,10 +1191,13 @@ postgres_prepare (PGconn *db_conn)
                             ",wire_fee_val"
                             ",wire_fee_frac"
                             ",wire_fee_curr"
+                            ",closing_fee_val"
+                            ",closing_fee_frac"
+                            ",closing_fee_curr"
                             ",master_sig"
                             ") VALUES "
-                            "($1, $2, $3, $4, $5, $6, $7);",
-                            7),
+                            "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);",
+                            19),
     /* Used in #postgres_store_wire_transfer_out */
     GNUNET_PQ_make_prepare ("insert_wire_out",
                             "INSERT INTO wire_out "
@@ -3033,6 +3042,128 @@ postgres_insert_refund (void *cls,
 
 
 /**
+ * Closure for #get_refunds_cb().
+ */
+struct SelectRefundContext
+{
+  /**
+   * Function to call on each result.
+   */
+  TALER_EXCHANGEDB_RefundCoinCallback cb;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Set to #GNUNET_SYSERR on error.
+   */
+  int status;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct SelectRefundContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+get_refunds_cb (void *cls,
+               PGresult *result,
+               unsigned int num_results)
+{
+  struct SelectRefundContext *srctx = cls;
+
+  for (unsigned int i=0;i<num_results;i++)
+  {
+    struct TALER_MerchantPublicKeyP merchant_pub;
+    struct TALER_MerchantSignatureP merchant_sig;
+    struct GNUNET_HashCode h_contract;
+    uint64_t rtransaction_id;
+    struct TALER_Amount amount_with_fee;
+    struct TALER_Amount refund_fee;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
+                                           &merchant_pub),
+      GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
+                                           &merchant_sig),
+      GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+                                           &h_contract),
+      GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
+                                   &rtransaction_id),
+      TALER_PQ_result_spec_amount ("amount_with_fee",
+                                  &amount_with_fee),
+      TALER_PQ_result_spec_amount ("fee_refund",
+                                  &refund_fee),
+      GNUNET_PQ_result_spec_end
+    };
+
+    if (GNUNET_OK !=
+       GNUNET_PQ_extract_result (result,
+                                 rs,
+                                 i))
+    {
+      GNUNET_break (0);
+      srctx->status = GNUNET_SYSERR;
+      return;
+    }
+    if (GNUNET_OK !=
+       srctx->cb (srctx->cb_cls,
+                  &merchant_pub,
+                  &merchant_sig,
+                  &h_contract,
+                  rtransaction_id,
+                  &amount_with_fee,
+                  &refund_fee))
+      return;
+  }
+}
+
+
+/**
+ * Select refunds by @a coin_pub.
+ *
+ * @param cls closure of plugin
+ * @param session database handle to use
+ * @param coin_pub coin to get refunds for
+ * @param cb function to call for each refund found
+ * @param cb_cls closure for @a cb
+ * @return query result status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_select_refunds_by_coin (void *cls,
+                                struct TALER_EXCHANGEDB_Session *session,
+                                const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
+                                TALER_EXCHANGEDB_RefundCoinCallback cb,
+                                void *cb_cls)
+{
+  enum GNUNET_DB_QueryStatus qs;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (coin_pub),
+    GNUNET_PQ_query_param_end
+  };
+  struct SelectRefundContext srctx = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .status = GNUNET_OK
+  };
+
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                             "get_refunds_by_coin",
+                                             params,
+                                             &get_refunds_cb,
+                                             &srctx);
+  if (GNUNET_SYSERR == srctx.status)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
+
+
+/**
  * Lookup refresh melt commitment data under the given @a rc.
  *
  * @param cls the `struct PostgresClosure` with the plugin-specific state
@@ -4234,6 +4365,7 @@ postgres_insert_aggregation_tracking (void *cls,
  * @param[out] start_date when does the fee go into effect
  * @param[out] end_date when does the fee end being valid
  * @param[out] wire_fee how high is the wire transfer fee
+ * @param[out] closing_fee how high is the closing fee
  * @param[out] master_sig signature over the above by the exchange master key
  * @return status of the transaction
  */
@@ -4245,6 +4377,7 @@ postgres_get_wire_fee (void *cls,
                        struct GNUNET_TIME_Absolute *start_date,
                        struct GNUNET_TIME_Absolute *end_date,
                        struct TALER_Amount *wire_fee,
+                      struct TALER_Amount *closing_fee,
                        struct TALER_MasterSignatureP *master_sig)
 {
   struct GNUNET_PQ_QueryParam params[] = {
@@ -4256,6 +4389,7 @@ postgres_get_wire_fee (void *cls,
     TALER_PQ_result_spec_absolute_time ("start_date", start_date),
     TALER_PQ_result_spec_absolute_time ("end_date", end_date),
     TALER_PQ_result_spec_amount ("wire_fee", wire_fee),
+    TALER_PQ_result_spec_amount ("closing_fee", closing_fee),
     GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig),
     GNUNET_PQ_result_spec_end
   };
@@ -4276,6 +4410,7 @@ postgres_get_wire_fee (void *cls,
  * @param start_date when does the fee go into effect
  * @param end_date when does the fee end being valid
  * @param wire_fee how high is the wire transfer fee
+ * @param closing_fee how high is the closing fee
  * @param master_sig signature over the above by the exchange master key
  * @return transaction status code
  */
@@ -4286,6 +4421,7 @@ postgres_insert_wire_fee (void *cls,
                           struct GNUNET_TIME_Absolute start_date,
                           struct GNUNET_TIME_Absolute end_date,
                           const struct TALER_Amount *wire_fee,
+                          const struct TALER_Amount *closing_fee,
                           const struct TALER_MasterSignatureP *master_sig)
 {
   struct GNUNET_PQ_QueryParam params[] = {
@@ -4293,10 +4429,12 @@ postgres_insert_wire_fee (void *cls,
     TALER_PQ_query_param_absolute_time (&start_date),
     TALER_PQ_query_param_absolute_time (&end_date),
     TALER_PQ_query_param_amount (wire_fee),
+    TALER_PQ_query_param_amount (closing_fee),
     GNUNET_PQ_query_param_auto_from_type (master_sig),
     GNUNET_PQ_query_param_end
   };
   struct TALER_Amount wf;
+  struct TALER_Amount cf;
   struct TALER_MasterSignatureP sig;
   struct GNUNET_TIME_Absolute sd;
   struct GNUNET_TIME_Absolute ed;
@@ -4309,6 +4447,7 @@ postgres_insert_wire_fee (void *cls,
                              &sd,
                              &ed,
                              &wf,
+                             &cf,
                              &sig);
   if (qs < 0)
     return qs;
@@ -4327,6 +4466,12 @@ postgres_insert_wire_fee (void *cls,
       GNUNET_break (0);
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
+    if (0 != TALER_amount_cmp (closing_fee,
+                               &cf))
+      {
+      GNUNET_break (0);
+      return GNUNET_DB_STATUS_HARD_ERROR;
+    }
     if ( (sd.abs_value_us != start_date.abs_value_us) ||
          (ed.abs_value_us != end_date.abs_value_us) )
     {
@@ -6070,8 +6215,8 @@ missing_wire_cb (void *cls,
     struct TALER_Amount amount;
     json_t *wire;
     struct GNUNET_TIME_Absolute deadline;
-    /* bool? */ uint32_t tiny;
-    /* bool? */ uint32_t done;
+    uint8_t tiny;
+    uint8_t done;
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
                                    &rowid),
@@ -6083,10 +6228,10 @@ missing_wire_cb (void *cls,
                                 &wire),
       TALER_PQ_result_spec_absolute_time ("wire_deadline",
                                           &deadline),
-      GNUNET_PQ_result_spec_uint32 ("tiny",
-                                   &tiny),
-      GNUNET_PQ_result_spec_uint32 ("done",
-                                   &done),
+      GNUNET_PQ_result_spec_auto_from_type ("tiny",
+                                            &tiny),
+      GNUNET_PQ_result_spec_auto_from_type ("done",
+                                            &done),
       GNUNET_PQ_result_spec_end
     };
 
@@ -6236,6 +6381,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
   plugin->iterate_matching_deposits = &postgres_iterate_matching_deposits;
   plugin->insert_deposit = &postgres_insert_deposit;
   plugin->insert_refund = &postgres_insert_refund;
+  plugin->select_refunds_by_coin = &postgres_select_refunds_by_coin;
   plugin->insert_melt = &postgres_insert_melt;
   plugin->get_melt = &postgres_get_melt;
   plugin->insert_refresh_reveal = &postgres_insert_refresh_reveal;
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index 5891a08..a0eb50f 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -1062,10 +1062,12 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session 
*session)
   struct GNUNET_TIME_Absolute start_date;
   struct GNUNET_TIME_Absolute end_date;
   struct TALER_Amount wire_fee;
+  struct TALER_Amount closing_fee;
   struct TALER_MasterSignatureP master_sig;
   struct GNUNET_TIME_Absolute sd;
   struct GNUNET_TIME_Absolute ed;
   struct TALER_Amount fee;
+  struct TALER_Amount fee2;
   struct TALER_MasterSignatureP ms;
 
   start_date = GNUNET_TIME_absolute_get ();
@@ -1075,6 +1077,9 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
   GNUNET_assert (GNUNET_OK ==
                  TALER_string_to_amount (CURRENCY ":1.424242",
                                          &wire_fee));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_string_to_amount (CURRENCY ":2.424242",
+                                         &closing_fee));
   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
                               &master_sig,
                               sizeof (master_sig));
@@ -1085,6 +1090,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
                                start_date,
                                end_date,
                                &wire_fee,
+                              &closing_fee,
                                &master_sig))
   {
     GNUNET_break (0);
@@ -1097,6 +1103,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
                                start_date,
                                end_date,
                                &wire_fee,
+                              &closing_fee,
                                &master_sig))
   {
     GNUNET_break (0);
@@ -1112,6 +1119,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
                             &sd,
                             &ed,
                             &fee,
+                           &fee2,
                             &ms))
   {
     GNUNET_break (0);
@@ -1125,6 +1133,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
                             &sd,
                             &ed,
                             &fee,
+                           &fee2,
                             &ms))
   {
     GNUNET_break (0);
@@ -1134,6 +1143,8 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
        (ed.abs_value_us != end_date.abs_value_us) ||
        (0 != TALER_amount_cmp (&fee,
                                &wire_fee)) ||
+       (0 != TALER_amount_cmp (&fee2,
+                               &closing_fee)) ||
        (0 != memcmp (&ms,
                      &master_sig,
                      sizeof (ms))) )
@@ -1394,6 +1405,72 @@ wire_missing_cb (void *cls,
 
 
 /**
+ * Callback invoked with information about refunds applicable
+ * to a particular coin.
+ *
+ * @param cls closure with the `struct TALER_EXCHANGEDB_Refund *` we expect to 
get
+ * @param merchant_pub public key of merchant who authorized refund
+ * @param merchant_sig signature of merchant authorizing refund
+ * @param h_contract hash of contract being refunded
+ * @param rtransaction_id refund transaction ID
+ * @param amount_with_fee amount being refunded
+ * @param refund_fee fee the exchange keeps for the refund processing
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ */
+static int
+check_refund_cb (void *cls,
+                const struct TALER_MerchantPublicKeyP *merchant_pub,
+                const struct TALER_MerchantSignatureP *merchant_sig,
+                const struct GNUNET_HashCode *h_contract,
+                uint64_t rtransaction_id,
+                const struct TALER_Amount *amount_with_fee,
+                const struct TALER_Amount *refund_fee)
+{
+  const struct TALER_EXCHANGEDB_Refund *refund = cls;
+
+  if (0 != memcmp (merchant_pub,
+                  &refund->merchant_pub,
+                  sizeof (struct TALER_MerchantPublicKeyP)))
+  {
+    GNUNET_break (0);
+    result = 66;
+  }
+  if (0 != memcmp (merchant_sig,
+                  &refund->merchant_sig,
+                  sizeof (struct TALER_MerchantSignatureP)))
+  {
+    GNUNET_break (0);
+    result = 66;
+  }
+  if (0 != memcmp (h_contract,
+                  &refund->h_contract_terms,
+                  sizeof (struct GNUNET_HashCode)))
+  {
+    GNUNET_break (0);
+    result = 66;
+  }
+  if (rtransaction_id != refund->rtransaction_id)
+  {
+    GNUNET_break (0);
+    result = 66;
+  }
+  if (0 != TALER_amount_cmp (amount_with_fee,
+                            &refund->refund_amount))
+  {
+    GNUNET_break (0);
+    result = 66;
+  }
+  if (0 != TALER_amount_cmp (refund_fee,
+                            &refund->refund_fee))
+  {
+    GNUNET_break (0);
+    result = 66;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
  * Main function that will be run by the scheduler.
  *
  * @param cls closure with config
@@ -1890,14 +1967,20 @@ run (void *cls)
   refund.merchant_pub = deposit.merchant_pub;
   RND_BLK (&refund.merchant_sig);
   refund.h_contract_terms = deposit.h_contract_terms;
-  refund.rtransaction_id = GNUNET_CRYPTO_random_u64 
(GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
+  refund.rtransaction_id = GNUNET_CRYPTO_random_u64 
(GNUNET_CRYPTO_QUALITY_WEAK,
+                                                    UINT64_MAX);
   refund.refund_amount = deposit.amount_with_fee;
   refund.refund_fee = fee_refund;
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_refund (plugin->cls,
                                  session,
                                  &refund));
-
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+         plugin->select_refunds_by_coin (plugin->cls,
+                                         session,
+                                         &refund.coin.coin_pub,
+                                         &check_refund_cb,
+                                         &refund));
 
   /* test payback / revocation */
   RND_BLK (&master_sig);
@@ -2127,9 +2210,11 @@ main (int argc,
                     NULL);
   plugin_name++;
   (void) GNUNET_asprintf (&testname,
-                          "test-exchange-db-%s", plugin_name);
+                          "test-exchange-db-%s",
+                         plugin_name);
   (void) GNUNET_asprintf (&config_filename,
-                          "%s.conf", testname);
+                          "%s.conf",
+                         testname);
   cfg = GNUNET_CONFIGURATION_create ();
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_parse (cfg,
@@ -2140,7 +2225,8 @@ main (int argc,
     GNUNET_free (testname);
     return 2;
   }
-  GNUNET_SCHEDULER_run (&run, cfg);
+  GNUNET_SCHEDULER_run (&run,
+                       cfg);
   GNUNET_CONFIGURATION_destroy (cfg);
   GNUNET_free (config_filename);
   GNUNET_free (testname);
diff --git a/src/exchangedb/test_exchangedb_fees.c 
b/src/exchangedb/test_exchangedb_fees.c
index 2bee774..0c9ecea 100644
--- a/src/exchangedb/test_exchangedb_fees.c
+++ b/src/exchangedb/test_exchangedb_fees.c
@@ -86,6 +86,9 @@ main (int argc,
   GNUNET_assert (GNUNET_OK ==
                  TALER_string_to_amount ("EUR:1.0",
                                          &af->wire_fee));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_string_to_amount ("EUR:1.0",
+                                         &af->closing_fee));
   sign_af (af,
            priv);
   n = GNUNET_new (struct TALER_EXCHANGEDB_AggregateFees);
@@ -94,6 +97,9 @@ main (int argc,
   GNUNET_assert (GNUNET_OK ==
                  TALER_string_to_amount ("EUR:0.1",
                                          &n->wire_fee));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_string_to_amount ("EUR:0.1",
+                                         &n->closing_fee));
   sign_af (n,
            priv);
   af->next = n;
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
index 7a8f6e0..6583ad5 100644
--- a/src/include/Makefile.am
+++ b/src/include/Makefile.am
@@ -23,6 +23,7 @@ talerinclude_HEADERS = \
   taler_exchangedb_plugin.h \
   taler_fakebank_lib.h \
   taler_json_lib.h \
+  taler_testing_lib.h \
   taler_util.h \
   taler_pq_lib.h \
   taler_signatures.h \
diff --git a/src/include/taler_bank_service.h b/src/include/taler_bank_service.h
index 6349326..bfa0503 100644
--- a/src/include/taler_bank_service.h
+++ b/src/include/taler_bank_service.h
@@ -212,7 +212,7 @@ struct TALER_BANK_TransferDetails
 
   /**
    * Wire transfer subject.  Usually a reserve public key
-   * followed by the BASE URI of the exchange.
+   * followed by the base URL of the exchange.
    */
   char *wire_transfer_subject;
 
diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h
index 24d738f..1a20889 100644
--- a/src/include/taler_error_codes.h
+++ b/src/include/taler_error_codes.h
@@ -1074,11 +1074,19 @@ enum TALER_ErrorCode
 
   /**
    * We got different currencies for the wire fee and the maximum wire
-   * fee.  This response is provided with HTTP status code
+   * fee.  This response is provided with HTTP status code of
    * MHD_HTTP_INTERNAL_SERVER_ERROR.
    */
   TALER_EC_PAY_WIRE_FEE_CURRENCY_MISSMATCH = 2125,
 
+  /**
+   * The merchant refuses to abort and refund the payment
+   * operation as the payment succeeded already.
+   * This response is provided with HTTP status code of
+   * MHD_HTTP_FORBIDDEN.
+   */
+  TALER_EC_PAY_ABORT_REFUND_REFUSED_PAYMENT_COMPLETE = 2126,
+
 
   /**
    * Integer overflow with sepcified timestamp argument detected.
@@ -1377,12 +1385,6 @@ enum TALER_ErrorCode
   TALER_EC_TIP_AUTHORIZE_DB_SOFT_ERROR = 2706,
 
   /**
-   * The reserve that was used to fund the tips was never enabled.
-   * Returned with an HTTP status code of "not found".
-   */
-  TALER_EC_TIP_AUTHORIZE_RESERVE_NOT_ENABLED = 2707,
-
-  /**
    * The backend had trouble accessing the database to persist
    * information about enabling tips.
    * Returned with an HTTP status code of internal error.
@@ -1397,7 +1399,8 @@ enum TALER_ErrorCode
 
   /**
    * The amount requested exceeds the remaining tipping balance for this tip 
ID.
-   * Returned with an HTTP status code of "service unavailable".
+   * Returned with an HTTP status code of "Conflict" (as it conflicts with
+   * a previous pickup operation).
    */
   TALER_EC_TIP_PICKUP_NO_FUNDS = 2801,
 
@@ -1461,6 +1464,38 @@ enum TALER_ErrorCode
    */
   TALER_EC_TIP_QUERY_TIP_ID_UNKNOWN = 2810,
 
+  /**
+   * The instance for check-payment is unknown, likely
+   * a buggy frontend or misconfigured instances.
+   */
+  TALER_EC_CHECK_PAYMENT_INSTANCE_UNKNOWN = 2910,
+
+  /**
+   * We failed to contract terms from our merchant database.
+   * The response is provided with HTTP status code
+   * MHD_HTTP_INTERNAL_SERVER_ERROR.
+   */
+  TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR = 2911,
+
+  /**
+   * We failed to contract terms from our merchant database.
+   * The response is provided with HTTP status code
+   * MHD_HTTP_INTERNAL_SERVER_ERROR.
+   */
+  TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR = 2912,
+
+  /**
+   * The order id we're checking is unknown, likely
+   * the frontend did not create the order first.
+   */
+  TALER_EC_CHECK_PAYMENT_ORDER_ID_UNKNOWN = 2913,
+
+  /**
+   * Failed computing a hash code (likely server out-of-memory).
+   * This response is
+   * provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
+   */
+  TALER_EC_CHECK_PAYMENT_FAILED_COMPUTE_PROPOSAL_HASH = 2014,
 
   /* ********** /test API error codes ************* */
 
@@ -1502,32 +1537,85 @@ enum TALER_ErrorCode
 
   /* *************** Taler BANK/FAKEBANK error codes *************** */
 
+  /**
+   * The request cannot be served because the client failed to
+   * login.  To be returned along HTTP 401 Unauthorized.
+   */
+  TALER_EC_BANK_REJECT_LOGIN_FAILED = 5312,
+
+  /**
+   * The transaction cannot be rejected becasue it does not exist
+   * at the bank.  To be returned along HTTP 404 Not Found.
+   */
+  TALER_EC_BANK_REJECT_TRANSACTION_NOT_FOUND = 5301,
+
+  /**
+   * The client does not own the account credited by the transaction
+   * which is to be rejected, so it has no rights do reject it.  To be
+   * returned along HTTP 403 Forbidden.
+   */
+  TALER_EC_BANK_REJECT_NO_RIGHTS = 5313,
 
   /**
-   * Authentication failed for the /admin/add/incoming request.
-   * Returned with a status code of MHD_HTTP_FORBIDDEN.
+   * The POSTed JSON at /reject was invalid.  To be returned along
+   * HTTP 400 Bad Request.
    */
-  TALER_EC_BANK_TRANSFER_NOT_AUHTORIZED = 4101,
+  TALER_EC_BANK_REJECT_JSON_INVALID = 5306,
 
   /**
-   * Authentication failed for the /history request.
-   * Returned with a status code of MHD_HTTP_FORBIDDEN.
+   * A URL parameter for /history was missing.  To be returned along
+   * HTTP 400 Bad Request.
    */
-  TALER_EC_BANK_HISTORY_NOT_AUHTORIZED = 4151,
+  TALER_EC_BANK_HISTORY_PARAMETER_MISSING = 5208,
 
   /**
-   * The bank could not find the wire transfer that was supposed to
-   * be rejected.
-   * Returned with a status code of MHD_HTTP_NOT_FOUND.
+   * A URL parameter for /history was malformed.  To be returned along
+   * HTTP 400 Bad Request.
    */
-  TALER_EC_BANK_REJECT_NOT_FOUND = 4250,
+  TALER_EC_BANK_HISTORY_PARAMETER_MALFORMED = 5209,
 
   /**
-   * Authentication failed for the /reject request.
-   * Returned with a status code of MHD_HTTP_FORBIDDEN.
+   * The client failed to login for /history.  To be returned along
+   * HTTP 401 Unauthorized.
    */
-  TALER_EC_BANK_REJECT_NOT_AUHTORIZED = 4251,
+  TALER_EC_BANK_HISTORY_LOGIN_FAILED = 5212,
 
+  /**
+   * The bank had trouble obtaining a valid HTTP response.  To be returned
+   * along status code 0.
+   */
+  TALER_EC_BANK_HISTORY_HTTP_FAILURE = 5213,
+
+  /**
+   * The debit account for /admin/add/incoming is not known to the
+   * bank.  To be returned along HTTP 404 Not Found.
+   */
+  TALER_EC_BANK_ADD_INCOMING_UNKNOWN_ACCOUNT = 5100,
+
+  /**
+   * The client specified the same bank account for both the credit
+   * and the debit account.  The bank will not accomplish this operation.
+   * To be returned along HTTP 403 Forbidden.
+   */
+  TALER_EC_BANK_ADD_INCOMING_SAME_ACCOUNT = 5102,
+
+  /**
+   * The operation would put the client in a debit situation which is
+   * forbidden to them.  To return along HTTP 403 Forbidden.
+   */
+  TALER_EC_BANK_ADD_INCOMING_UNALLOWED_DEBIT = 5103,
+
+  /**
+   * The client POSTed an invalid JSON.  To be returned along HTTP
+   * 400 Bad Request.
+   */
+  TALER_EC_BANK_ADD_INCOMING_JSON_INVALID = 5106,
+
+  /**
+   * The client failed to login for /admin/add/incoming.  To be returned
+   * along HTTP 401 Unauthorized.
+   */
+  TALER_EC_BANK_ADD_INCOMING_LOGIN_FAILED = 5112,
 
   /**
    * End of error code range.
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index 7e9ad91..30ea4ce 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -206,6 +206,13 @@ struct TALER_EXCHANGE_Keys
   char *version;
 
   /**
+   * How long after a reserve went idle will the exchange close it?
+   * This is an approximate number, not cryptographically signed by
+   * the exchange (advisory-only, may change anytime).
+   */
+  struct GNUNET_TIME_Relative reserve_closing_delay;
+
+  /**
    * Timestamp indicating the /keys generation.
    */
   struct GNUNET_TIME_Absolute list_issue_date;
@@ -717,6 +724,50 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle 
*exchange,
 
 
 /**
+ * Submit a refund request to the exchange and get the exchange's
+ * response.  This API is used by a merchant.  Note that
+ * while we return the response verbatim to the caller for further
+ * processing, we do already verify that the response is well-formed
+ * (i.e. that signatures included in the response are all valid).  If
+ * the exchange's reply is not well-formed, we return an HTTP status code
+ * of zero to @a cb.
+ *
+ * The @a exchange must be ready to operate (i.e.  have
+ * finished processing the /keys reply).  If this check fails, we do
+ * NOT initiate the transaction with the exchange and instead return NULL.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param amount the amount to be refunded; must be larger than the refund fee
+ *        (as that fee is still being subtracted), and smaller than the amount
+ *        (with deposit fee) of the original deposit contribution of this coin
+ * @param refund_fee fee applicable to this coin for the refund
+ * @param h_contract_terms hash of the contact of the merchant with the 
customer that is being refunded
+ * @param coin_pub coin’s public key of the coin from the original deposit 
operation
+ * @param rtransaction_id transaction id for the transaction between merchant 
and customer (of refunding operation);
+ *                        this is needed as we may first do a partial refund 
and later a full refund.  If both
+ *                        refunds are also over the same amount, we need the 
@a rtransaction_id to make the disjoint
+ *                        refund requests different (as requests are 
idempotent and otherwise the 2nd refund might not work).
+ * @param merchant_pub public key of the merchant
+ * @param merchant_sig signature affirming the refund from the merchant
+ * @param cb the callback to call when a reply for this request is available
+ * @param cb_cls closure for the above callback
+ * @return a handle for this request; NULL if the inputs are invalid (i.e.
+ *         signatures fail to verify).  In this case, the callback is not 
called.
+ */
+struct TALER_EXCHANGE_RefundHandle *
+TALER_EXCHANGE_refund2 (struct TALER_EXCHANGE_Handle *exchange,
+                       const struct TALER_Amount *amount,
+                       const struct TALER_Amount *refund_fee,
+                       const struct GNUNET_HashCode *h_contract_terms,
+                       const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                       uint64_t rtransaction_id,
+                       const struct TALER_MerchantPublicKeyP *merchant_pub,
+                       const struct TALER_MerchantSignatureP *merchant_sig,
+                       TALER_EXCHANGE_RefundResultCallback cb,
+                       void *cb_cls);
+
+
+/**
  * Cancel a refund permission request.  This function cannot be used
  * on a request handle if a response is already served for it.  If
  * this function is called, the refund may or may not have happened.
@@ -802,6 +853,11 @@ struct TALER_EXCHANGE_ReserveHistory
       void *wire_reference;
 
       /**
+       * When did the wire transfer happen?
+       */
+      struct GNUNET_TIME_Absolute timestamp;
+
+      /**
        * Number of bytes stored in @e wire_reference.
        */
       size_t wire_reference_size;
@@ -1306,75 +1362,6 @@ void
 TALER_EXCHANGE_refresh_link_cancel (struct TALER_EXCHANGE_RefreshLinkHandle 
*rlh);
 
 
-/* ********************* /admin/add/incoming *********************** */
-
-
-/**
- * @brief A /admin/add/incoming Handle
- */
-struct TALER_EXCHANGE_AdminAddIncomingHandle;
-
-
-/**
- * Callbacks of this type are used to serve the result of submitting
- * information about an incoming transaction to a exchange.
- *
- * @param cls closure
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
- *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param full_response full response from the exchange (for logging, in case 
of errors)
- */
-typedef void
-(*TALER_EXCHANGE_AdminAddIncomingResultCallback) (void *cls,
-                                                  unsigned int http_status,
-                                                 enum TALER_ErrorCode ec,
-                                                  const json_t *full_response);
-
-
-/**
- * Notify the exchange that we have received an incoming transaction
- * which fills a reserve.  Note that this API is an administrative
- * API and thus not accessible to typical exchange clients, but only
- * to the operators of the exchange.
- *
- * @param exchange the exchange handle; the exchange must be ready to operate
- * @param admin_url URL of the administrative interface of the exchange
- * @param reserve_pub public key of the reserve
- * @param amount amount that was deposited
- * @param execution_date when did we receive the amount
- * @param sender_account_details account information of the sender of the 
money;
- *        the receiver is always the exchange.
- * @param transfer_details details that uniquely identify the transfer;
- *        used to check for duplicate operations by the exchange
- * @param res_cb the callback to call when the final result for this request 
is available
- * @param res_cb_cls closure for the above callback
- * @return NULL
- *         if the inputs are invalid (i.e. invalid amount).
- *         In this case, the callback is not called.
- */
-struct TALER_EXCHANGE_AdminAddIncomingHandle *
-TALER_EXCHANGE_admin_add_incoming (struct TALER_EXCHANGE_Handle *exchange,
-                                   const char *admin_url,
-                                   const struct TALER_ReservePublicKeyP 
*reserve_pub,
-                                   const struct TALER_Amount *amount,
-                                   struct GNUNET_TIME_Absolute execution_date,
-                                   const json_t *sender_account_details,
-                                   const json_t *transfer_details,
-                                   
TALER_EXCHANGE_AdminAddIncomingResultCallback res_cb,
-                                   void *res_cb_cls);
-
-
-/**
- * Cancel an add incoming.  This function cannot be used on a request
- * handle if a response is already served for it.
- *
- * @param aai the admin add incoming request handle
- */
-void
-TALER_EXCHANGE_admin_add_incoming_cancel (struct 
TALER_EXCHANGE_AdminAddIncomingHandle *aai);
-
-
 /* ********************* /track/transfer *********************** */
 
 /**
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index e64b0ad..ae38856 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -733,6 +733,29 @@ typedef int
 
 
 /**
+ * Callback invoked with information about refunds applicable
+ * to a particular coin.
+ *
+ * @param cls closure
+ * @param merchant_pub public key of merchant who authorized refund
+ * @param merchant_sig signature of merchant authorizing refund
+ * @param h_contract hash of contract being refunded
+ * @param rtransaction_id refund transaction ID
+ * @param amount_with_fee amount being refunded
+ * @param refund_fee fee the exchange keeps for the refund processing
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ */
+typedef int
+(*TALER_EXCHANGEDB_RefundCoinCallback)(void *cls,
+                                      const struct TALER_MerchantPublicKeyP 
*merchant_pub,
+                                      const struct TALER_MerchantSignatureP 
*merchant_sig,
+                                      const struct GNUNET_HashCode *h_contract,
+                                      uint64_t rtransaction_id,
+                                      const struct TALER_Amount 
*amount_with_fee,
+                                      const struct TALER_Amount *refund_fee);
+
+
+/**
  * Information about a coin that was revealed to the exchange
  * during /refresh/reveal.
  */
@@ -1358,6 +1381,23 @@ struct TALER_EXCHANGEDB_Plugin
                     struct TALER_EXCHANGEDB_Session *session,
                     const struct TALER_EXCHANGEDB_Refund *refund);
 
+  /**
+   * Select refunds by @a coin_pub.
+   *
+   * @param cls closure of plugin
+   * @param session database handle to use
+   * @param coin_pub coin to get refunds for
+   * @param cb function to call for each refund found
+   * @param cb_cls closure for @a cb
+   * @return query result status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*select_refunds_by_coin)(void *cls,
+                           struct TALER_EXCHANGEDB_Session *session,
+                           const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                           TALER_EXCHANGEDB_RefundCoinCallback cb,
+                           void *cb_cls);
+  
 
   /**
    * Mark a deposit as tiny, thereby declaring that it cannot be
@@ -1658,6 +1698,7 @@ struct TALER_EXCHANGEDB_Plugin
    * @param start_date when does the fee go into effect
    * @param end_date when does the fee end being valid
    * @param wire_fee how high is the wire transfer fee
+   * @param closing_fee how high is the closing fee
    * @param master_sig signature over the above by the exchange master key
    * @return transaction status code
    */
@@ -1668,6 +1709,7 @@ struct TALER_EXCHANGEDB_Plugin
                      struct GNUNET_TIME_Absolute start_date,
                      struct GNUNET_TIME_Absolute end_date,
                      const struct TALER_Amount *wire_fee,
+                     const struct TALER_Amount *closing_fee,
                      const struct TALER_MasterSignatureP *master_sig);
 
 
@@ -1681,6 +1723,7 @@ struct TALER_EXCHANGEDB_Plugin
    * @param[out] start_date when does the fee go into effect
    * @param[out] end_date when does the fee end being valid
    * @param[out] wire_fee how high is the wire transfer fee
+   * @param[out] closing_fee how high is the closing fee
    * @param[out] master_sig signature over the above by the exchange master key
    * @return query status of the transaction
    */
@@ -1692,6 +1735,7 @@ struct TALER_EXCHANGEDB_Plugin
                    struct GNUNET_TIME_Absolute *start_date,
                    struct GNUNET_TIME_Absolute *end_date,
                    struct TALER_Amount *wire_fee,
+                  struct TALER_Amount *closing_fee,
                    struct TALER_MasterSignatureP *master_sig);
 
 
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h
index 6355303..c281d21 100644
--- a/src/include/taler_signatures.h
+++ b/src/include/taler_signatures.h
@@ -178,6 +178,12 @@
  */
 #define TALER_SIGNATURE_MERCHANT_REFUND_OK 1105
 
+/**
+ * Signature where the merchant confirms that the user replayed
+ * a payment for a browser session.
+ */
+#define TALER_SIGNATURE_MERCHANT_PAY_SESSION 1106
+
 
 /*********************/
 /* Wallet signatures */
@@ -1291,6 +1297,31 @@ struct TALER_MerchantRefundConfirmationPS
 
 };
 
+/**
+ * Used by the merchant to confirm to the frontend that
+ * the user did a payment replay with the current browser session.
+ */
+struct TALER_MerchantPaySessionSigPS
+{
+  /**
+   * Set to #TALER_SIGNATURE_MERCHANT_PAY_SESSION.
+   */
+  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+  /**
+   * Hashed order id.
+   * Hashed without the 0-termination.
+   */
+  struct GNUNET_HashCode h_order_id GNUNET_PACKED;
+
+  /**
+   * Hashed session id.
+   * Hashed without the 0-termination.
+   */
+  struct GNUNET_HashCode h_session_id GNUNET_PACKED;
+
+};
+
 GNUNET_NETWORK_STRUCT_END
 
 #endif
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
new file mode 100644
index 0000000..2a95ff6
--- /dev/null
+++ b/src/include/taler_testing_lib.h
@@ -0,0 +1,508 @@
+/*
+  This file is part of TALER
+  (C) 2018 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file include/taler_testing_lib.h
+ * @brief API for writing an interpreter to test Taler components
+ * @author Christian Grothoff <address@hidden>
+ * @author Marcello Stanisci
+ */
+#ifndef TALER_TESTING_LIB_H
+#define TALER_TESTING_LIB_H
+
+#include "taler_util.h"
+#include <gnunet/gnunet_json_lib.h>
+#include "taler_json_lib.h"
+#include <microhttpd.h>
+
+
+/* ********************* Helper functions *********************** */
+
+/**
+ * Find denomination key matching the given amount.
+ *
+ * @param keys array of keys to search
+ * @param amount coin value to look for
+ * @return NULL if no matching key was found
+ */
+const struct TALER_EXCHANGE_DenomPublicKey *
+TALER_TESTING_find_pk (const struct TALER_EXCHANGE_Keys *keys,
+                       const struct TALER_Amount *amount);
+
+
+/**
+ * Prepare launching an exchange.  Checks that the configured
+ * port is available, runs taler-exchange-keyup,
+ * taler-auditor-sign and taler-exchange-dbinit.  Does not
+ * launch the exchange process itself.
+ *
+ * @param config_filename configuration file to use
+ * @return #GNUNET_OK on success, #GNUNET_NO if test should be skipped,
+ *         #GNUNET_SYSERR on test failure
+ */
+int
+TALER_TESTING_prepare_exchange (const char *config_filename);
+
+
+/**
+ * Remove files from previous runs
+ */
+void
+TALER_TESTING_cleanup_files (const char *config_name);
+
+
+/**
+ * Test port in URL string for availability.
+ */
+int
+TALER_TESTING_url_port_free (const char *url);
+
+
+/**
+ * Prepare launching a fakebank.  Check that the configuration
+ * file has the right option, and that the port is avaiable.
+ * If everything is OK, return the configured URL of the fakebank.
+ *
+ * @param config_filename configuration file to use
+ * @return NULL on error, fakebank URL otherwise
+ */
+char *
+TALER_TESTING_prepare_fakebank (const char *config_filename);
+
+
+/* ******************* Generic interpreter logic ****************** */
+
+/**
+ * Global state of the interpreter, used by a command
+ * to access information about other commands.
+ */
+struct TALER_TESTING_Interpreter;
+
+
+/**
+ * A command to be run by the interpreter.
+ */
+struct TALER_TESTING_Command
+{
+
+  /**
+   * Closure for all commands with command-specific context
+   * information.
+   */
+  void *cls;
+
+  /**
+   * Label for the command.
+   */
+  const char *label;
+
+  /**
+   * Runs the command.  Note that upon return, the interpreter
+   * will not automatically run the next command, as the command
+   * may continue asynchronously in other scheduler tasks.  Thus,
+   * the command must ensure to eventually call
+   * #TALER_TESTING_interpreter_next() or
+   * #TALER_TESTING_interpreter_fail().
+   *
+   * @param i interpreter state
+   */
+  void
+  (*run)(void *cls,
+         const struct TALER_TESTING_Command *cmd,
+         struct TALER_TESTING_Interpreter *i);
+
+
+  /**
+   * Clean up after the command.  Run during forced termination
+   * (CTRL-C) or test failure or test success.
+   *
+   * @param cls closure
+   */
+  void
+  (*cleanup)(void *cls,
+             const struct TALER_TESTING_Command *cmd);
+
+  /**
+   * Extract information from a command that is useful for other
+   * commands.
+   *
+   * @param cls closure
+   * @param ret[out] result (could be anything)
+   * @param trait name of the trait
+   * @param selector more detailed information about which object
+   *                 to return in case there were multiple generated
+   *                 by the command
+   * @return #GNUNET_OK on success
+   */
+  int
+  (*traits)(void *cls,
+            void **ret,
+            const char *trait,
+            const char *selector);
+
+};
+
+
+/**
+ * Lookup command by label.
+ */
+const struct TALER_TESTING_Command *
+TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *i,
+                                          const char *label);
+
+
+/**
+ * Obtain main execution context for the main loop.
+ */
+struct GNUNET_CURL_Context *
+TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is);
+
+/**
+ * Obtain current label.
+ */
+const char *
+TALER_TESTING_interpreter_get_current_label (struct TALER_TESTING_Interpreter 
*is);
+
+/**
+ * Obtain main execution context for the main loop.
+ */
+struct GNUNET_CURL_Context *
+TALER_TESTING_interpreter_get_context (struct TALER_TESTING_Interpreter *is);
+
+
+struct TALER_FAKEBANK_Handle *
+TALER_TESTING_interpreter_get_fakebank (struct TALER_TESTING_Interpreter *is);
+
+/**
+ * Current command is done, run the next one.
+ */
+void
+TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is);
+
+/**
+ * Current command failed, clean up and fail the test case.
+ */
+void
+TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is);
+
+/**
+ * Create command array terminator.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_end (void);
+
+
+/**
+ * Wait until we receive SIGCHLD signal.
+ * Then obtain the process trait of the current
+ * command, wait on the the zombie and continue
+ * with the next command.
+ */
+void
+TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is);
+
+
+void
+TALER_TESTING_run (struct TALER_TESTING_Interpreter *is,
+                   struct TALER_TESTING_Command *commands);
+
+
+
+void
+TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is,
+                                 struct TALER_TESTING_Command *commands,
+                                 const char *bank_url);
+
+
+typedef void
+(*TALER_TESTING_Main)(void *cls,
+                      struct TALER_TESTING_Interpreter *is);
+
+
+/**
+ * Initialize scheduler loop and curl context for the testcase.
+ */
+int
+TALER_TESTING_setup (TALER_TESTING_Main main_cb,
+                     void *main_cb_cls);
+
+
+/**
+ * Initialize scheduler loop and curl context for the testcase
+ * including starting and stopping the exchange using the given
+ * configuration file.
+ */
+int
+TALER_TESTING_setup_with_exchange (TALER_TESTING_Main main_cb,
+                                   void *main_cb_cls,
+                                   const char *config_file);
+
+
+
+
+/* ****************** Specific interpreter commands **************** */
+
+/**
+ * Perform a wire transfer (formerly Admin-add-incoming)
+ *
+ * @return NULL on failure
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_fakebank_transfer (const char *label,
+                                     const char *amount,
+                                     const char *bank_url,
+                                     uint64_t debit_account_no,
+                                     uint64_t credit_account_no,
+                                     const char *auth_username,
+                                     const char *auth_password);
+
+
+/**
+ * Create fakebank_transfer command with custom subject.
+ *
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label,
+                                                  const char *amount,
+                                                  const char *bank_url,
+                                                  uint64_t debit_account_no,
+                                                  uint64_t credit_account_no,
+                                                  const char *auth_username,
+                                                  const char *auth_password,
+                                                  const char *subject);
+
+
+/**
+ * Create fakebank_transfer command with custom subject.
+ *
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label,
+                                              const char *amount,
+                                              const char *bank_url,
+                                              uint64_t debit_account_no,
+                                              uint64_t credit_account_no,
+                                              const char *auth_username,
+                                              const char *auth_password,
+                                              const char *ref);
+
+
+/**
+ * Execute taler-exchange-wirewatch process.
+ *
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_wirewatch (const char *label,
+                                  const char *config_filename);
+
+
+/**
+ * Create withdraw command.
+ *
+ * @return NULL on failure
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_withdraw_amount (const char *label,
+                                   struct TALER_EXCHANGE_Handle *exchange,
+                                   const char *reserve_reference,
+                                   const char *amount,
+                                   unsigned int expected_response_code);
+
+
+
+/**
+ * Create withdraw command.
+ *
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_withdraw_denomination (const char *label,
+                                         struct TALER_EXCHANGE_Handle 
*exchange,
+                                         const char *reserve_reference,
+                                         const struct 
TALER_EXCHANGE_DenomPublicKey *dk,
+                                         unsigned int expected_response_code);
+
+
+/* ********************** Generic trait logic for implementing traits 
******************* */
+
+/**
+ * A trait.
+ */
+struct TALER_TESTING_Trait
+{
+  const char *selector;
+
+  const char *trait_name;
+
+  const void *ptr;
+};
+
+
+
+struct TALER_TESTING_Trait
+TALER_TESTING_trait_end (void);
+
+
+int
+TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
+                         void **ret,
+                         const char *trait,
+                         const char *selector);
+
+
+/* ****************** Specific traits supported by this component 
*************** */
+
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_reserve_priv (const char *selector,
+                                       const struct TALER_ReservePrivateKeyP 
*reserve_priv);
+
+
+/**
+ * Obtain a reserve private key from a @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on offer
+ * @param reserve_priv[out] set to the private key of the reserve
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_reserve_priv (const struct TALER_TESTING_Command *cmd,
+                                      const char *selector,
+                                      struct TALER_ReservePrivateKeyP 
**reserve_priv);
+
+
+
+/**
+ * Obtain location where a command stores a pointer to a process
+ *
+ * @param cmd command to extract trait from
+ * @param selector which process to pick if @a cmd has multiple on offer
+ * @param coin_priv[out] set to address of the pointer to the process
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_process (const struct TALER_TESTING_Command *cmd,
+                                 const char *selector,
+                                 struct GNUNET_OS_Process ***processp);
+
+
+
+
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_process (const char *selector,
+                                  struct GNUNET_OS_Process **processp);
+
+
+/**
+ * @param selector
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_coin_priv (const char *selector,
+                                    const struct TALER_CoinSpendPrivateKeyP 
*coin_priv);
+
+
+/**
+ * Obtain a coin private key from a @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on offer
+ * @param coin_priv[out] set to the private key of the coin
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_coin_priv (const struct TALER_TESTING_Command *cmd,
+                                   const char *selector,
+                                   struct TALER_CoinSpendPrivateKeyP 
**coin_priv);
+
+
+
+/**
+ * @param selector
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_blinding_key (const char *selector,
+                                       const struct 
TALER_DenominationBlindingKeyP *blinding_key);
+
+
+/**
+ * Obtain a coin's blinding key from a @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on offer
+ * @param blinding_key[out] set to the blinding key of the coin
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_blinding_key (const struct TALER_TESTING_Command *cmd,
+                                      const char *selector,
+                                      struct TALER_DenominationBlindingKeyP 
**blinding_key);
+
+
+
+
+/**
+ * @param selector
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_denom_pub (const char *selector,
+                                    const struct TALER_EXCHANGE_DenomPublicKey 
*dpk);
+
+
+/**
+ * Obtain a coin private key from a @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on offer
+ * @param dpk[out] set to a denomination key of the coin
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_denom_pub (const struct TALER_TESTING_Command *cmd,
+                                   const char *selector,
+                                   struct TALER_EXCHANGE_DenomPublicKey **dpk);
+
+
+/**
+ * Obtain a coin denomination signature from a @a cmd.
+ *
+ * @param cmd command to extract trait from
+ * @param selector which coin to pick if @a cmd has multiple on offer
+ * @param sig[out] set to a denomination signature over the coin
+ * @return #GNUNET_OK on success
+ */
+int
+TALER_TESTING_get_trait_denom_sig (const struct TALER_TESTING_Command *cmd,
+                                   const char *selector,
+                                   struct TALER_DenominationSignature **dpk);
+
+
+/**
+ * @param selector
+ */
+struct TALER_TESTING_Trait
+TALER_TESTING_make_trait_denom_sig (const char *selector,
+                                    const struct TALER_DenominationSignature 
*sig);
+
+
+
+
+
+
+
+
+
+
+
+#endif
diff --git a/src/include/taler_util.h b/src/include/taler_util.h
index 84d4f5d..407521c 100644
--- a/src/include/taler_util.h
+++ b/src/include/taler_util.h
@@ -22,6 +22,7 @@
 #define TALER_UTIL_H
 
 #include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
 #include "taler_amount_lib.h"
 #include "taler_crypto_lib.h"
 
@@ -134,4 +135,62 @@ const struct GNUNET_OS_ProjectData *
 TALER_project_data_default (void);
 
 
+/**
+ * URL-encode a string according to rfc3986.
+ *
+ * @param s string to encode
+ * @returns the urlencoded string, the caller must free it with GNUNET_free
+ */
+char *
+TALER_urlencode (const char *s);
+
+
+/**
+ * Make an absolute URL with query parameters.
+ *
+ * @param base_url absolute base URL to use
+ * @param path path of the url
+ * @param ... NULL-terminated key-value pairs (char *) for query parameters,
+ *        only the value will be url-encoded
+ * @returns the URL, must be freed with #GNUNET_free
+ */
+char *
+TALER_url_join (const char *base_url,
+                const char *path,
+                ...);
+
+
+/**
+ * Make an absolute URL for the given parameters.
+ *
+ * @param proto protocol for the URL (typically https)
+ * @param host hostname for the URL
+ * @param prefix prefix for the URL
+ * @param path path for the URL
+ * @param ... NULL-terminated key-value pairs (char *) for query parameters,
+ *        the value will be url-encoded
+ * @returns the URL, must be freed with #GNUNET_free
+ */
+char *
+TALER_url_absolute_raw (const char *proto,
+                        const char *host,
+                        const char *prefix,
+                        const char *path,
+                        ...);
+
+
+/**
+ * Make an absolute URL for a given MHD connection.
+ *
+ * @param path path of the url
+ * @param ... NULL-terminated key-value pairs (char *) for query parameters,
+ *        the value will be url-encoded
+ * @returns the URL, must be freed with #GNUNET_free
+ */
+char *
+TALER_url_absolute_mhd (struct MHD_Connection *connection,
+                        const char *path,
+                        ...);
+
+
 #endif
diff --git a/src/json/json_helper.c b/src/json/json_helper.c
index 4f966c6..c8eab06 100644
--- a/src/json/json_helper.c
+++ b/src/json/json_helper.c
@@ -35,25 +35,15 @@
 json_t *
 TALER_JSON_from_amount (const struct TALER_Amount *amount)
 {
-  json_t *j;
+  char *amount_str = TALER_amount_to_string (amount);
+
+  GNUNET_assert (NULL != amount_str);
 
-  if ( (amount->value != (uint64_t) ((json_int_t) amount->value)) ||
-       (0 > ((json_int_t) amount->value)) )
   {
-    /* Theoretically, json_int_t can be a 32-bit "long", or we might
-       have a 64-bit value which converted to a 63-bit signed long
-       long causes problems here.  So we check.  Note that depending
-       on the platform, the compiler may be able to statically tell
-       that at least the first check is always false. */
-    GNUNET_break (0);
-    return NULL;
+    json_t *j = json_string (amount_str);
+    GNUNET_free (amount_str);
+    return j;
   }
-  j = json_pack ("{s:s, s:I, s:I}",
-                 "currency", amount->currency,
-                 "value", (json_int_t) amount->value,
-                 "fraction", (json_int_t) amount->fraction);
-  GNUNET_assert (NULL != j);
-  return j;
 }
 
 
@@ -93,6 +83,20 @@ parse_amount (void *cls,
   json_int_t fraction;
   const char *currency;
 
+  if (json_is_string (root))
+  {
+    if (GNUNET_OK !=
+        TALER_string_to_amount (json_string_value (root), r_amount))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    return GNUNET_OK;
+  }
+
+  /* Also allow the legacy { value, fraction, currency} format.
+     This might be removed in the future. */
+
   memset (r_amount,
           0,
           sizeof (struct TALER_Amount));
@@ -179,6 +183,17 @@ parse_amount_nbo (void *cls,
   json_int_t fraction;
   const char *currency;
 
+  if (json_is_string (root))
+  {
+    if (GNUNET_OK !=
+        TALER_string_to_amount_nbo (json_string_value (root), r_amount))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    return GNUNET_OK;
+  }
+
   memset (&amount,
           0,
           sizeof (struct TALER_Amount));
diff --git a/src/json/test_json.c b/src/json/test_json.c
index 3a89746..d5b6d13 100644
--- a/src/json/test_json.c
+++ b/src/json/test_json.c
@@ -36,14 +36,14 @@ test_amount ()
   struct TALER_Amount a1;
   struct TALER_Amount a2;
   struct GNUNET_JSON_Specification spec[] = {
-    TALER_JSON_spec_amount (NULL, &a2),
+    TALER_JSON_spec_amount ("amount", &a2),
     GNUNET_JSON_spec_end()
   };
 
   GNUNET_assert (GNUNET_OK ==
                 TALER_string_to_amount ("EUR:4.3",
                                         &a1));
-  j = TALER_JSON_from_amount (&a1);
+  j = json_pack("{s:o}", "amount", TALER_JSON_from_amount (&a1));
   GNUNET_assert (NULL != j);
   GNUNET_assert (GNUNET_OK ==
                 GNUNET_JSON_parse (j, spec,
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index c05f756..4f9a636 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -57,11 +57,13 @@ libtalerutil_la_LDFLAGS = \
 
 TESTS = \
  test_amount \
- test_crypto
+ test_crypto \
+ test_url
 
 check_PROGRAMS = \
  test_amount \
- test_crypto
+ test_crypto \
+ test_url
 
 
 test_amount_SOURCES = \
@@ -75,3 +77,9 @@ test_crypto_SOURCES = \
 test_crypto_LDADD = \
   -lgnunetutil \
   libtalerutil.la
+
+test_url_SOURCES = \
+  test_url.c
+test_url_LDADD = \
+  -lgnunetutil \
+  libtalerutil.la
diff --git a/src/util/amount.c b/src/util/amount.c
index 7765c74..20d0642 100644
--- a/src/util/amount.c
+++ b/src/util/amount.c
@@ -207,6 +207,8 @@ void
 TALER_amount_hton (struct TALER_AmountNBO *res,
                    const struct TALER_Amount *d)
 {
+  GNUNET_assert (GNUNET_YES ==
+                TALER_amount_is_valid (d));
   res->value = GNUNET_htonll (d->value);
   res->fraction = htonl (d->fraction);
   memcpy (res->currency,
@@ -230,6 +232,8 @@ TALER_amount_ntoh (struct TALER_Amount *res,
   memcpy (res->currency,
           dn->currency,
           TALER_CURRENCY_LEN);
+  GNUNET_assert (GNUNET_YES ==
+                TALER_amount_is_valid (res));
 }
 
 
diff --git a/src/util/test_url.c b/src/util/test_url.c
new file mode 100644
index 0000000..3734da4
--- /dev/null
+++ b/src/util/test_url.c
@@ -0,0 +1,83 @@
+/*
+  This file is part of TALER
+  (C) 2015 GNUnet e.V.
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file util/test_url.c
+ * @brief Tests for url helpers
+ * @author Florian Dold
+ */
+#include "platform.h"
+#include "taler_util.h"
+
+
+/**
+ * Check and free result.
+ *
+ * @param input to check and free
+ * @param expected expected input
+ */
+static void
+cf (char *input, char *expected)
+{
+  GNUNET_assert (0 == strcmp (input, expected));
+  GNUNET_free (input);
+}
+
+
+int
+main (int argc,
+      const char *const argv[])
+{
+
+  cf (TALER_urlencode (""), "");
+  cf (TALER_urlencode ("abc"), "abc");
+  cf (TALER_urlencode ("~~"), "~~");
+  cf (TALER_urlencode ("foo bar"), "foo%20bar");
+  cf (TALER_urlencode ("foo bar "), "foo%20bar%20");
+  cf (TALER_urlencode ("% % "), "%25%20%25%20");
+
+  cf (TALER_url_join ("https://taler.net/";, "foo", NULL), 
"https://taler.net/foo";);
+  cf (TALER_url_join ("https://taler.net";, "foo", NULL), 
"https://taler.net/foo";);
+  cf (TALER_url_join ("https://taler.net/";, "/foo", NULL), 
"https://taler.net/foo";);
+  cf (TALER_url_join ("https://taler.net/";, "/foo/", NULL), 
"https://taler.net/foo/";);
+
+  cf (TALER_url_join ("https://taler.net/";, "foo", "x", "42", NULL),
+      "https://taler.net/foo?x=42";);
+  cf (TALER_url_join ("https://taler.net/";, "foo", "x", "42", "y", "bla", 
NULL),
+      "https://taler.net/foo?x=42&y=bla";);
+  cf (TALER_url_join ("https://taler.net/";, "foo", "x", NULL, "y", "bla", 
NULL),
+      "https://taler.net/foo?y=bla";);
+  cf (TALER_url_join ("https://taler.net/";, "foo", "x", "", "y", "1", NULL),
+      "https://taler.net/foo?x=&y=1";);
+
+  cf (TALER_url_join ("https://taler.net";, "foo/bar", "x", "a&b", NULL),
+      "https://taler.net/foo/bar?x=a%26b";);
+
+  /* Path component is not encoded! */
+  cf (TALER_url_join ("https://taler.net";, "foo/bar?spam=eggs&quux=", NULL),
+      "https://taler.net/foo/bar?spam=eggs&quux=";);
+
+  cf (TALER_url_absolute_raw ("https", "taler.net", "foo/bar", "baz",
+                              "x", "a&b",
+                              "c", "d",
+                              "e", "",
+                              NULL),
+      "https://taler.net/foo/bar/baz?x=a%26b&c=d&e=";);
+
+  return 0;
+}
+
+/* end of test_url.c */
diff --git a/src/util/util.c b/src/util/util.c
index 8976b0a..ceb3a3a 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -159,4 +159,304 @@ TALER_getopt_get_amount (char shortName,
 }
 
 
+/**
+ * Check if a character is reserved and should
+ * be urlencoded.
+ *
+ * @param c character to look at
+ * @return #GNUNET_YES if @a c needs to be urlencoded,
+ *         #GNUNET_NO otherwise
+ */
+static bool
+is_reserved(char c)
+{
+  switch (c) {
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+    case 'a': case 'b': case 'c': case 'd': case 'e':
+    case 'f': case 'g': case 'h': case 'i': case 'j':
+    case 'k': case 'l': case 'm': case 'n': case 'o':
+    case 'p': case 'q': case 'r': case 's': case 't':
+    case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
+    case 'A': case 'B': case 'C': case 'D': case 'E':
+    case 'F': case 'G': case 'H': case 'I': case 'J':
+    case 'K': case 'L': case 'M': case 'N': case 'O':
+    case 'P': case 'Q': case 'R': case 'S': case 'T':
+    case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
+    case '-': case '.': case '_': case '~':
+      return GNUNET_NO;
+    default:
+      break;
+  }
+  return GNUNET_YES;
+}
+
+
+/**
+ * URL-encode a string according to rfc3986.
+ *
+ * @param s string to encode
+ * @returns the urlencoded string, the caller must free it with GNUNET_free
+ */
+char *
+TALER_urlencode (const char *s)
+{
+  unsigned int new_size;
+  unsigned int i;
+  unsigned int t;
+  char *out;
+
+  new_size = strlen (s);
+  for (i = 0; i < strlen (s); i++)
+    if (GNUNET_YES == is_reserved (s[i]))
+      new_size += 2;
+  out = GNUNET_malloc (new_size + 1);
+  for (i = 0, t = 0; i < strlen (s); i++, t++)
+  {
+    if (GNUNET_YES == is_reserved (s[i]))
+    {
+      snprintf(&out[t], 4, "%%%02X", s[i]);
+      t += 2;
+      continue;
+    }
+    out[t] = s[i];
+  }
+  return out;
+}
+
+
+/**
+ * Grow a string in a buffer with the given size.
+ * The buffer is re-allocated if necessary.
+ *
+ * @param s pointer to string buffer
+ * @param p the string to append
+ * @param n pointer to the allocated size of n
+ * @returns pointer to the resulting buffer,
+ *          might differ from @a s (!!)
+ */
+static char *
+grow_string (char **s, const char *p, size_t *n)
+{
+  for (; strlen (*s) + strlen (p) >= *n; *n *= 2);
+  *s = GNUNET_realloc (*s, *n);
+  GNUNET_assert (NULL != s);
+  strncat (*s, p, *n);
+  return *s;
+}
+
+
+/**
+ * Grow a string in a buffer with the given size.
+ * The buffer is re-allocated if necessary.
+ *
+ * Ensures that slashes are removed or added when joining paths.
+ *
+ * @param s pointer to string buffer
+ * @param p the string to append
+ * @param n pointer to the allocated size of n
+ * @returns pointer to the resulting buffer,
+ *          might differ from @a s (!!)
+ */
+static char *
+grow_string_path (char **s, const char *p, size_t *n)
+{
+  char a = (0 == strlen (*s)) ? '\0' : (*s)[strlen (*s) - 1];
+  char b = (0 == strlen (p)) ? '\0' : p[0];
+
+  if ( (a == '/') && (b == '/'))
+  {
+    p++;
+  }
+  else if ( (a != '/') && (b != '/'))
+  {
+    if (NULL == (*s = grow_string (s, "/", n)))
+      return NULL;
+  }
+  return grow_string (s, p, n);
+}
+
+
+/**
+ * Make an absolute URL with query parameters.
+ *
+ * @param base_url absolute base URL to use
+ * @param path path of the url
+ * @param ... NULL-terminated key-value pairs (char *) for query parameters,
+ *        the value will be url-encoded
+ * @returns the URL, must be freed with #GNUNET_free
+ */
+char *
+TALER_url_join (const char *base_url,
+                const char *path,
+                ...)
+{
+  size_t n = 256;
+  char *res = GNUNET_malloc (n);
+  unsigned int iparam = 0;
+  char *enc;
+  va_list args;
+
+  GNUNET_assert (NULL != res);
+
+  grow_string (&res, base_url, &n);
+
+  grow_string_path (&res, path, &n);
+
+  va_start (args, path);
+
+  while (1) {
+    char *key;
+    char *value;
+    key = va_arg (args, char *);
+    if (NULL == key)
+      break;
+    value = va_arg (args, char *);
+    if (NULL == value)
+      continue;
+    grow_string (&res, (0 == iparam) ? "?" : "&", &n);
+    iparam++;
+    grow_string (&res, key, &n);
+    grow_string (&res, "=", &n);
+    enc = TALER_urlencode (value);
+    grow_string (&res, enc, &n);
+    GNUNET_free (enc);
+  }
+
+  va_end (args);
+
+  return res;
+}
+
+
+/**
+ * Make an absolute URL for the given parameters.
+ *
+ * @param proto protocol for the URL (typically https)
+ * @param host hostname for the URL
+ * @param prefix prefix for the URL
+ * @param path path for the URL
+ * @param args NULL-terminated key-value pairs (char *) for query parameters,
+ *        the value will be url-encoded
+ * @returns the URL, must be freed with #GNUNET_free
+ */
+char *
+url_absolute_raw_va (const char *proto,
+                     const char *host,
+                     const char *prefix,
+                     const char *path,
+                     va_list args)
+{
+  size_t n = 256;
+  char *res = GNUNET_malloc (n);
+  char *enc;
+  unsigned int iparam = 0;
+
+  grow_string (&res, proto, &n);
+  grow_string (&res, "://", &n);
+  grow_string (&res, host, &n);
+
+  grow_string_path (&res, prefix, &n);
+
+  grow_string_path (&res, path, &n);
+
+  while (1) {
+    char *key;
+    char *value;
+    key = va_arg (args, char *);
+    if (NULL == key)
+      break;
+    value = va_arg (args, char *);
+    if (NULL == value)
+      continue;
+    grow_string (&res, (0 == iparam) ? "?" : "&", &n);
+    iparam++;
+    grow_string (&res, key, &n);
+    grow_string (&res, "=", &n);
+    enc = TALER_urlencode (value);
+    grow_string (&res, enc, &n);
+    GNUNET_free (enc);
+  }
+
+  return res;
+}
+
+
+/**
+ * Make an absolute URL for the given parameters.
+ *
+ * @param proto protocol for the URL (typically https)
+ * @param host hostname for the URL
+ * @param prefix prefix for the URL
+ * @param path path for the URL
+ * @param args NULL-terminated key-value pairs (char *) for query parameters,
+ *        the value will be url-encoded
+ * @returns the URL, must be freed with #GNUNET_free
+ */
+char *
+TALER_url_absolute_raw (const char *proto,
+                        const char *host,
+                        const char *prefix,
+                        const char *path,
+                        ...)
+{
+  char *result;
+  va_list args;
+
+  va_start (args, path);
+  result = url_absolute_raw_va (proto, host, prefix, path, args);
+  va_end (args);
+  return result;
+}
+
+
+/**
+ * Make an absolute URL for a given MHD connection.
+ *
+ * @param path path of the url
+ * @param ... NULL-terminated key-value pairs (char *) for query parameters,
+ *        the value will be url-encoded
+ * @returns the URL, must be freed with #GNUNET_free
+ */
+char *
+TALER_url_absolute_mhd (struct MHD_Connection *connection,
+                        const char *path,
+                        ...)
+{
+  /* By default we assume we're running under HTTPS */
+  const char *proto = "https";
+  const char *forwarded_proto = MHD_lookup_connection_value (connection, 
MHD_HEADER_KIND, "X-Forwarded-Proto");
+  const char *host;
+  const char *forwarded_host;
+  const char *prefix;
+  va_list args;
+  char *result;
+
+  if (NULL != forwarded_proto)
+    proto = forwarded_proto;
+
+  host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "Host");
+  forwarded_host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, 
"X-Forwarded-Host");
+
+  prefix = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, 
"X-Forwarded-Prefix");
+  if (NULL == prefix)
+    prefix = "";
+
+  if (NULL != forwarded_host)
+    host = forwarded_host;
+
+  if (NULL == host)
+  {
+    /* Should never happen, at last the host header should be defined */
+    GNUNET_break (0);
+    return NULL;
+  }
+
+  va_start (args, path);
+  result = url_absolute_raw_va (proto, host, prefix, path, args);
+  va_end (args);
+  return result;
+}
+
+
 /* end of util.c */
diff --git a/src/wire/plugin_wire_template.c b/src/wire/plugin_wire_template.c
index c4eefd1..06e70c2 100644
--- a/src/wire/plugin_wire_template.c
+++ b/src/wire/plugin_wire_template.c
@@ -33,9 +33,9 @@ struct TemplateClosure
 {
 
   /**
-   * URI of the bank for sending funds to the bank.
+   * URL of the bank for sending funds to the bank.
    */
-  char *bank_uri;
+  char *bank_url;
 
   /**
    * Which currency do we support?
@@ -285,12 +285,12 @@ libtaler_plugin_wire_template_init (void *cls)
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_string (cfg,
                                              "exchange-wire-template",
-                                             "bank_uri",
-                                             &tc->bank_uri))
+                                             "bank_url",
+                                             &tc->bank_url))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                "exchange-wire-template",
-                               "bank_uri");
+                               "bank_url");
     GNUNET_free (tc);
     return NULL;
   }
@@ -303,7 +303,7 @@ libtaler_plugin_wire_template_init (void *cls)
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                "taler",
                                "CURRENCY");
-    GNUNET_free (tc->bank_uri);
+    GNUNET_free (tc->bank_url);
     GNUNET_free (tc);
     return NULL;
   }
@@ -336,7 +336,7 @@ libtaler_plugin_wire_template_done (void *cls)
   struct TALER_WIRE_Plugin *plugin = cls;
   struct TemplateClosure *tc = plugin->cls;
 
-  GNUNET_free (tc->bank_uri);
+  GNUNET_free (tc->bank_url);
   GNUNET_free (tc->currency);
   GNUNET_free (tc);
   GNUNET_free (plugin);
diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c
index fa6ba3d..c76bf01 100644
--- a/src/wire/plugin_wire_test.c
+++ b/src/wire/plugin_wire_test.c
@@ -41,9 +41,9 @@ struct TestClosure
   char *currency;
 
   /**
-   * URI of our bank.
+   * URL of our bank.
    */
-  char *bank_uri;
+  char *bank_url;
 
   /**
    * Authentication information.
@@ -187,12 +187,12 @@ test_amount_round (void *cls,
  * Compute purpose for signing.
  *
  * @param account number of the account
- * @param bank_uri URI of the bank
+ * @param bank_url URL of the bank
  * @param[out] wsd purpose to be signed
  */
 static void
 compute_purpose (uint64_t account,
-                 const char *bank_uri,
+                 const char *bank_url,
                  struct TALER_MasterWireDetailsPS *wsd)
 {
   struct GNUNET_HashContext *hc;
@@ -208,8 +208,8 @@ compute_purpose (uint64_t account,
                                   &n,
                                    sizeof (n));
   GNUNET_CRYPTO_hash_context_read (hc,
-                                  bank_uri,
-                                  strlen (bank_uri) + 1);
+                                  bank_url,
+                                  strlen (bank_url) + 1);
   GNUNET_CRYPTO_hash_context_finish (hc,
                                     &wsd->h_sepa_details);
 }
@@ -236,7 +236,7 @@ test_wire_validate (void *cls,
   struct TestClosure *tc = cls;
   json_error_t error;
   json_int_t account_no;
-  const char *bank_uri;
+  const char *bank_url;
   const char *sig_s;
   struct TALER_MasterWireDetailsPS wsd;
   struct TALER_MasterSignatureP sig;
@@ -248,7 +248,7 @@ test_wire_validate (void *cls,
                      0,
                      "{s:I, s:s}",
                      "account_number", &account_no,
-                      "bank_uri", &bank_uri))
+                      "bank_url", &bank_url))
   {
     char *dump;
 
@@ -270,14 +270,14 @@ test_wire_validate (void *cls,
                      account_no);
     return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_ACCOUNT_NUMBER;
   }
-  if ( (NULL != tc->bank_uri) &&
-       (0 != strcmp (bank_uri,
-                     tc->bank_uri)) )
+  if ( (NULL != tc->bank_url) &&
+       (0 != strcmp (bank_url,
+                     tc->bank_url)) )
   {
     GNUNET_asprintf (emsg,
-                     "Wire specifies bank URI `%s', but this exchange only 
supports `%s'\n",
-                     bank_uri,
-                     tc->bank_uri);
+                     "Wire specifies bank URL `%s', but this exchange only 
supports `%s'\n",
+                     bank_url,
+                     tc->bank_url);
     return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_BANK;
   }
   if (NULL == master_pub)
@@ -298,7 +298,7 @@ test_wire_validate (void *cls,
     return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_SIGNATURE;
   }
   compute_purpose (account_no,
-                   bank_uri,
+                   bank_url,
                    &wsd);
   if (GNUNET_OK !=
       GNUNET_STRINGS_string_to_data (sig_s,
@@ -616,7 +616,7 @@ test_sign_wire_details (void *cls,
                         struct TALER_MasterSignatureP *sig)
 {
   struct TALER_MasterWireDetailsPS wsd;
-  const char *bank_uri;
+  const char *bank_url;
   const char *type;
   json_int_t account;
   json_error_t err;
@@ -627,7 +627,7 @@ test_sign_wire_details (void *cls,
                       0 /* flags */,
                       "{s:s, s:s, s:I}",
                       "type", &type,
-                      "bank_uri", &bank_uri,
+                      "bank_url", &bank_url,
                       "account_number", &account))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
@@ -644,7 +644,7 @@ test_sign_wire_details (void *cls,
     return GNUNET_SYSERR;
   }
   compute_purpose (account,
-                   bank_uri,
+                   bank_url,
                    &wsd);
   GNUNET_CRYPTO_eddsa_sign (&key->eddsa_priv,
                            &wsd.purpose,
@@ -736,7 +736,7 @@ test_execute_wire_transfer (void *cls,
   wire_s = GNUNET_STRINGS_data_to_string_alloc (&bf.wtid,
                                                 sizeof (bf.wtid));
   eh->aaih = TALER_BANK_admin_add_incoming (tc->ctx,
-                                            tc->bank_uri,
+                                            tc->bank_url,
                                             &tc->auth,
                                             exchange_base_url,
                                             wire_s,
@@ -891,9 +891,10 @@ bhist_cb (void *cls,
     GNUNET_free (whh);
     break;
   default:
-    /* FIXME: consider modifying API to pass more specific error code(s)
-       back to the application. */
-    GNUNET_break (0);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Bank failed with HTTP status %u (EC: %u)\n",
+                http_status,
+                ec);
     if (NULL != whh->hres_cb)
       (void) whh->hres_cb (whh->hres_cb_cls,
                            ec,
@@ -971,7 +972,7 @@ test_get_history (void *cls,
   whh->hres_cb = hres_cb;
   whh->hres_cb_cls = hres_cb_cls;
   whh->hh = TALER_BANK_history (tc->ctx,
-                                tc->bank_uri,
+                                tc->bank_url,
                                 &tc->auth,
                                 (uint64_t) tc->exchange_account_no,
                                 direction,
@@ -1088,7 +1089,7 @@ test_reject_transfer (void *cls,
   rh->rej_cb = rej_cb;
   rh->rej_cb_cls = rej_cb_cls;
   rh->brh = TALER_BANK_reject (tc->ctx,
-                               tc->bank_uri,
+                               tc->bank_url,
                                &tc->auth,
                                (uint64_t) tc->exchange_account_no,
                                GNUNET_ntohll (*rowid_b64),
@@ -1149,12 +1150,12 @@ libtaler_plugin_wire_test_init (void *cls)
     if (GNUNET_OK !=
         GNUNET_CONFIGURATION_get_value_string (cfg,
                                                "exchange-wire-test",
-                                               "BANK_URI",
-                                               &tc->bank_uri))
+                                               "BANK_URL",
+                                               &tc->bank_url))
     {
       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                  "exchange-wire-test",
-                                 "BANK_URI");
+                                 "BANK_URL");
       GNUNET_free (tc);
       return NULL;
     }
@@ -1167,7 +1168,7 @@ libtaler_plugin_wire_test_init (void *cls)
       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                  "exchange-wire-test",
                                  "EXCHANGE_ACCOUNT_NUMBER");
-      GNUNET_free (tc->bank_uri);
+      GNUNET_free (tc->bank_url);
       GNUNET_free (tc);
       return NULL;
     }
@@ -1180,7 +1181,7 @@ libtaler_plugin_wire_test_init (void *cls)
       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                  "taler",
                                  "CURRENCY");
-      GNUNET_free (tc->bank_uri);
+      GNUNET_free (tc->bank_url);
       GNUNET_free (tc);
       return NULL;
     }
@@ -1193,7 +1194,7 @@ libtaler_plugin_wire_test_init (void *cls)
       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                  "exchange-wire-test",
                                  "USERNAME");
-      GNUNET_free (tc->bank_uri);
+      GNUNET_free (tc->bank_url);
       GNUNET_free (tc);
       return NULL;
     }
@@ -1206,7 +1207,7 @@ libtaler_plugin_wire_test_init (void *cls)
       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                  "exchange-wire-test",
                                  "PASSWORD");
-      GNUNET_free (tc->bank_uri);
+      GNUNET_free (tc->bank_url);
       GNUNET_free (tc);
       GNUNET_free (user);
       return NULL;
@@ -1221,7 +1222,7 @@ libtaler_plugin_wire_test_init (void *cls)
     {
       GNUNET_break (0);
       GNUNET_free (tc->currency);
-      GNUNET_free (tc->bank_uri);
+      GNUNET_free (tc->bank_url);
       GNUNET_free (tc->auth.details.basic.username);
       GNUNET_free (tc->auth.details.basic.password);
       GNUNET_free (tc);
@@ -1286,7 +1287,7 @@ libtaler_plugin_wire_test_done (void *cls)
     break;
   }
   GNUNET_free_non_null (tc->currency);
-  GNUNET_free_non_null (tc->bank_uri);
+  GNUNET_free_non_null (tc->bank_url);
   GNUNET_free (tc);
   GNUNET_free (plugin);
   return NULL;
diff --git a/src/wire/test_wire_plugin.c b/src/wire/test_wire_plugin.c
index 0e24120..f5dba01 100644
--- a/src/wire/test_wire_plugin.c
+++ b/src/wire/test_wire_plugin.c
@@ -73,7 +73,7 @@ static struct TestBlock tests[] = {
   },
   {
     .plugin_name = "test",
-    .json_proto = "{  \"type\":\"test\", \"bank_uri\":\"http://localhost/\";, 
\"account_number\":42 }",
+    .json_proto = "{  \"type\":\"test\", \"bank_url\":\"http://localhost/\";, 
\"account_number\":42 }",
     .round_in = "KUDOS:0.123456",
     .round_out = "KUDOS:0.12",
     .currency = "KUDOS"
diff --git a/src/wire/test_wire_plugin.conf b/src/wire/test_wire_plugin.conf
index 717c166..90b4f07 100644
--- a/src/wire/test_wire_plugin.conf
+++ b/src/wire/test_wire_plugin.conf
@@ -13,9 +13,9 @@ SEPA_RESPONSE_FILE = test_wire_plugin_sepa.json
 
 [exchange-wire-test]
 # For transfers made by the exchange, we need to know
-# the URI of the bank (where the /admin/add/incoming API
+# the URL of the bank (where the /admin/add/incoming API
 # is avaialble).
-BANK_URI = http://localhost/
+BANK_URL = http://localhost/
 
 [taler]
 CURRENCY = "EUR"
diff --git a/src/wire/test_wire_plugin_test.json 
b/src/wire/test_wire_plugin_test.json
index 6fe6b23..e5a0c33 100644
--- a/src/wire/test_wire_plugin_test.json
+++ b/src/wire/test_wire_plugin_test.json
@@ -1,7 +1,7 @@
 {
   "type": "test",
-  "bank_uri": "http://localhost/";,
+  "bank_url": "http://localhost/";,
   "sig": 
"KX1CMHNFH1WE10244AEF07AXHJCF9PZDZVNZBC9P4EJEQ1MH1Y3C2TWF08VTQMK4N5TCV0V1VTGWSV0WB8TB9YQRZW87F5A6KCEZ81R",
   "account_number": 42,
   "salt": 
"EZV905MQPVAZEMGC6SEZQF2Z75P6ZKTN8TX00JHN11S7J81DQ78G8Z551K6TGR9WHPP0JW1X9J9X9CVRY48JTHBCP6Q4XKJ6R2G18G0"
-}
\ No newline at end of file
+}
diff --git a/src/wire/test_wire_plugin_transactions_test.c 
b/src/wire/test_wire_plugin_transactions_test.c
index a020f13..ccfde1b 100644
--- a/src/wire/test_wire_plugin_transactions_test.c
+++ b/src/wire/test_wire_plugin_transactions_test.c
@@ -38,7 +38,7 @@
  * Input for the wire transfer details.
  */
 static const char *json_proto =
-  "{  \"type\":\"test\", \"bank_uri\":\"http://localhost:8088/\";, 
\"account_number\":42 }";
+  "{  \"type\":\"test\", \"bank_url\":\"http://localhost:8088/\";, 
\"account_number\":42 }";
 
 
 /**
diff --git a/src/wire/test_wire_plugin_transactions_test.conf 
b/src/wire/test_wire_plugin_transactions_test.conf
index 601b28f..42fb51b 100644
--- a/src/wire/test_wire_plugin_transactions_test.conf
+++ b/src/wire/test_wire_plugin_transactions_test.conf
@@ -7,9 +7,9 @@ TEST_RESPONSE_FILE = test_wire_plugin_test.json
 
 [exchange-wire-test]
 # For transfers made by the exchange, we need to know
-# the URI of the bank (where the /admin/add/incoming API
+# the URL of the bank (where the /admin/add/incoming API
 # is avaialble).
-BANK_URI = http://localhost:8088/
+BANK_URL= http://localhost:8088/
 
 [taler]
 CURRENCY = "KUDOS"
diff --git a/src/wire/wire-test.conf b/src/wire/wire-test.conf
index 29846ce..7ee217e 100644
--- a/src/wire/wire-test.conf
+++ b/src/wire/wire-test.conf
@@ -14,8 +14,8 @@ TEST_RESPONSE_FILE = 
${TALER_CONFIG_HOME}/exchange/wire/test.json
 EXCHANGE_ACCOUNT_NUMBER = 2
 
 # For accessing transfers, we need to know
-# the URI of the bank (where the /history API is available).
-# BANK_URI = https://bank.demo.taler.net/
+# the URL of the bank (where the /history API is available).
+# BANK_URL = https://bank.demo.taler.net/
 
 # Authentication information for basic authentication
 USERNAME = user

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

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