gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-merchant] branch stable updated (2a0a13a -> 194c3c0)


From: gnunet
Subject: [GNUnet-SVN] [taler-merchant] branch stable updated (2a0a13a -> 194c3c0)
Date: Mon, 05 Feb 2018 11:48:32 +0100

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

marcello pushed a change to branch stable
in repository merchant.

    from 2a0a13a  simplified tipping
     add a6a6868  towards using new AAI in generate-payments
     add 9d79c8c  adaptations to taler-merchant-generate-payments to use 
(fakebank) admin/add/incoming instead of exchange; not quite there yet. Also 
fixes #5174
     add 015dd3a  fix bug in testcase (and API) introduced when next_url and 
pickup_url were added to the spec
     add ee32930  install SIGCHLD handler only after wget is done, to ensure we 
don't block during fakebank interaction
     add b7c3585  first refactoring for #5158: allow multiple exchange URLs to 
be in database per /pay operation, plus other minor clean ups
     add 774758e  forgot to remove redundant struct in previous commit
     add bc88e6b  working on #5158, moving exchange_url to coin in /pay API, 
also renaming 'f' field to more clear 'contribution'
     add 07c86af  sync
     add ae5490c  add wire fee persisting per coin for /pay
     add f01c986  fix wire fee calculation logic for multi-exchange /pay
     add d0e409d  fix wire fee calculation logic for multi-exchange /pay
     add a7b2d84  implement TALER_MERCHANT_pay_abort() -- first version, lacks 
signature verification
     add b691689  more work towards /pay abort mode
     add f8f00e7  implement server-side of /pay abort-refund handling (#5158)
     add 51f363f  adding pay_again and pay_abort commands to testcase 
(incomplete, not used)
     add 7e9cb4f  rudimentary implementation of pay_refund_cb
     add 6b2297c  expand testcases to cover pay-again case
     add b31e9a3  expand test logic for #5158
     add cc367e6  enable test logic for #5158
     add a2129ac  start implementation of /check-pay
     add 10cd6a5  add missing check-payment implementation
     add 0db5194  add /trigger-pay skeleton
     add f864b1d  check history elements are returned from younger to older 
(#5014)
     add 391f371  implement /trigger-pay
     add c9cb5f2  respect X-Forwaded-Prefix
     add c327c34  confirm_url not needed anymore
     add abc3970  additional headers for taler-pay
     add cde5619  add session_sig to pay response
     add e06b52d  fill in missing proposal fields
     add a130158  add nonce only when proposal is requested
     add d9a0005  missing parenthesis
     add 50c7fe8  Addressing #5015.
     add c4fe61a  fix proposal test cases, fix use after free in refunds
     add 90d329c  gitignore
     add 0dc840d  products must be array, not object
     add 2a8c44f  generate contract url for wallet
     add f019979  patch for #5198
     add 55cd81c  also log error code on unexpected failure
     add 0d50ba2  fix a few memory leaks
     add 4446b15  fix test memory leaks
     add 07b11cf  fix leak
     add 4752bd0  add resource_url for re-pay detection without contract 
download
     add 0b5bb42  fix leaks in /proposal handling
     add a0fffe7  convert amount from string in /proposal
     add a1a5179  use dot to separate default order_id for friendlier URLs
     add 8e2a1cf  nicer order ids
     add a2a1f52  fix use after free (copy strings properly)
     add 144e55a  typo in header
     add 09de8f4  parse locations from config
     add 7215dea  remove unused exported variable
     add 4d6a591  include locations and proper labels in proposal
     add f52a678  use correct label
     add 69eefa5  convert refund amount if it is a string
     add 074f548  add backend redirect url to refund response
     add e5b33c5  generate complete URL for refund pickup
     add 4f2901f  fix use after free
     add fa3662a  make use of implementation of #5254
     add 81e2d6b  implement redirect-based tipping
     add 378c325  accept string amount for tip
     add 569ef00  add missing pack format field
     add f7baa35  minor style fix
     add e8f4501  work on #4939: load multiple wire methods per instance
     add 2610e66  fix leak
     add 54b7213  towards expanding testcase for #5258
     add d127df4  include information about refund in /check-payment
     add 345c41b  improve human-readable error message (maybe addressing 
Florians #5258-12809 concern
     add 77cf038  do not complain given legal error code
     add 0bc2f82  Florian is right, this should be a 409, not 500
     add d08f3bc  include contract terms in refund / check-pay
     add 2a87fd9  use 409 instead of 503 in lib (and test)
     add 01ad0c0  fix check-payment logic without session_id, use proper error 
codes
     add d66cae9  should be find_order, not find_orders
     add bee5098  fix #5259 (assertion slightly wrong)
     add cf4e7ee  implement check payment API client
     add 450ff23  make arguments const
     add 0b97373  tests for /check-payment
     add dcfd1fe  commit missing file
     add 00d2528  fix type error that lead to the strange bug
     add bd79913  fix #B5258-12808 issue
     add 0a0fe09  fix #5262
     add be835f4  return error instead of crashing on /pay with zero coins
     add 71c7b94  smaller refund response, give same refund format on /pay abort
     add 7580c75  put customer-facing endpoints under /public/
     add 237ff9a  always set the paid field in the check payment response
     add cb5ac23  adjust proposal download URL
     add 60fdfe7  adjust refund pickup URL
     add f587019  no need to convert amount now
     add 4b47e3a  keep old /tip-pickup alias
     add a4bff93  use url instead of uri consistently
     add 7cc0757  use url consistently in docs
     add 07cc75f  use url helpers from taler util
     add 9fe3018  nicer error messages for /tip-authorize
     add d0078be  remove remnants of tip-enable
     add 77f74fb  implement /tip-query and fix tip db issues (uniqueness)
     add 299b442  proper return code for insert
     add a2610d1  pickup URL is public now
     add 4db25c5  fix doc build, include syntax highlighting
     add 51ed7cb  allow default instance for POST /order
     add 3e2fe5b  add missing doc syntax highlighter
     add b9b9e1e  only fill out merchant info if not given
     add 1ae3bdf  remove unnecessary table, track last_session_id
     add 194c3c0  fix two uninitialized memory accesses

No new revisions were added by this update.

Summary of changes:
 .gitignore                                         |    2 +
 ChangeLog                                          |   15 +
 doc/Makefile.am                                    |   11 +-
 doc/highlight.pack.js                              |    2 +
 doc/manual.texi                                    |   50 +-
 doc/syntax.texi                                    |   44 +
 doc/taler-merchant-generate-payments.1             |   50 +
 doc/taler-merchant-tip-enable.1                    |   47 -
 src/backend/Makefile.am                            |    5 +-
 src/backend/taler-merchant-httpd.c                 |  351 +++-
 src/backend/taler-merchant-httpd.h                 |   62 +-
 src/backend/taler-merchant-httpd_auditors.c        |   18 +-
 src/backend/taler-merchant-httpd_check-payment.c   |  348 ++++
 ...able.h => taler-merchant-httpd_check-payment.h} |   24 +-
 src/backend/taler-merchant-httpd_exchanges.c       |   40 +-
 src/backend/taler-merchant-httpd_exchanges.h       |    2 +-
 src/backend/taler-merchant-httpd_pay.c             | 1477 ++++++++++------
 src/backend/taler-merchant-httpd_proposal.c        |  318 +++-
 src/backend/taler-merchant-httpd_refund.c          |   97 +-
 src/backend/taler-merchant-httpd_responses.c       |   33 +-
 src/backend/taler-merchant-httpd_responses.h       |   34 +-
 src/backend/taler-merchant-httpd_tip-authorize.c   |  422 ++++-
 src/backend/taler-merchant-httpd_tip-enable.c      |  157 --
 src/backend/taler-merchant-httpd_tip-pickup.c      |   20 +-
 src/backend/taler-merchant-httpd_tip-query.c       |  499 +++++-
 src/backend/taler-merchant-httpd_tip-query.h       |    4 +-
 .../taler-merchant-httpd_track-transaction.c       |  313 ++--
 src/backend/taler-merchant-httpd_track-transfer.c  |   37 +-
 src/backend/taler-merchant-httpd_trigger-pay.c     |   96 +
 ...-query.h => taler-merchant-httpd_trigger-pay.h} |   26 +-
 src/backenddb/plugin_merchantdb_postgres.c         |  625 ++++---
 src/backenddb/test_merchantdb.c                    |  149 +-
 src/include/taler_merchant_service.h               |  422 +++--
 src/include/taler_merchantdb_plugin.h              |  153 +-
 src/lib/Makefile.am                                |    6 +-
 src/lib/merchant_api_check_payment.c               |  258 +++
 src/lib/merchant_api_common.c                      |   50 -
 src/lib/merchant_api_common.h                      |   36 -
 src/lib/merchant_api_history.c                     |   10 +-
 src/lib/merchant_api_pay.c                         |  768 ++++++--
 ...merchant_api_pay.c => merchant_api_pay_abort.c} |   80 +-
 src/lib/merchant_api_proposal.c                    |  163 +-
 src/lib/merchant_api_refund.c                      |   17 +-
 src/lib/merchant_api_tip_authorize.c               |   24 +-
 src/lib/merchant_api_tip_enable.c                  |  227 ---
 src/lib/merchant_api_tip_pickup.c                  |   18 +-
 src/lib/merchant_api_tip_query.c                   |  244 +++
 src/lib/merchant_api_track_transaction.c           |   10 +-
 src/lib/merchant_api_track_transfer.c              |   27 +-
 src/lib/test_merchant_api.c                        | 1866 ++++++++++++++++----
 src/lib/test_merchant_api.conf                     |   15 +-
 .../.config/taler/merchant/wire/test.json          |    2 +-
 .../test_merchant_api_home/.config/taler/test.json |    4 +-
 src/merchant-tools/Makefile.am                     |   14 +-
 src/merchant-tools/README                          |    2 +-
 .../taler-merchant-generate-payments.c             |  531 ++++--
 src/merchant-tools/taler-merchant-tip-enable.c     |  277 ---
 57 files changed, 7283 insertions(+), 3319 deletions(-)
 create mode 100644 doc/highlight.pack.js
 create mode 100644 doc/syntax.texi
 create mode 100644 doc/taler-merchant-generate-payments.1
 delete mode 100644 doc/taler-merchant-tip-enable.1
 create mode 100644 src/backend/taler-merchant-httpd_check-payment.c
 rename src/backend/{taler-merchant-httpd_tip-enable.h => 
taler-merchant-httpd_check-payment.h} (63%)
 delete mode 100644 src/backend/taler-merchant-httpd_tip-enable.c
 create mode 100644 src/backend/taler-merchant-httpd_trigger-pay.c
 copy src/backend/{taler-merchant-httpd_tip-query.h => 
taler-merchant-httpd_trigger-pay.h} (62%)
 create mode 100644 src/lib/merchant_api_check_payment.c
 delete mode 100644 src/lib/merchant_api_common.c
 delete mode 100644 src/lib/merchant_api_common.h
 copy src/lib/{merchant_api_pay.c => merchant_api_pay_abort.c} (87%)
 delete mode 100644 src/lib/merchant_api_tip_enable.c
 create mode 100644 src/lib/merchant_api_tip_query.c
 delete mode 100644 src/merchant-tools/taler-merchant-tip-enable.c

diff --git a/.gitignore b/.gitignore
index 7ef1c79..f2f8d1f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+.vscode
 *~
 *Makefile.in
 !src/mitm/*.in
@@ -40,6 +41,7 @@ doc/*
 doc/version.texi
 !doc/*.am
 !doc/*.sh
+!doc/*.js
 !doc/examples/
 src/lib/test_merchant_api_home/.local/share/taler/exchange/wirefees/
 src/merchant-tools/taler-merchant-generate-payments
diff --git a/ChangeLog b/ChangeLog
index 5b0dfa3..b3c2c46 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+Mon Jan 22 21:54:42 CET 2018
+       Address #5262. -CG
+
+Tue Jan 2 00:27:29 2018
+       Implement #5158 (proper handling of aborted payments). -CG
+
+Wed Dec 27 11:21:43 2017
+       Complete logic to allow /pay to span coins from multiple exchanges. -CG
+
+Wed Dec 13 21:50:59 2017
+       Use new wire transfer logic in payments generator. -CG
+
+Thu Dec 7 07:42:40 2017
+       Implemented new tipping feature (now with private keys in files). -CG
+
 Wed Oct 18 15:33:23 CEST 2017
        Releasing taler-merchant 0.4.0. -CG
 
diff --git a/doc/Makefile.am b/doc/Makefile.am
index e20e333..a755143 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1,18 +1,17 @@
 all: manual.pdf manual.html
 
 manual.pdf: arch.pdf manual.texi
-       texi2pdf manual.texi
-manual.html: arch.jpg manual.texi
-       makeinfo --html --no-split manual.texi
+manual.html: arch.png manual.texi
+
 arch.pdf: arch.dot
        dot -Tpdf arch.dot > arch.pdf
-arch.jpg: arch.dot
-       dot -Tjpg arch.dot > arch.jpg
+arch.png: arch.dot
+       dot -Tpng arch.dot > arch.png
 
 AM_MAKEINFOHTMLFLAGS = --no-split --css-ref=docstyle.css 
--css-ref=brown-paper.css
 
 man_MANS = \
-  taler-merchant-tip-enable.1 \
+  taler-merchant-generate-payments.1 \
   taler-merchant-httpd.1
 
 info_TEXINFOS = manual.texi
diff --git a/doc/highlight.pack.js b/doc/highlight.pack.js
new file mode 100644
index 0000000..b2f9ff2
--- /dev/null
+++ b/doc/highlight.pack.js
@@ -0,0 +1,2 @@
+/*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */
+!function(e){var n="object"==typeof window&&window||"object"==typeof 
self&&self;"undefined"!=typeof 
exports?e(exports):n&&(n.hljs=e({}),"function"==typeof 
define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function 
n(e){return 
e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function 
t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return 
t&&0===t.index}function a(e){return k.test(e)}function i(e){var 
n,t,r,i,o=e.className+" ";i [...]
\ No newline at end of file
diff --git a/doc/manual.texi b/doc/manual.texi
index 3c7c3c0..d8eeb0e 100644
--- a/doc/manual.texi
+++ b/doc/manual.texi
@@ -4,6 +4,8 @@
 @include version.texi
 @settitle The GNU Taler merchant backend operator tutorial @value{VERSION}
 
address@hidden syntax.texi
+
 @c Define a new index for options.
 @defcodeindex op
 @c Combine everything into one index (arbitrarily chosen to be the
@@ -159,7 +161,7 @@ The Taler software stack for a merchant consists of four 
main components:
 The following image illustrates the various interactions of these
 key components:
 
address@hidden @image{arch, 3in, 4in}
address@hidden @image{arch, 3in}
 
 @cindex RESTful
 Basically, the backend provides the cryptographic protocol support,
@@ -575,11 +577,11 @@ In that section, the following options need to be 
configured:
 @itemize
 
 @item
-The ``uri'' option specifies the exchange's base URL.  For example,
+The ``url'' option specifies the exchange's base URL.  For example,
 to use the Taler demonstrator use:
 
 @example
-$ taler-config -s merchant-exchange-demo -o URI \
+$ taler-config -s merchant-exchange-demo -o URL \
   -V https://exchange.demo.taler.net/
 @end example
 
@@ -639,7 +641,7 @@ For the @code{test} wire format, a sample specification 
looks as follows:
 @verbatim
 {
   "type": "test",
-  "bank_uri": "https://bank.demo.taler.net/";,
+  "bank_url": "https://bank.demo.taler.net/";,
   "account_number": 5,
   "salt": "RANDOMSALT"
 }
@@ -695,7 +697,7 @@ TEST_RESPONSE_FILE = $DATADIR/test.json
 config = postgres:///donations
 
 [merchant-demoexchange]
-uri = https://exchange.demo.taler.net/
+url = https://exchange.demo.taler.net/
 master_key = CQQZ9DY3MZ1ARMN5K1VKDETS04Y2QCKMMCFHZSWJWWVN82BTTH00
 @end smallexample
 
@@ -785,7 +787,7 @@ currency = KUDOS
 Run the test in the following way:
 
 @example
-$ taler-merchant-generate-payments [-c config] [-e EURI] [-m MURI]
+$ taler-merchant-generate-payments [-c config] [-e EURL] [-m MURL]
 @end example
 
 the argument @code{config} given to @code{-c} points to the configuration
@@ -794,7 +796,7 @@ By default, the tool forks two processes: one for the 
merchant backend, and one
 for the exchange.
 The option @code{-e} (@code{-m}) avoids any exchange (merchant backend) fork,
 and just runs the generator against the exchange (merchant backend) running
-at @code{EURI} (@code{MURI}).
+at @code{EURL} (@code{MURL}).
 
 Please NOTE that the generator contains @emph{hardcoded} values, as for deposit
 fees of the coins it uses.  In order to work against the used exchange, those 
values
@@ -1125,36 +1127,16 @@ Make your wire transfer and (optionally) check at
 ``https://exchange:443/reserve/status/reserve_pub=QPE24X...''
 whether your transfer has arrived at the exchange.
 @c FIXME: we should create a nicer tool to do this check!
-Once the funds have arrived, you can now enable tipping using:
 
address@hidden
-$ taler-merchant-tip-enable \
-    --amount=AMOUNT \
-    --backend=BACKEND_URI \
-    --credit-uuid=CREDIT_UUID \
-    --instance=INSTANCE \
-    --expiration=EXPIRATION
address@hidden example
-For ``AMOUNT'', specify the amount you transferred in the usual Taler
-format of ``CURRENCY:VALUE[.FRACTION]'', i.e. ``EUR:50''.  The
-``BACKEND_URI'' should be the URI where your Taler merchant backend is
-running.  For ``CREDIT_UUID'', you need to specify a unique number
-that identifies your wire transfer.  You may have gotten one from your
-bank, or you can just make one up! The important thing is that you
-must never use the same UUID twice, except to repeat a failed command.
-For INSTANCE, specify the backend instance (i.e. ``default'').
-Finally, for EXPIRATION, pick a date two weeks after the wire
-transfer, unless you know that the exchange that is being used has a
-different period for closing reserves.  The format @code{YYYY-MM-DD}
-is accepted.
-
-Note that an exchange will typically close a reserve after two weeks,
+Once the funds have arrived, you can start to use the reserve
+for tipping.
+
+Note that an exchange will typically close a reserve after four weeks,
 wiring all remaining funds back to the sender's account.  Thus, you
 should plan to wire funds corresponding to a campaign of about two
 weeks to the exchange initially. If your campaign runs longer, you
-should wire further funds to the reserve every week to prevent it from
-expiring.  You need to run the ``taler-merchant-tip-enable'' command
-each time after you wire more funds to the reserve.
+should wire further funds to the reserve every other week to prevent
+it from expiring.
 
 
 @subsection Authorize a tip
@@ -1176,7 +1158,7 @@ in the body of the POST request:
 @end itemize
 
 In response to this request, the backend will return a tip token, an expiration
-time and the exchange URI.  The expiration time will indicate how long the tip
+time and the exchange URL.  The expiration time will indicate how long the tip
 is valid (when the reserve expires).  The tip token is an opaque string that
 contains all the information needed by the wallet to process the tip.  The
 frontend must send this tip token to the browser in a  a special ``402 Payment
diff --git a/doc/syntax.texi b/doc/syntax.texi
new file mode 100644
index 0000000..8aca39d
--- /dev/null
+++ b/doc/syntax.texi
@@ -0,0 +1,44 @@
address@hidden Syntax highlighting for texinfo's HTML output
+
address@hidden
+<script src="highlight.pack.js"></script>
+<script>
+var hls = [];
+var syntaxAuto = true;
+addEventListener("DOMContentLoaded", function() {
+  // Highlight blocks with fixed language
+  for (let x of hls) {
+    let next = x[0].nextElementSibling;
+    console.log("next", next);
+    let blocks = next.querySelectorAll("pre.example");
+    for (let i = 0; i < blocks.length; i++) {
+      blocks[i].classList.add("language-" + x[1]);
+      hljs.highlightBlock(blocks[i]);
+    }
+  } 
+  // auto-detect other blocks if not disabled
+  if (syntaxAuto) {
+    let blocks = document.querySelectorAll("pre.example");
+    for (let i = 0; i < blocks.length; i++) {
+      hljs.highlightBlock(blocks[i]);
+    }
+  }
+});
+</script>
address@hidden html
+
address@hidden setsyntax{lang}
address@hidden
+<script>
+hls.push([document.currentScript, "\lang\"]);
+</script>
address@hidden html
address@hidden macro
+
address@hidden setsyntaxnoauto{}
address@hidden
+<script>
+syntaxAuto = false;
+</script>
address@hidden html
address@hidden macro
diff --git a/doc/taler-merchant-generate-payments.1 
b/doc/taler-merchant-generate-payments.1
new file mode 100644
index 0000000..a848f2c
--- /dev/null
+++ b/doc/taler-merchant-generate-payments.1
@@ -0,0 +1,50 @@
+.TH TALER\-MERCHANT\-GENERATE\-PAYMENTS 1 "Nov 4, 2017" "GNU Taler"
+
+.SH NAME
+taler\-merchant\-generate\-payments \- Generate Taler\-style fake payments.
+
+.SH SYNOPSIS
+.B taler\-merchant\-httpd
+.RI [ options ]
+.br
+
+.SH DESCRIPTION
+\fBtaler\-merchant\-generate\-payments\fP is a command line tool to populate 
your merchant database with fake Taler\-style payments.
+
+.SH OPTIONS
+.B
+.IP "\-n TIMES,  \-\-times TIMES"
+Generate TIMES payments.
+
+.B
+.IP "\-b,  \-\-remote\-bank"
+If given, use the bank specified in the configuration.  Without this option, 
the "fake bank" will be started on port 8888.
+
+.B
+.IP "\-e,  \-\-remote\-exchange"
+Do not fork any exchange, so the payments will be executed against the 
exchange specified in the configuration.
+
+.B
+.IP "\-m, \-\-remote\-merchant"
+Do not fork any merchant.  All the payments will be executed against the 
merchant backend specified in the configuration.
+
+.B
+.IP "\-c FILENAME,  \-\-config=FILENAME"
+Use the configuration and other resources for the merchant to operate from 
FILENAME.
+.B
+.IP "\-h, \-\-help"
+Print short help on options.
+.B
+.IP "\-v, \-\-version"
+Print version information.
+
+.SH SIGNALS
+.B
+.IP SIGTERM
+Sending a SIGTERM to the process will cause it to shutdown cleanly.
+
+.SH BUGS
+Report bugs by using Mantis <https://gnunet.org/bugs/> or by sending 
electronic mail to <address@hidden>
+
+.SH "SEE ALSO"
+\fBtaler\-merchant\-dbinit\fP(1), \fBtaler\-merchant\-tip\-enable\fP(1), 
\fBtaler.conf\fP(5)
diff --git a/doc/taler-merchant-tip-enable.1 b/doc/taler-merchant-tip-enable.1
deleted file mode 100644
index b61504b..0000000
--- a/doc/taler-merchant-tip-enable.1
+++ /dev/null
@@ -1,47 +0,0 @@
-.TH TALER\-MERCHANT\-TIP\-ENABLE 1 "Nov 4, 2017" "GNU Taler"
-
-.SH NAME
-taler\-merchant\-tip\-enable \- Tell Taler merchant backend about reserve 
funding for tipping
-
-.SH SYNOPSIS
-.B taler\-merchant\-tip\-enable
-.RI [ options ]
-.br
-
-.SH DESCRIPTION
-\fBtaler\-merchant\-tip\-enable\fP is a command line tool to inform the Taler 
merchant backend that a wire transfer was made to enable tipping from the 
backend.  Note that the command cannot check that the wire transfer was made 
correctly (with the correct wire subject and the specified amount), and will 
thus just trust the operator.  Enabling tipping at an instance without a wire 
transfer may cause visitors to receive unfunded tips and experience error 
messages.  You should read the man [...]
-
-.SH OPTIONS
-.B
-.IP "\-a VALUE,  \-\-amount=VALUE"
-Which amount was transferred into the reserve at the exchange.  Must be of the 
format CUR:VALUE.FRACTION.
-.B
-.IP "\-b URI,  \-\-backend=URI"
-At which URI does the backend run that we are to inform about the availability 
of funding for tipping?
-.B
-.IP "\-C UUID,  \-\-credit-uuid=UUID"
-What is the UUID of the wire transfer.  The backend will automatically detect 
if the same UUID is used repeatedly, and ignore multiple invocations.  Pass a 
UUID generated by the wire transfer of the bank.
-.B
-.IP "\-e TIMESTAMP,  \-\-expiration=TIMESTAMP"
-When does the reserve expire.  Determining this value may today require 
information from the exchange operator.
-.B
-.IP "\-h, \-\-help"
-Print short help on options.
-.B
-.IP "\-i NAME, \-\-instance=NAME"
-Name of the instance where tipping is to be enabled.  The instance must have 
the reserve (private) key and exchange URI already configured. Note that this 
command-line tool must also have access to the same configuration with the 
instance's private key in it.
-.B
-.IP "\-v, \-\-version"
-Print version information.
-.B
-
-.SH SIGNALS
-.B
-.IP SIGTERM
-Sending a SIGTERM to the process will cause it to shutdown cleanly.
-
-.SH BUGS
-Report bugs by using Mantis <https://gnunet.org/bugs/> or by sending 
electronic mail to <address@hidden>
-
-.SH "SEE ALSO"
-\fBtaler\-merchant\-httpd\fP(1), \fBtaler.conf\fP(5)
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index c73c7e6..a83a185 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -23,12 +23,13 @@ taler_merchant_httpd_SOURCES = \
   taler-merchant-httpd_pay.c taler-merchant-httpd_pay.h \
   taler-merchant-httpd_history.c taler-merchant-httpd_history.h \
   taler-merchant-httpd_tip-authorize.c taler-merchant-httpd_tip-authorize.h \
-  taler-merchant-httpd_tip-enable.c taler-merchant-httpd_tip-enable.h \
   taler-merchant-httpd_tip-pickup.c taler-merchant-httpd_tip-pickup.h \
   taler-merchant-httpd_tip-query.c taler-merchant-httpd_tip-query.h \
   taler-merchant-httpd_track-transaction.c 
taler-merchant-httpd_track-transaction.h \
   taler-merchant-httpd_track-transfer.c taler-merchant-httpd_track-transfer.h \
-  taler-merchant-httpd_refund.c taler-merchant-httpd_refund.h
+  taler-merchant-httpd_refund.c taler-merchant-httpd_refund.h \
+  taler-merchant-httpd_check-payment.c taler-merchant-httpd_check-payment.h \
+  taler-merchant-httpd_trigger-pay.c taler-merchant-httpd_trigger-pay.h
 
 
 taler_merchant_httpd_LDADD = \
diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index c9b66a4..174be2d 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  (C) 2014-2017 INRIA
+  (C) 2014-2018 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero General Public License as published by the Free 
Software
@@ -40,11 +40,12 @@
 #include "taler-merchant-httpd_track-transaction.h"
 #include "taler-merchant-httpd_track-transfer.h"
 #include "taler-merchant-httpd_tip-authorize.h"
-#include "taler-merchant-httpd_tip-enable.h"
 #include "taler-merchant-httpd_tip-pickup.h"
 #include "taler-merchant-httpd_tip-query.h"
 #include "taler-merchant-httpd_history.h"
 #include "taler-merchant-httpd_refund.h"
+#include "taler-merchant-httpd_check-payment.h"
+#include "taler-merchant-httpd_trigger-pay.h"
 
 /**
  * Backlog for listen operation on unix-domain sockets.
@@ -77,6 +78,12 @@ static long long unsigned port;
 struct GNUNET_TIME_Relative wire_transfer_delay;
 
 /**
+ * Locations from the configuration.  Mapping from
+ * label to location data.
+ */
+json_t *default_locations;
+
+/**
  * If the frontend does NOT specify a payment deadline, how long should
  * offers we make be valid by default?
  */
@@ -192,10 +199,10 @@ url_handler (void *cls,
       { "/", MHD_HTTP_METHOD_GET, "text/plain",
         "Hello, I'm a merchant's Taler backend. This HTTP server is not for 
humans.\n", 0,
         &TMH_MHD_handler_static_response, MHD_HTTP_OK },
-      { "/pay", MHD_HTTP_METHOD_POST, "application/json",
+      { "/public/pay", MHD_HTTP_METHOD_POST, "application/json",
         NULL, 0,
         &MH_handler_pay, MHD_HTTP_OK },
-      { "/pay", NULL, "text/plain",
+      { "/public/pay", NULL, "text/plain",
         "Only POST is allowed", 0,
         &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
       { "/track/transfer", MHD_HTTP_METHOD_GET, "application/json",
@@ -213,10 +220,10 @@ url_handler (void *cls,
       { "/history", MHD_HTTP_METHOD_GET, "text/plain",
         "Only GET is allowed", 0,
         &MH_handler_history, MHD_HTTP_OK},
-      { "/proposal", MHD_HTTP_METHOD_POST, "application/json",
+      { "/order", MHD_HTTP_METHOD_POST, "application/json",
         NULL, 0,
         &MH_handler_proposal_put, MHD_HTTP_OK },
-      { "/proposal", MHD_HTTP_METHOD_GET, "text/plain",
+      { "/public/proposal", MHD_HTTP_METHOD_GET, "text/plain",
         NULL, 0,
         &MH_handler_proposal_lookup, MHD_HTTP_OK},
       { "/proposal", NULL, "text/plain",
@@ -225,7 +232,7 @@ url_handler (void *cls,
       { "/refund", MHD_HTTP_METHOD_POST, "application/json",
         NULL, 0,
         &MH_handler_refund_increase, MHD_HTTP_OK},
-      { "/refund", MHD_HTTP_METHOD_GET, "text/plain",
+      { "/public/refund", MHD_HTTP_METHOD_GET, "text/plain",
         NULL, 0,
         &MH_handler_refund_lookup, MHD_HTTP_OK},
       { "/refund", NULL, "application/json",
@@ -237,21 +244,25 @@ url_handler (void *cls,
       { "/tip-authorize", NULL, "application/json",
         "Only POST is allowed", 0,
         &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED},
+      /* backwards compatibility alias for /public/tip-pickup */
       { "/tip-pickup", MHD_HTTP_METHOD_POST, "text/plain",
         NULL, 0,
         &MH_handler_tip_pickup, MHD_HTTP_OK},
-      { "/tip-pickup", NULL, "application/json",
-        "Only POST is allowed", 0,
-        &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED},
-      { "/tip-enable", MHD_HTTP_METHOD_POST, "text/plain",
+      { "/public/tip-pickup", MHD_HTTP_METHOD_POST, "text/plain",
         NULL, 0,
-        &MH_handler_tip_enable, MHD_HTTP_OK},
-      { "/tip-enable", NULL, "application/json",
+        &MH_handler_tip_pickup, MHD_HTTP_OK},
+      { "/tip-pickup", NULL, "application/json",
         "Only POST is allowed", 0,
         &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED},
       { "/tip-query", MHD_HTTP_METHOD_GET, "text/plain",
         NULL, 0,
         &MH_handler_tip_query, MHD_HTTP_OK},
+      { "/check-payment", MHD_HTTP_METHOD_GET, "text/plain",
+        NULL, 0,
+        &MH_handler_check_payment, MHD_HTTP_OK},
+      { "/public/trigger-pay", MHD_HTTP_METHOD_GET, "text/plain",
+        NULL, 0,
+        &MH_handler_trigger_pay, MHD_HTTP_OK},
       {NULL, NULL, NULL, NULL, 0, 0 }
     };
   static struct TMH_RequestHandler h404 =
@@ -261,7 +272,8 @@ url_handler (void *cls,
       &TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
     };
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Handling request for URL `%s'\n",
+              "Handling request (%s) for URL `%s'\n",
+              method,
               url);
   for (unsigned int i=0;NULL != handlers[i].url;i++)
   {
@@ -308,10 +320,20 @@ hashmap_free (void *cls,
               void *value)
 {
   struct MerchantInstance *mi = value;
+  struct WireMethod *wm;
+
+  while (NULL != (wm = (mi->wm_head)))
+  {
+    GNUNET_CONTAINER_DLL_remove (mi->wm_head,
+                                 mi->wm_tail,
+                                 wm);
+    json_decref (wm->j_wire);
+    GNUNET_free (wm);
+  }
 
-  json_decref (mi->j_wire);
   GNUNET_free (mi->id);
   GNUNET_free (mi->keyfile);
+  GNUNET_free (mi->name);
   GNUNET_free_non_null (mi->tip_exchange);
   GNUNET_free (mi);
   return GNUNET_YES;
@@ -404,6 +426,12 @@ prepare_daemon (void);
 
 
 /**
+ * Set if we should immediately #MHD_run again.
+ */
+static int triggered;
+
+
+/**
  * Call MHD to process pending requests and then go back
  * and schedule the next run.
  *
@@ -413,7 +441,10 @@ static void
 run_daemon (void *cls)
 {
   mhd_task = NULL;
-  GNUNET_assert (MHD_YES == MHD_run (mhd));
+  do {
+    triggered = 0;
+    GNUNET_assert (MHD_YES == MHD_run (mhd));
+  } while (0 != triggered);
   mhd_task = prepare_daemon ();
 }
 
@@ -427,8 +458,16 @@ run_daemon (void *cls)
 void
 TMH_trigger_daemon ()
 {
-  GNUNET_SCHEDULER_cancel (mhd_task);
-  run_daemon (NULL);
+  if (NULL != mhd_task)
+  {
+    GNUNET_SCHEDULER_cancel (mhd_task);
+    mhd_task = NULL;
+    run_daemon (NULL);
+  }
+  else
+  {
+    triggered = 1;
+  }
 }
 
 
@@ -486,30 +525,187 @@ prepare_daemon ()
 
 
 /**
+ * Callback that looks for 'merchant-location-*' sections,
+ * and populates @a default_locations.
+ *
+ * @param cls closure
+ * @section section name this callback gets
+ */
+static void
+locations_iterator_cb (void *cls,
+                       const char *section)
+{
+  static const char *keys[] = {
+    "country",
+    "city",
+    "state",
+    "region",
+    "province",
+    "zip_code",
+    "street",
+    "street_number",
+    NULL,
+  };
+  struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+  const char *prefix = "merchant-location-";
+  const char *substr = strstr (section, prefix);
+  const char *locname;
+  json_t *loc;
+
+  if ( (NULL == substr) || (substr != section) )
+    return;
+  locname = section + strlen (prefix);
+  if (0 == strlen (locname))
+    return;
+  GNUNET_assert (json_is_object (default_locations));
+
+  loc = json_object ();
+  json_object_set_new (default_locations,
+                       locname,
+                       loc);
+  for (unsigned int pos = 0; NULL != keys[pos]; pos++)
+  {
+    char *val;
+
+    if (GNUNET_OK ==
+        GNUNET_CONFIGURATION_get_value_string (cfg,
+                                               section,
+                                               keys[pos],
+                                               &val))
+    {
+      json_object_set_new (loc,
+                           keys[pos],
+                           json_string (val));
+      GNUNET_free (val);
+    }
+  }
+}
+
+
+/**
+ * Closure for the #wireformat_iterator_cb().
+ */
+struct WireFormatIteratorContext
+{
+  /**
+   * The global iteration context.
+   */
+  struct IterateInstancesCls *iic;
+
+  /**
+   * The merchant instance we are currently building.
+   */
+  struct MerchantInstance *mi;
+};
+
+
+/**
+ * Callback that looks for 'merchant-instance-wireformat-*' sections,
+ * and populates our wire method according to the data
+ *
+ * @param cls closure with a `struct WireFormatIteratorContext *`
+ * @section section name this callback gets
+ */
+static void
+wireformat_iterator_cb (void *cls,
+                        const char *section)
+{
+  struct WireFormatIteratorContext *wfic = cls;
+  struct MerchantInstance *mi = wfic->mi;
+  struct IterateInstancesCls *iic = wfic->iic;
+  char *instance_wiresection;
+  struct WireMethod *wm;
+  json_t *type;
+  char *emsg;
+
+  GNUNET_asprintf (&instance_wiresection,
+                   "merchant-instance-wireformat-%s",
+                   mi->id);
+  if (0 != strncmp (section,
+                    instance_wiresection,
+                    strlen (instance_wiresection)))
+  {
+    GNUNET_free (instance_wiresection);
+    return;
+  }
+  GNUNET_free (instance_wiresection);
+
+  wm = GNUNET_new (struct WireMethod);
+  /* FIXME: maybe use sorting to address #4939-12806? */
+  GNUNET_CONTAINER_DLL_insert (mi->wm_head,
+                               mi->wm_tail,
+                               wm);
+  wm->j_wire = iic->plugin->get_wire_details (iic->plugin->cls,
+                                              iic->config,
+                                              section);
+  if ( (NULL == (type = json_object_get (wm->j_wire,
+                                         "type"))) ||
+       (! json_is_string (type)) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Malformed wireformat: lacks type\n");
+    iic->ret |= GNUNET_SYSERR;
+    return;
+  }
+  wm->wire_method = json_string_value (type);
+
+  if (TALER_EC_NONE !=
+      iic->plugin->wire_validate (iic->plugin->cls,
+                                  wm->j_wire,
+                                  NULL,
+                                  &emsg))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Malformed wireformat: %s\n",
+                emsg);
+    GNUNET_free (emsg);
+    iic->ret |= GNUNET_SYSERR;
+    return;
+  }
+
+  if (GNUNET_OK !=
+      TALER_JSON_hash (wm->j_wire,
+                       &wm->h_wire))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to hash wireformat\n");
+    iic->ret |= GNUNET_SYSERR;
+  }
+
+#define EXTRADEBUG
+#ifdef EXTRADEBUGG
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Found wireformat instance:\n");
+  json_dumpf (wm->j_wire,
+              stdout,
+              0);
+  printf ("\n");
+#endif
+}
+
+
+/**
  * Callback that looks for 'merchant-instance-*' sections,
  * and populates accordingly each instance's data
  *
- * @param cls closure
+ * @param cls closure of type `struct IterateInstancesCls`
  * @section section name this callback gets
  */
 static void
 instances_iterator_cb (void *cls,
                        const char *section)
 {
-  char *substr;
+  struct IterateInstancesCls *iic = cls;
   char *token;
-  char *instance_wiresection;
   struct MerchantInstance *mi;
-  struct IterateInstancesCls *iic;
   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
   /* used as hashmap keys */
   struct GNUNET_HashCode h_pk;
   struct GNUNET_HashCode h_id;
-  json_t *type;
-  char *emsg;
+  const char *substr;
 
-  iic = cls;
-  substr = strstr (section, "merchant-instance-");
+  substr = strstr (section,
+                   "merchant-instance-");
 
   if ( (NULL == substr) ||
        (NULL != strstr (section,
@@ -530,6 +726,19 @@ instances_iterator_cb (void *cls,
               "Extracted token: %s\n",
               token + 1);
   mi = GNUNET_new (struct MerchantInstance);
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (iic->config,
+                                             section,
+                                             "NAME",
+                                             &mi->name))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               section,
+                               "NAME");
+    GNUNET_free (mi);
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
 
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_filename (iic->config,
@@ -540,6 +749,7 @@ instances_iterator_cb (void *cls,
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                section,
                                "KEYFILE");
+    GNUNET_free (mi->name);
     GNUNET_free (mi);
     GNUNET_SCHEDULER_shutdown ();
     return;
@@ -562,6 +772,8 @@ instances_iterator_cb (void *cls,
       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                  section,
                                  "TIP_RESERVE_PRIV_FILENAME");
+      GNUNET_free (mi->keyfile);
+      GNUNET_free (mi->name);
       GNUNET_free (mi);
       GNUNET_SCHEDULER_shutdown ();
       return;
@@ -574,6 +786,8 @@ instances_iterator_cb (void *cls,
                                  "TIP_RESERVE_PRIV_FILENAME",
                                  "Failed to read private key");
       GNUNET_free (tip_reserves);
+      GNUNET_free (mi->keyfile);
+      GNUNET_free (mi->name);
       GNUNET_free (mi);
       GNUNET_SCHEDULER_shutdown ();
       return;
@@ -593,6 +807,7 @@ instances_iterator_cb (void *cls,
   {
     GNUNET_break (0);
     GNUNET_free (mi->keyfile);
+    GNUNET_free (mi->name);
     GNUNET_free (mi);
     GNUNET_SCHEDULER_shutdown ();
     return;
@@ -603,61 +818,33 @@ instances_iterator_cb (void *cls,
   GNUNET_free (pk);
 
   mi->id = GNUNET_strdup (token + 1);
-  if (0 == strcmp ("default", mi->id))
+  if (0 == strcasecmp ("default",
+                       mi->id))
     iic->default_instance = GNUNET_YES;
 
-  GNUNET_asprintf (&instance_wiresection,
-                   "merchant-instance-wireformat-%s",
-                   mi->id);
-  mi->j_wire = iic->plugin->get_wire_details (iic->plugin->cls,
-                                              iic->config,
-                                              instance_wiresection);
-  GNUNET_free (instance_wiresection);
-  if ( (NULL == (type = json_object_get (mi->j_wire,
-                                         "type"))) ||
-       (! json_is_string (type)) )
+  /* Initialize wireformats */
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Malformed wireformat: lacks type\n");
-    iic->ret |= GNUNET_SYSERR;
-  }
-  mi->wire_method = json_string_value (type);
+    struct WireFormatIteratorContext wfic = {
+      .iic = iic,
+      .mi = mi
+    };
 
-  if (TALER_EC_NONE !=
-      iic->plugin->wire_validate (iic->plugin->cls,
-                                  mi->j_wire,
-                                  NULL,
-                                  &emsg))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Malformed wireformat: %s\n",
-                emsg);
-    GNUNET_free (emsg);
-    iic->ret |= GNUNET_SYSERR;
+    GNUNET_CONFIGURATION_iterate_sections (iic->config,
+                                           &wireformat_iterator_cb,
+                                           &wfic);
   }
 
-  if (GNUNET_OK !=
-      TALER_JSON_hash (mi->j_wire,
-                       &mi->h_wire))
+  if (NULL == mi->wm_head)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to hash wireformat\n");
+                "Failed to load wire formats for instance `%s'\n",
+                mi->id);
     iic->ret |= GNUNET_SYSERR;
   }
-#define EXTRADEBUG
-#ifdef EXTRADEBUGG
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Found wireformat instance:\n");
-              json_dumpf (mi->j_wire, stdout, 0);
-              printf ("\n");
-#endif
 
   GNUNET_CRYPTO_hash (mi->id,
                       strlen (mi->id),
                       &h_id);
-  GNUNET_CRYPTO_hash (&mi->pubkey.eddsa_pub,
-                      sizeof (struct GNUNET_CRYPTO_EddsaPublicKey),
-                      &h_pk);
   if (GNUNET_OK !=
       GNUNET_CONTAINER_multihashmap_put (by_id_map,
                                          &h_id,
@@ -668,6 +855,9 @@ instances_iterator_cb (void *cls,
                 "Failed to put an entry into the 'by_id' hashmap\n");
     iic->ret |= GNUNET_SYSERR;
   }
+  GNUNET_CRYPTO_hash (&mi->pubkey.eddsa_pub,
+                      sizeof (struct GNUNET_CRYPTO_EddsaPublicKey),
+                      &h_pk);
   if (GNUNET_OK !=
       GNUNET_CONTAINER_multihashmap_put (by_kpub_map,
                                          &h_pk,
@@ -734,6 +924,23 @@ TMH_lookup_instance_json (struct json_t *json)
 
 
 /**
+ * Iterate over locations in config in order to populate
+ * the location data.
+ *
+ * @param config configuration handle
+ * @return #GNUNET_OK if successful, #GNUNET_SYSERR upon errors
+ */
+static void
+iterate_locations (const struct GNUNET_CONFIGURATION_Handle *config)
+{
+  GNUNET_assert (NULL == default_locations);
+  default_locations = json_object ();
+  GNUNET_CONFIGURATION_iterate_sections (config,
+                                         &locations_iterator_cb,
+                                         (void *) config);
+}
+
+/**
  * Iterate over each merchant instance, in order to populate
  * each instance's own data
  *
@@ -769,7 +976,7 @@ iterate_instances (const struct GNUNET_CONFIGURATION_Handle 
*config,
   iic->plugin->library_name = lib_name;
   GNUNET_CONFIGURATION_iterate_sections (config,
                                          &instances_iterator_cb,
-                                         (void *) iic);
+                                         iic);
 
   if (GNUNET_NO == iic->default_instance)
   {
@@ -802,6 +1009,7 @@ iterate_instances (const struct 
GNUNET_CONFIGURATION_Handle *config,
   return GNUNET_SYSERR;
 }
 
+
 /**
  * Main function that will be run by the scheduler.
  *
@@ -820,6 +1028,8 @@ run (void *cls,
   char *wireformat;
   int fh;
 
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "running taler-merchant-httpd\n");
+
   result = GNUNET_SYSERR;
   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
                                  NULL);
@@ -942,6 +1152,8 @@ run (void *cls,
   }
   GNUNET_free (wireformat);
 
+  iterate_locations (config);
+
   if (NULL ==
       (db = TALER_MERCHANTDB_plugin_load (config)))
   {
@@ -1217,7 +1429,8 @@ run (void *cls,
  * @return 0 ok, 1 on error
  */
 int
-main (int argc, char *const *argv)
+main (int argc,
+      char *const *argv)
 {
   struct GNUNET_GETOPT_CommandLineOption options[] = {
 
diff --git a/src/backend/taler-merchant-httpd.h 
b/src/backend/taler-merchant-httpd.h
index 6418801..726dc62 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015 INRIA
+  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
@@ -72,7 +72,40 @@ struct IterateInstancesCls
 
 
 /**
- * Information that defines a merchant "instance". That way, a single
+ * Supported wire method.  Kept in a DLL.
+ */
+struct WireMethod
+{
+  /**
+   * Next entry in DLL.
+   */
+  struct WireMethod *next;
+
+  /**
+   * Previous entry in DLL.
+   */
+  struct WireMethod *prev;
+
+  /**
+   * Which wire method is @e j_wire using?  Points into @e j_wire.
+   */
+  const char *wire_method;
+
+  /**
+   * Wire details for this instance
+   */
+  struct json_t *j_wire;
+
+  /**
+   * Hash of our wire format details as given in #j_wire.
+   */
+  struct GNUNET_HashCode h_wire;
+
+};
+
+
+/**
+ * Information that defines a merchant "instance". Tha4673t way, a single
  * backend can account for several merchants, as used to do in donation
  * shops
  */
@@ -87,27 +120,24 @@ struct MerchantInstance
   char *id;
 
   /**
-   * File holding the merchant's private key
+   * Legal name of the merchant.
    */
-  char *keyfile;
-
-  /* NOTE: the *_wire-fields should eventually be moved into a DLL
-     once we implement #4939 */
+  char *name;
 
   /**
-   * Which wire method is @e j_wire using?
+   * File holding the merchant's private key
    */
-  const char *wire_method;
+  char *keyfile;
 
   /**
-   * Wire details for this instance
+   * Next entry in DLL.
    */
-  struct json_t *j_wire;
+  struct WireMethod *wm_head;
 
   /**
-   * Hash of our wire format details as given in #j_wire.
+   * Previous entry in DLL.
    */
-  struct GNUNET_HashCode h_wire;
+  struct WireMethod *wm_tail;
 
   /**
    * Merchant's private key
@@ -130,7 +160,6 @@ struct MerchantInstance
    * Only valid if @e tip_exchange is non-null.
    */
   struct TALER_ReservePrivateKeyP tip_reserve;
-
 };
 
 
@@ -232,9 +261,10 @@ struct TM_HandlerContext
 
 
 /**
- * Our wire format details in JSON format (with salt).
+ * Locations from the configuration.  Mapping from
+ * label to location data.
  */
-extern json_t *j_wire;
+extern json_t *default_locations;
 
 /**
  * Default maximum wire fee to assume, unless stated differently in the 
proposal
diff --git a/src/backend/taler-merchant-httpd_auditors.c 
b/src/backend/taler-merchant-httpd_auditors.c
index 81b6422..8b620e0 100644
--- a/src/backend/taler-merchant-httpd_auditors.c
+++ b/src/backend/taler-merchant-httpd_auditors.c
@@ -34,9 +34,9 @@ struct Auditor
   char *name;
 
   /**
-   * Auditor's URI.
+   * Auditor's URL.
    */
-  char *uri;
+  char *url;
 
   /**
    * Public key of the auditor.
@@ -156,12 +156,12 @@ parse_auditors (void *cls,
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_string (cfg,
                                              section,
-                                             "URI",
-                                             &auditor.uri))
+                                             "URL",
+                                             &auditor.url))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                section,
-                               "URI");
+                               "URL");
     GNUNET_free (auditor.name);
     return;
   }
@@ -175,7 +175,7 @@ parse_auditors (void *cls,
                                section,
                                "PUBLIC_KEY");
     GNUNET_free (auditor.name);
-    GNUNET_free (auditor.uri);
+    GNUNET_free (auditor.url);
     return;
   }
   if (GNUNET_OK !=
@@ -188,7 +188,7 @@ parse_auditors (void *cls,
                                "PUBLIC_KEY",
                                "valid public key");
     GNUNET_free (auditor.name);
-    GNUNET_free (auditor.uri);
+    GNUNET_free (auditor.url);
     GNUNET_free (pks);
     return;
   }
@@ -225,7 +225,7 @@ TMH_AUDITORS_init (const struct GNUNET_CONFIGURATION_Handle 
*cfg)
                                           json_pack ("{s:s, s:o, s:s}",
                                                      "name", 
auditors[cnt].name,
                                                      "auditor_pub", 
GNUNET_JSON_from_data_auto (&auditors[cnt].public_key),
-                                                     "uri", 
auditors[cnt].uri)));
+                                                     "url", 
auditors[cnt].url)));
   return nauditors;
 }
 
@@ -241,7 +241,7 @@ TMH_AUDITORS_done ()
   for (unsigned int i=0;i<nauditors;i++)
   {
     GNUNET_free (auditors[i].name);
-    GNUNET_free (auditors[i].uri);
+    GNUNET_free (auditors[i].url);
   }
   GNUNET_free_non_null (auditors);
   auditors = NULL;
diff --git a/src/backend/taler-merchant-httpd_check-payment.c 
b/src/backend/taler-merchant-httpd_check-payment.c
new file mode 100644
index 0000000..8366186
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_check-payment.c
@@ -0,0 +1,348 @@
+/*
+  This file is part of TALER
+  (C) 2017 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 backend/taler-merchant-httpd_check-payment.c
+ * @brief implementation of /check-payment handler
+ * @author Florian Dold
+ */
+#include "platform.h"
+#include <string.h>
+#include <microhttpd.h>
+#include <jansson.h>
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+#include "taler-merchant-httpd.h"
+#include "taler-merchant-httpd_mhd.h"
+#include "taler-merchant-httpd_parsing.h"
+#include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_responses.h"
+#include "taler-merchant-httpd_check-payment.h"
+
+/**
+ * Maximum number of retries for database operations.
+ */
+#define MAX_RETRIES 5
+
+
+/**
+ * Function called with information about a refund.
+ * It is responsible for summing up the refund amount.
+ *
+ * @param cls closure
+ * @param coin_pub public coin from which the refund comes from
+ * @param rtransaction_id identificator of the refund
+ * @param reason human-readable explaination of the refund
+ * @param refund_amount refund amount which is being taken from coin_pub
+ * @param refund_fee cost of this refund operation
+ */
+static void
+process_refunds_cb (void *cls,
+                    const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                    uint64_t rtransaction_id,
+                    const char *reason,
+                    const struct TALER_Amount *refund_amount,
+                    const struct TALER_Amount *refund_fee)
+{
+  struct TALER_Amount *acc_amount = cls;
+
+  GNUNET_assert (TALER_amount_add (acc_amount, acc_amount, refund_amount));
+}
+
+
+/**
+ * Manages a /check-payment call, checking the status
+ * of a payment and, if necessary, constructing the URL
+ * for a payment redirect URL.
+ *
+ * @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
+MH_handler_check_payment (struct TMH_RequestHandler *rh,
+                          struct MHD_Connection *connection,
+                          void **connection_cls,
+                          const char *upload_data,
+                          size_t *upload_data_size)
+{
+  const char *order_id;
+  const char *contract_url;
+  const char *session_id;
+  const char *session_sig_str;
+  const char *instance_str;
+  const char *resource_url;
+  char *final_contract_url = NULL;
+  char *h_contract_terms_str = NULL;
+  struct MerchantInstance *mi;
+  enum GNUNET_DB_QueryStatus qs;
+  json_t *contract_terms;
+  struct GNUNET_HashCode h_contract_terms;
+  struct TALER_Amount refund_amount;
+  char *last_session_id;
+
+  order_id = MHD_lookup_connection_value (connection,
+                                          MHD_GET_ARGUMENT_KIND,
+                                          "order_id");
+  contract_url = MHD_lookup_connection_value (connection,
+                                              MHD_GET_ARGUMENT_KIND,
+                                              "contract_url");
+  session_id = MHD_lookup_connection_value (connection,
+                                                MHD_GET_ARGUMENT_KIND,
+                                                "session_id");
+  session_sig_str = MHD_lookup_connection_value (connection,
+                                                MHD_GET_ARGUMENT_KIND,
+                                                "session_sig");
+  instance_str = MHD_lookup_connection_value (connection,
+                                              MHD_GET_ARGUMENT_KIND,
+                                              "instance");
+  resource_url = MHD_lookup_connection_value (connection,
+                                              MHD_GET_ARGUMENT_KIND,
+                                              "resource_url");
+
+  if (NULL == instance_str)
+    instance_str = "default";
+
+  mi = TMH_lookup_instance (instance_str);
+  if (NULL == mi)
+    return TMH_RESPONSE_reply_bad_request (connection,
+                                           
TALER_EC_CHECK_PAYMENT_INSTANCE_UNKNOWN,
+                                           "merchant instance unknown");
+
+  if (NULL == order_id)
+  {
+    if (NULL == contract_url)
+      return TMH_RESPONSE_reply_bad_request (connection,
+                                             TALER_EC_PARAMETER_MISSING,
+                                             "either order_id or contract_url 
must be given");
+    goto do_pay;
+    // No order_id given, redirect to a page that gives the wallet a new
+    // contract.
+  }
+
+  if (NULL == contract_url)
+  {
+    final_contract_url = TALER_url_absolute_mhd (connection, 
"/public/proposal",
+                                                 "instance", instance_str,
+                                                 "order_id", order_id,
+                                                 NULL);
+    GNUNET_assert (NULL != final_contract_url);
+  }
+  else
+  {
+    final_contract_url = GNUNET_strdup (contract_url);
+  }
+
+  if (NULL != session_id)
+  {
+    struct GNUNET_CRYPTO_EddsaSignature sig;
+    struct TALER_MerchantPaySessionSigPS mps;
+    // If the session id is given, the frontend wants us
+    // to verify the session signature.
+    if (NULL == session_sig_str)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pay session signature required but 
missing\n");
+      goto do_pay;
+    }
+
+    if (GNUNET_OK !=
+        GNUNET_STRINGS_string_to_data (session_sig_str,
+                                       strlen (session_sig_str),
+                                       &sig,
+                                       sizeof (struct 
GNUNET_CRYPTO_EddsaSignature)))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pay session signature 
malformed\n");
+      goto do_pay;
+    }
+    mps.purpose.size = htonl (sizeof (struct TALER_MerchantPaySessionSigPS));
+    mps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAY_SESSION);
+    GNUNET_assert (NULL != order_id);
+    GNUNET_assert (NULL != session_id);
+    GNUNET_CRYPTO_hash (order_id, strlen (order_id), &mps.h_order_id);
+    GNUNET_CRYPTO_hash (session_id, strlen (session_id), &mps.h_session_id);
+    if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify 
(TALER_SIGNATURE_MERCHANT_PAY_SESSION,
+                                                 &mps.purpose,
+                                                 &sig,
+                                                 &mi->pubkey.eddsa_pub))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pay session signature invalid\n");
+      goto do_pay;
+    }
+  }
+
+  GNUNET_assert (NULL != order_id);
+
+  qs = db->find_contract_terms (db->cls,
+                                &contract_terms,
+                                &last_session_id,
+                                order_id,
+                                &mi->pubkey);
+  if (0 > qs)
+  {
+    /* single, read-only SQL statements should never cause
+       serialization problems */
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+    /* Always report on hard error as well to enable diagnostics */
+    GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+    return TMH_RESPONSE_reply_internal_error (connection,
+                                             
TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
+                                             "db error fetching contract 
terms");
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+  {
+    qs = db->find_order (db->cls,
+                         &contract_terms,
+                         order_id,
+                         &mi->pubkey);
+    if (0 > qs)
+    {
+      /* single, read-only SQL statements should never cause
+         serialization problems */
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+      /* Always report on hard error as well to enable diagnostics */
+      GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+      return TMH_RESPONSE_reply_internal_error (connection,
+                                                
TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR,
+                                                "db error fetching order");
+    }
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+    {
+      return TMH_RESPONSE_reply_not_found (connection,
+                                           
TALER_EC_CHECK_PAYMENT_ORDER_ID_UNKNOWN,
+                                           "unknown order id");
+    }
+    /* Offer was not picked up yet, but we ensured that it exists */
+    goto do_pay;
+  }
+
+  GNUNET_assert (NULL != contract_terms);
+  GNUNET_assert (NULL != last_session_id);
+
+  if (GNUNET_OK !=
+      TALER_JSON_hash (contract_terms,
+                       &h_contract_terms))
+  {
+    GNUNET_break (0);
+    return TMH_RESPONSE_reply_internal_error (connection,
+                                              
TALER_EC_CHECK_PAYMENT_FAILED_COMPUTE_PROPOSAL_HASH,
+                                              "Failed to hash proposal");
+  }
+
+  h_contract_terms_str = GNUNET_STRINGS_data_to_string_alloc 
(&h_contract_terms,
+                                                              sizeof (struct 
GNUNET_HashCode));
+
+
+  /* Check if paid */
+  {
+    json_t *xcontract_terms = NULL;
+    
+    qs = db->find_paid_contract_terms_from_hash (db->cls,
+                                                 &xcontract_terms,
+                                                 &h_contract_terms,
+                                                 &mi->pubkey);
+    if (0 > qs)
+    {
+      /* Always report on hard error as well to enable diagnostics */
+      GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+      GNUNET_free_non_null (h_contract_terms_str);
+      return TMH_RESPONSE_reply_internal_error (connection,
+                                                
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
+                                                "Merchant database error");
+    }
+    if (0 == qs)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "not paid yet\n");
+      goto do_pay;
+    }
+    GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
+    GNUNET_assert (NULL != xcontract_terms);
+    json_decref (xcontract_terms);
+  }
+
+  {
+    struct TALER_Amount amount;
+    struct GNUNET_JSON_Specification spec[] = {
+      TALER_JSON_spec_amount ("amount", &amount),
+      GNUNET_JSON_spec_end ()
+    };
+    if (GNUNET_OK != GNUNET_JSON_parse (contract_terms, spec, NULL, NULL))
+      return TMH_RESPONSE_reply_internal_error (connection,
+                                                
TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
+                                                "Merchant database error 
(contract terms corrupted)");
+    TALER_amount_get_zero (amount.currency, &refund_amount);
+  }
+
+  for (unsigned int i=0;i<MAX_RETRIES;i++)
+  {
+    qs = db->get_refunds_from_contract_terms_hash (db->cls,
+                                                  &mi->pubkey,
+                                                  &h_contract_terms,
+                                                  &process_refunds_cb,
+                                                  &refund_amount);
+    if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
+      break;
+  }
+  if (0 > qs)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Database hard error on refunds_from_contract_terms_hash 
lookup: %s\n",
+                GNUNET_h2s (&h_contract_terms));
+    return TMH_RESPONSE_reply_internal_error (connection,
+                                              
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
+                                              "Merchant database error");
+  }
+
+  GNUNET_free_non_null (h_contract_terms_str);
+
+  {
+    int refunded = (0 != refund_amount.value) || (0 != refund_amount.fraction);
+    int res;
+    res = TMH_RESPONSE_reply_json_pack (connection,
+                                        MHD_HTTP_OK,
+                                        "{s:o s:b, s:b, s:o, s:s}",
+                                        "contract_terms", contract_terms,
+                                        "paid", 1,
+                                        "refunded", refunded,
+                                        "refund_amount", 
TALER_JSON_from_amount (&refund_amount),
+                                        "last_session_id", last_session_id);
+    return res;
+  }
+
+do_pay:
+  {
+    char *url = TALER_url_absolute_mhd (connection, "public/trigger-pay",
+                                        "contract_url", final_contract_url,
+                                        "session_id", session_id,
+                                        "resource_url", resource_url,
+                                        "h_contract_terms", 
h_contract_terms_str,
+                                        NULL);
+    GNUNET_assert (NULL != url);
+    int ret = TMH_RESPONSE_reply_json_pack (connection,
+                                            MHD_HTTP_OK,
+                                            "{s:s, s:b}",
+                                            "payment_redirect_url",
+                                            url,
+                                            "paid",
+                                            0);
+    GNUNET_free_non_null (h_contract_terms_str);
+    GNUNET_free_non_null (final_contract_url);
+    json_decref (contract_terms);
+    GNUNET_free (url);
+    return ret;
+  }
+}
diff --git a/src/backend/taler-merchant-httpd_tip-enable.h 
b/src/backend/taler-merchant-httpd_check-payment.h
similarity index 63%
rename from src/backend/taler-merchant-httpd_tip-enable.h
rename to src/backend/taler-merchant-httpd_check-payment.h
index 2981ef4..14c2a67 100644
--- a/src/backend/taler-merchant-httpd_tip-enable.h
+++ b/src/backend/taler-merchant-httpd_check-payment.h
@@ -14,18 +14,20 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file backend/taler-merchant-httpd_tip-enable.h
- * @brief headers for /tip-enable handler
+ * @file backend/taler-merchant-httpd_tip-query.h
+ * @brief headers for /tip-query handler
  * @author Christian Grothoff
+ * @author Florian Dold
  */
-#ifndef TALER_MERCHANT_HTTPD_TIP_ENABLE_H
-#define TALER_MERCHANT_HTTPD_TIP_ENABLE_H
+#ifndef TALER_MERCHANT_HTTPD_CHECK_PAYMENT_H
+#define TALER_MERCHANT_HTTPD_CHECK_PAYMENT_H
 #include <microhttpd.h>
 #include "taler-merchant-httpd.h"
 
 /**
- * Manages a /tip-enable call, storing information about the
- * reserve.
+ * Manages a /check-payment call, checking the status
+ * of a payment and, if necessary, constructing the URL
+ * for a payment redirect URL.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
@@ -35,10 +37,10 @@
  * @return MHD result code
  */
 int
-MH_handler_tip_enable (struct TMH_RequestHandler *rh,
-                       struct MHD_Connection *connection,
-                       void **connection_cls,
-                       const char *upload_data,
-                       size_t *upload_data_size);
+MH_handler_check_payment (struct TMH_RequestHandler *rh,
+                          struct MHD_Connection *connection,
+                          void **connection_cls,
+                          const char *upload_data,
+                          size_t *upload_data_size);
 
 #endif
diff --git a/src/backend/taler-merchant-httpd_exchanges.c 
b/src/backend/taler-merchant-httpd_exchanges.c
index 4484689..c82e831 100644
--- a/src/backend/taler-merchant-httpd_exchanges.c
+++ b/src/backend/taler-merchant-httpd_exchanges.c
@@ -153,9 +153,9 @@ struct Exchange
   struct TMH_EXCHANGES_FindOperation *fo_tail;
 
   /**
-   * (base) URI of the exchange.
+   * (base) URL of the exchange.
    */
-  char *uri;
+  char *url;
 
   /**
    * A connection to this exchange
@@ -279,14 +279,14 @@ retry_exchange (void *cls)
   exchange->retry_task = NULL;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Connecting to exchange exchange %s in retry_exchange\n",
-              exchange->uri);
+              exchange->url);
   if (NULL != exchange->conn)
   {
     TALER_EXCHANGE_disconnect (exchange->conn);
     exchange->conn = NULL;
   }
   exchange->conn = TALER_EXCHANGE_connect (merchant_curl_ctx,
-                                           exchange->uri,
+                                           exchange->url,
                                            &keys_mgmt_cb,
                                            exchange,
                                            TALER_EXCHANGE_OPTION_END);
@@ -565,7 +565,7 @@ handle_wire_data (void *cls,
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Failed to obtain /wire details from `%s': %d\n",
-                exchange->uri,
+                exchange->url,
                 ec);
     return;
   }
@@ -685,13 +685,13 @@ keys_mgmt_cb (void *cls,
       /* Give up, log hard error. */
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  "Exchange `%s' runs an incompatible more recent version of 
the Taler protocol. Will not retry. This client may need to be updated.\n",
-                 exchange->uri);
+                 exchange->url);
       return;
     }
     exchange->retry_delay = RETRY_BACKOFF (exchange->retry_delay);
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Failed to fetch /keys from `%s', retrying in %s\n",
-                exchange->uri,
+                exchange->url,
                 GNUNET_STRINGS_relative_time_to_string (exchange->retry_delay,
                                                         GNUNET_YES));
     GNUNET_assert (NULL == exchange->retry_task);
@@ -710,7 +710,7 @@ keys_mgmt_cb (void *cls,
       once = 1;
       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                  "Exchange `%s' runs a more recent version of the Taler 
protocol. You may want to update this client.\n",
-                 exchange->uri);
+                 exchange->url);
     }
   }
   expire = TALER_EXCHANGE_check_keys_current (exchange->conn,
@@ -775,7 +775,7 @@ return_result (void *cls)
  * to the exchange, or if it is not acceptable, @a fc is called with
  * NULL for the exchange.
  *
- * @param chosen_exchange URI of the exchange we would like to talk to
+ * @param chosen_exchange URL of the exchange we would like to talk to
  * @param wire_method the wire method we will use with @a chosen_exchange, 
NULL for none
  * @param fc function to call with the handles for the exchange
  * @param fc_cls closure for @a fc
@@ -804,9 +804,9 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
   /* Check if the exchange is known */
   for (exchange = exchange_head; NULL != exchange; exchange = exchange->next)
     /* test it by checking public key --- FIXME: hostname or public key!?
-       Should probably be URI, not hostname anyway! */
+       Should probably be URL, not hostname anyway! */
   {
-    if (0 == strcmp (exchange->uri,
+    if (0 == strcmp (exchange->url,
                      chosen_exchange))
     {
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -816,13 +816,13 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
     }
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Comparing chosen exchange url '%s' with known url '%s'.\n",
-                chosen_exchange, exchange->uri);
+                chosen_exchange, exchange->url);
   }
   if (NULL == exchange)
   {
     /* This is a new exchange */
     exchange = GNUNET_new (struct Exchange);
-    exchange->uri = GNUNET_strdup (chosen_exchange);
+    exchange->url = GNUNET_strdup (chosen_exchange);
     exchange->pending = GNUNET_YES;
     GNUNET_CONTAINER_DLL_insert (exchange_head,
                                  exchange_tail,
@@ -916,7 +916,7 @@ accept_exchanges (void *cls,
                   const char *section)
 {
   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
-  char *uri;
+  char *url;
   char *mks;
   struct Exchange *exchange;
 
@@ -927,16 +927,16 @@ accept_exchanges (void *cls,
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_string (cfg,
                                              section,
-                                             "URI",
-                                             &uri))
+                                             "URL",
+                                             &url))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                section,
-                               "URI");
+                               "URL");
     return;
   }
   exchange = GNUNET_new (struct Exchange);
-  exchange->uri = uri;
+  exchange->url = url;
   if (GNUNET_OK ==
       GNUNET_CONFIGURATION_get_value_string (cfg,
                                              section,
@@ -1010,7 +1010,7 @@ TMH_EXCHANGES_init (const struct 
GNUNET_CONFIGURATION_Handle *cfg)
     if (GNUNET_YES != exchange->trusted)
       continue;
     j_exchange = json_pack ("{s:s, s:o}",
-                            "url", exchange->uri,
+                            "url", exchange->url,
                             "master_pub", GNUNET_JSON_from_data_auto 
(&exchange->master_pub));
     GNUNET_assert (0 ==
                    json_array_append_new (trusted_exchanges,
@@ -1070,7 +1070,7 @@ TMH_EXCHANGES_done ()
       GNUNET_SCHEDULER_cancel (exchange->retry_task);
       exchange->retry_task = NULL;
     }
-    GNUNET_free (exchange->uri);
+    GNUNET_free (exchange->url);
     GNUNET_free (exchange);
   }
   GNUNET_CURL_fini (merchant_curl_ctx);
diff --git a/src/backend/taler-merchant-httpd_exchanges.h 
b/src/backend/taler-merchant-httpd_exchanges.h
index 6b763ae..7523e84 100644
--- a/src/backend/taler-merchant-httpd_exchanges.h
+++ b/src/backend/taler-merchant-httpd_exchanges.h
@@ -81,7 +81,7 @@ struct TMH_EXCHANGES_FindOperation;
  * to the exchange, or if it is not acceptable, @a fc is called with
  * NULL for the exchange.
  *
- * @param chosen_exchange URI of the exchange we would like to talk to
+ * @param chosen_exchange URL of the exchange we would like to talk to
  * @param wire_method the wire method we will use with @a chosen_exchange, 
NULL for none
  * @param fc function to call with the handles for the exchange
  * @param fc_cls closure for @a fc
diff --git a/src/backend/taler-merchant-httpd_pay.c 
b/src/backend/taler-merchant-httpd_pay.c
index ff8d59c..b112edf 100644
--- a/src/backend/taler-merchant-httpd_pay.c
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -40,9 +40,9 @@
 #define PAY_TIMEOUT (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 
30))
 
 /**
- * How often do we retry the simple INSERT database transaction?
+ * How often do we retry the (complex!) database transaction?
  */
-#define MAX_RETRIES 3
+#define MAX_RETRIES 5
 
 /**
  * Information we keep for an individual call to the /pay handler.
@@ -67,6 +67,11 @@ struct DepositConfirmation
   struct TALER_EXCHANGE_DepositHandle *dh;
 
   /**
+   * URL of the exchange that issued this coin.
+   */
+  char *exchange_url;
+
+  /**
    * Denomination of this coin.
    */
   struct TALER_DenominationPublicKey denom;
@@ -88,6 +93,11 @@ struct DepositConfirmation
   struct TALER_Amount refund_fee;
 
   /**
+   * Wire fee charged by the exchange of this coin.
+   */
+  struct TALER_Amount wire_fee;
+
+  /**
    * Public key of the coin.
    */
   struct TALER_CoinSpendPublicKeyP coin_pub;
@@ -113,6 +123,11 @@ struct DepositConfirmation
    */
   int found_in_db;
 
+  /**
+   * #GNUNET_YES if this coin was refunded.
+   */
+  int refunded;
+
 };
 
 
@@ -154,6 +169,12 @@ struct PayContext
   struct MerchantInstance *mi;
 
   /**
+   * What wire method (of the @e mi) was selected by the wallet?
+   * Set in #parse_pay().
+   */
+  struct WireMethod *wm;
+
+  /**
    * Proposal data for the proposal that is being
    * payed for in this context.
    */
@@ -185,19 +206,14 @@ struct PayContext
   struct TMH_EXCHANGES_FindOperation *fo;
 
   /**
-   * Placeholder for #TMH_PARSE_post_json() to keep its internal state.
-   */
-  void *json_parse_context;
-
-  /**
-   * Exchange URI given in @e root.
+   * URL of the exchange used for the last @e fo.
    */
-  char *chosen_exchange;
+  const char *current_exchange;
 
   /**
-   * Transaction ID given in @e root.
+   * Placeholder for #TMH_PARSE_post_json() to keep its internal state.
    */
-  const char *order_id;
+  void *json_parse_context;
 
   /**
    * Hashed proposal.
@@ -244,6 +260,26 @@ struct PayContext
   struct TALER_Amount amount;
 
   /**
+   * Considering all the coins with the "found_in_db" flag
+   * set, what is the total amount we were so far paid on
+   * this contract?
+   */
+  struct TALER_Amount total_paid;
+
+  /**
+   * Considering all the coins with the "found_in_db" flag
+   * set, what is the total amount we had to pay in deposit
+   * fees so far on this contract?
+   */
+  struct TALER_Amount total_fees_paid;
+
+  /**
+   * Considering all the coins with the "found_in_db" flag
+   * set, what is the total amount we already refunded?
+   */
+  struct TALER_Amount total_refunded;
+
+  /**
    * Wire transfer deadline. How soon would the merchant like the
    * wire transfer to be executed? (Can be given by the frontend
    * or be determined by our configuration via #wire_transfer_delay.)
@@ -281,6 +317,11 @@ struct PayContext
   unsigned int coins_cnt;
 
   /**
+   * How often have we retried the 'main' transaction?
+   */
+  unsigned int retry_counter;
+
+  /**
    * Number of transactions still pending.  Initially set to
    * @e coins_cnt, decremented on each transaction that
    * successfully finished.
@@ -288,6 +329,14 @@ struct PayContext
   unsigned int pending;
 
   /**
+   * Number of transactions still pending for the currently selected
+   * exchange.  Initially set to the number of coins started at the
+   * exchange, decremented on each transaction that successfully
+   * finished.  Once it hits zero, we pick the next exchange.
+   */
+  unsigned int pending_at_ce;
+
+  /**
    * HTTP status code to use for the reply, i.e 200 for "OK".
    * Special value UINT_MAX is used to indicate hard errors
    * (no reply, return #MHD_NO).
@@ -295,20 +344,28 @@ struct PayContext
   unsigned int response_code;
 
   /**
-   * #GNUNET_NO if the transaction is not in our database,
-   * #GNUNET_YES if the transaction is known to our database,
-   * #GNUNET_SYSERR if the transaction ID is used for a different
-   * transaction in our database.
-   */
-  int transaction_exists;
-
-  /**
    * #GNUNET_NO if the @e connection was not suspended,
    * #GNUNET_YES if the @e connection was suspended,
    * #GNUNET_SYSERR if @e connection was resumed to as
    * part of #MH_force_pc_resume during shutdown.
    */
   int suspended;
+
+  /**
+   * Which operational mode is the /pay request made in?
+   */
+  enum { PC_MODE_PAY, PC_MODE_ABORT_REFUND } mode;
+
+  /**
+   * Optional session id given in @e root.
+   * NULL if not given.
+   */
+  char *session_id;
+
+  /**
+   * Transaction ID given in @e root.
+   */
+  char *order_id;
 };
 
 
@@ -409,171 +466,84 @@ sign_success_response (struct PayContext *pc)
   json_t *refunds;
   enum TALER_ErrorCode ec;
   const char *errmsg;
-
-  refunds = TM_get_refund_json (pc->mi, &pc->h_contract_terms, &ec, &errmsg);
-
-  if (NULL == refunds) {
-    return TMH_RESPONSE_make_internal_error (ec, errmsg);
-  }
-
   struct GNUNET_CRYPTO_EddsaSignature sig;
   struct PaymentResponsePS mr;
+  json_t *resp;
+  struct MHD_Response *mret;
+
+  refunds = TM_get_refund_json (pc->mi,
+                               &pc->h_contract_terms,
+                               &ec,
+                               &errmsg);
+
+  if (NULL == refunds)
+    return TMH_RESPONSE_make_error (ec,
+                                   errmsg);
 
   mr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK);
   mr.purpose.size = htonl (sizeof (mr));
   mr.h_contract_terms = pc->h_contract_terms;
-
   GNUNET_CRYPTO_eddsa_sign (&pc->mi->privkey.eddsa_priv,
                             &mr.purpose,
                            &sig);
+  resp = json_pack ("{s:O, s:o, s:o, s:o}",
+                    "contract_terms",
+                    pc->contract_terms,
+                    "sig",
+                    GNUNET_JSON_from_data_auto (&sig),
+                    "h_contract_terms",
+                    GNUNET_JSON_from_data (&pc->h_contract_terms,
+                                           sizeof (struct GNUNET_HashCode)),
+                    "refund_permissions",
+                    refunds);
+
+  if (NULL != pc->session_id)
+  {
+    struct GNUNET_CRYPTO_EddsaSignature session_sig;
+    struct TALER_MerchantPaySessionSigPS mps;
+
+    GNUNET_assert (NULL != pc->order_id);
+    mps.purpose.size = htonl (sizeof (struct TALER_MerchantPaySessionSigPS));
+    mps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAY_SESSION);
+    GNUNET_CRYPTO_hash (pc->order_id,
+                        strlen (pc->order_id),
+                        &mps.h_order_id);
+    GNUNET_CRYPTO_hash (pc->session_id,
+                        strlen (pc->session_id),
+                        &mps.h_session_id);
+
+    GNUNET_CRYPTO_eddsa_sign (&pc->mi->privkey.eddsa_priv,
+                              &mps.purpose,
+                              &session_sig);
+    json_object_set_new (resp,
+                         "session_sig",
+                         GNUNET_JSON_from_data_auto (&session_sig));
+  }
 
-  return TMH_RESPONSE_make_json_pack ("{s:O, s:o, s:o, s:o}",
-                                      "contract_terms",
-                                      pc->contract_terms,
-                                      "sig",
-                                      GNUNET_JSON_from_data_auto (&sig),
-                                      "h_contract_terms",
-                                      GNUNET_JSON_from_data 
(&pc->h_contract_terms,
-                                                             sizeof (struct 
GNUNET_HashCode)),
-                                      "refund_permissions",
-                                      refunds);
+  mret = TMH_RESPONSE_make_json (resp);
+  json_decref (resp);
+  return mret;
 }
 
 
 /**
- * Callback to handle a deposit permission's response.
+ * Resume payment processing with an error.
  *
- * @param cls a `struct DepositConfirmation` (i.e. a pointer
- *   into the global array of confirmations and an index for this call
- *   in that array). That way, the last executed callback can detect
- *   that no other confirmations are on the way, and can pack a response
- *   for the wallet
- * @param http_status HTTP response code, #MHD_HTTP_OK
- *   (200) for successful deposit; 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 sign_key which key did the exchange use to sign the @a proof
- * @param proof the received JSON reply,
- *   should be kept as proof (and, in case of errors, be forwarded to
- *   the customer)
+ * @param pc operation to resume
+ * @param http_status http status code to return
+ * @param ec taler error code to return
+ * @param msg human readable error message
  */
 static void
-deposit_cb (void *cls,
-            unsigned int http_status,
-           enum TALER_ErrorCode ec,
-            const struct TALER_ExchangePublicKeyP *sign_key,
-            const json_t *proof)
+resume_pay_with_error (struct PayContext *pc,
+                      unsigned int http_status,
+                      enum TALER_ErrorCode ec,
+                      const char *msg)
 {
-  struct DepositConfirmation *dc = cls;
-  struct PayContext *pc = dc->pc;
-  enum GNUNET_DB_QueryStatus qs;
-
-  dc->dh = NULL;
-  GNUNET_assert (GNUNET_YES == pc->suspended);
-  pc->pending--;
-  if (MHD_HTTP_OK != http_status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-               "Deposit operation failed with HTTP code %u\n",
-               http_status);
-    /* Transaction failed; stop all other ongoing deposits */
-    abort_deposit (pc);
-    db->rollback (db->cls);
-
-    if (NULL == proof)
-    {
-      /* We can't do anything meaningful here, the exchange did something 
wrong */
-      resume_pay_with_response (pc,
-                                MHD_HTTP_SERVICE_UNAVAILABLE,
-                                TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:I, 
s:s}",
-                                                             "error", 
"exchange failed",
-                                                            "code", 
(json_int_t) TALER_EC_PAY_EXCHANGE_FAILED,
-                                                            "exchange-code", 
(json_int_t) ec,
-                                                            
"exchange-http-status", (json_int_t) http_status,
-                                                             "hint", "The 
exchange provided an unexpected response"));
-    }
-    else
-    {
-      /* Forward error, adding the "coin_pub" for which the
-         error was being generated */
-      json_t *eproof;
-
-      eproof = json_copy ((json_t *) proof);
-      json_object_set_new (eproof,
-                           "coin_pub",
-                           GNUNET_JSON_from_data_auto (&dc->coin_pub));
-      resume_pay_with_response (pc,
-                                http_status,
-                                TMH_RESPONSE_make_json (eproof));
-      json_decref (eproof);
-    }
-    return;
-  }
-  /* store result to DB */
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Storing successful payment for h_contract_terms `%s' and 
merchant `%s'\n",
-              GNUNET_h2s (&pc->h_contract_terms),
-              TALER_B2S (&pc->mi->pubkey));
-  for (unsigned int i=0;i<MAX_RETRIES;i++)
-  {
-    qs = db->store_deposit (db->cls,
-                           &pc->h_contract_terms,
-                           &pc->mi->pubkey,
-                           &dc->coin_pub,
-                           &dc->amount_with_fee,
-                           &dc->deposit_fee,
-                           &dc->refund_fee,
-                           sign_key,
-                           proof);
-    if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
-      break;
-  }
-  if (0 > qs)
-  {
-    /* Special report if retries insufficient */
-    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
-    /* Always report on hard error as well to enable diagnostics */
-    GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-    /* internal error */
-    abort_deposit (pc);
-    db->rollback (db->cls);
-    /* Forward error including 'proof' for the body */
-    resume_pay_with_response (pc,
-                              MHD_HTTP_INTERNAL_SERVER_ERROR,
-                              TMH_RESPONSE_make_internal_error 
(TALER_EC_PAY_DB_STORE_PAY_ERROR,
-                                                               "Merchant 
database error"));
-    return;
-  }
-
-  if (0 != pc->pending)
-    return; /* still more to do */
-
-  qs = db->mark_proposal_paid (db->cls,
-                               &pc->h_contract_terms,
-                               &pc->mi->pubkey);
-  if (0 > qs)
-  {
-    abort_deposit (pc);
-    db->rollback (db->cls);
-    resume_pay_with_response (pc,
-                              MHD_HTTP_INTERNAL_SERVER_ERROR,
-                              TMH_RESPONSE_make_internal_error 
(TALER_EC_PAY_DB_STORE_PAYMENTS_ERROR,
-                                                                "Merchant 
database error: could not mark proposal as 'paid'"));
-    return;
-  }
-  qs = db->commit (db->cls);
-  if (0 > qs)
-  {
-    abort_deposit (pc);
-    resume_pay_with_response (pc,
-                              MHD_HTTP_INTERNAL_SERVER_ERROR,
-                              TMH_RESPONSE_make_internal_error 
(TALER_EC_PAY_DB_STORE_PAYMENTS_ERROR,
-                                                                "Merchant 
database error: could not commit"));
-    return;
-  }
   resume_pay_with_response (pc,
-                            MHD_HTTP_OK,
-                            sign_success_response (pc));
+                           http_status,
+                           TMH_RESPONSE_make_error (ec,
+                                                    msg));
 }
 
 
@@ -612,6 +582,7 @@ pay_context_cleanup (struct TM_HandlerContext *hc)
       GNUNET_CRYPTO_rsa_signature_free (dc->ub_sig.rsa_signature);
       dc->ub_sig.rsa_signature = NULL;
     }
+    GNUNET_free_non_null (dc->exchange_url);
   }
   GNUNET_free_non_null (pc->dc);
   if (NULL != pc->fo)
@@ -624,16 +595,13 @@ pay_context_cleanup (struct TM_HandlerContext *hc)
     MHD_destroy_response (pc->response);
     pc->response = NULL;
   }
-  if (NULL != pc->chosen_exchange)
-  {
-    GNUNET_free (pc->chosen_exchange);
-    pc->chosen_exchange = NULL;
-  }
   if (NULL != pc->contract_terms)
   {
     json_decref (pc->contract_terms);
     pc->contract_terms = NULL;
   }
+  GNUNET_free_non_null (pc->order_id);
+  GNUNET_free_non_null (pc->session_id);
   GNUNET_CONTAINER_DLL_remove (pc_head,
                                pc_tail,
                                pc);
@@ -642,159 +610,44 @@ pay_context_cleanup (struct TM_HandlerContext *hc)
 
 
 /**
- * Check if the existing transaction matches our transaction.
- * Update `transaction_exists` accordingly.
+ * Check whether the amount paid is sufficient to cover
+ * the contract.
  *
- * @param cls closure with the `struct PayContext`
- * @param merchant_pub merchant's public key
- * @param exchange_uri URI of the exchange
- * @param h_contract_terms hashed proposal data
- * @param h_xwire hash of our wire details
- * @param timestamp time of the confirmation
- * @param refund refund deadline
- * @param total_amount total amount we receive for the contract after fees
+ * @param pc payment context to check
+ * @return taler error code, #TALER_EC_NONE if amount is sufficient
  */
-static void
-check_transaction_exists (void *cls,
-                         const struct TALER_MerchantPublicKeyP *merchant_pub,
-                         const char *exchange_uri,
-                         const struct GNUNET_HashCode *h_contract_terms,
-                         const struct GNUNET_HashCode *h_xwire,
-                         struct GNUNET_TIME_Absolute timestamp,
-                         struct GNUNET_TIME_Absolute refund,
-                         const struct TALER_Amount *total_amount)
+static enum TALER_ErrorCode
+check_payment_sufficient (struct PayContext *pc)
 {
-  struct PayContext *pc = cls;
-
-  if ( (0 == memcmp (h_contract_terms,
-                    &pc->h_contract_terms,
-                     sizeof (struct GNUNET_HashCode))) &&
-       (0 == memcmp (h_xwire,
-                    &pc->mi->h_wire,
-                    sizeof (struct GNUNET_HashCode))) &&
-       (timestamp.abs_value_us == pc->timestamp.abs_value_us) &&
-       (refund.abs_value_us == pc->refund_deadline.abs_value_us) &&
-       (0 == TALER_amount_cmp (total_amount,
-                              &pc->amount) ) )
-  {
-    pc->transaction_exists = GNUNET_YES;
-  }
-  else
-  {
-    GNUNET_break_op (0);
-    pc->transaction_exists = GNUNET_SYSERR;
-  }
-}
-
-
-/**
- * Function called with the result of our exchange lookup.
- *
- * @param cls the `struct PayContext`
- * @param mh NULL if exchange was not found to be acceptable
- * @param wire_fee current applicable fee for dealing with @a mh, NULL if not 
available
- * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config
- */
-static void
-process_pay_with_exchange (void *cls,
-                           struct TALER_EXCHANGE_Handle *mh,
-                           const struct TALER_Amount *wire_fee,
-                           int exchange_trusted)
-{
-  struct PayContext *pc = cls;
   struct TALER_Amount acc_fee;
   struct TALER_Amount acc_amount;
   struct TALER_Amount wire_fee_delta;
   struct TALER_Amount wire_fee_customer_contribution;
-  const struct TALER_EXCHANGE_Keys *keys;
-  enum GNUNET_DB_QueryStatus qs;
+  struct TALER_Amount total_wire_fee;
 
-  pc->fo = NULL;
-  GNUNET_assert (GNUNET_YES == pc->suspended);
-  if (NULL == mh)
-  {
-    /* The exchange on offer is not in the set of our (trusted)
-       exchanges.  Reject the payment. */
-    GNUNET_break_op (0);
-    resume_pay_with_response (pc,
-                              MHD_HTTP_PRECONDITION_FAILED,
-                              TMH_RESPONSE_make_external_error 
(TALER_EC_PAY_EXCHANGE_REJECTED,
-                                                               "exchange not 
supported"));
-    return;
-  }
-  pc->mh = mh;
-  keys = TALER_EXCHANGE_get_keys (mh);
-  if (NULL == keys)
-  {
-    GNUNET_break (0);
-    resume_pay_with_response (pc,
-                              MHD_HTTP_INTERNAL_SERVER_ERROR,
-                              TMH_RESPONSE_make_internal_error 
(TALER_EC_PAY_EXCHANGE_KEYS_FAILURE,
-                                                               "no keys"));
-    return;
-  }
+  if (0 == pc->coins_cnt)
+    return TALER_EC_PAY_PAYMENT_INSUFFICIENT;
 
-  /* Total up the fees and the value of the deposited coins! */
-  GNUNET_assert (0 != pc->coins_cnt);
-  for (unsigned int i=0;i<pc->coins_cnt;i++)
+  acc_fee = pc->dc[0].deposit_fee;
+  total_wire_fee = pc->dc[0].wire_fee;
+  acc_amount = pc->dc[0].amount_with_fee;
+  for (unsigned int i=1;i<pc->coins_cnt;i++)
   {
     struct DepositConfirmation *dc = &pc->dc[i];
-    const struct TALER_EXCHANGE_DenomPublicKey *denom_details;
 
-    denom_details = TALER_EXCHANGE_get_denomination_key (keys,
-                                                        &dc->denom);
-    if (NULL == denom_details)
-    {
-      GNUNET_break_op (0);
-      resume_pay_with_response (pc,
-                                MHD_HTTP_BAD_REQUEST,
-                                TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:o, 
s:o}",
-                                                             "error", 
"denomination not found",
-                                                            "code", 
TALER_EC_PAY_DENOMINATION_KEY_NOT_FOUND,
-                                                             "denom_pub", 
GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key),
-                                                             "exchange_keys", 
TALER_EXCHANGE_get_keys_raw (mh)));
-      return;
-    }
-    if (GNUNET_OK !=
-        TMH_AUDITORS_check_dk (mh,
-                               denom_details,
-                               exchange_trusted))
+    GNUNET_assert (GNUNET_YES == dc->found_in_db);
+    if ( (GNUNET_OK !=
+         TALER_amount_add (&acc_fee,
+                           &dc->deposit_fee,
+                           &acc_fee)) ||
+        (GNUNET_OK !=
+         TALER_amount_add (&acc_amount,
+                           &dc->amount_with_fee,
+                           &acc_amount)) )
     {
       GNUNET_break_op (0);
-      resume_pay_with_response (pc,
-                                MHD_HTTP_BAD_REQUEST,
-                                TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:o}",
-                                                             "error", "invalid 
denomination",
-                                                            "code", 
(json_int_t) TALER_EC_PAY_DENOMINATION_KEY_AUDITOR_FAILURE,
-                                                             "denom_pub", 
GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key)));
-      return;
-    }
-    dc->deposit_fee = denom_details->fee_deposit;
-    dc->refund_fee = denom_details->fee_refund;
-    if (0 == i)
-    {
-      acc_fee = denom_details->fee_deposit;
-      acc_amount = dc->amount_with_fee;
-    }
-    else
-    {
-      if ( (GNUNET_OK !=
-           TALER_amount_add (&acc_fee,
-                             &denom_details->fee_deposit,
-                             &acc_fee)) ||
-          (GNUNET_OK !=
-           TALER_amount_add (&acc_amount,
-                             &dc->amount_with_fee,
-                             &acc_amount)) )
-      {
-       GNUNET_break_op (0);
-       /* Overflow in these amounts? Very strange. */
-       resume_pay_with_response (pc,
-                                 MHD_HTTP_BAD_REQUEST,
-                                 TMH_RESPONSE_make_internal_error 
(TALER_EC_PAY_AMOUNT_OVERFLOW,
-                                                                   "Overflow 
adding up amounts"));
-       return;
-      }
+      /* Overflow in these amounts? Very strange. */
+      return TALER_EC_PAY_AMOUNT_OVERFLOW;
     }
     if (1 ==
        TALER_amount_cmp (&dc->deposit_fee,
@@ -802,33 +655,47 @@ process_pay_with_exchange (void *cls,
     {
       GNUNET_break_op (0);
       /* fee higher than residual coin value, makes no sense. */
-      resume_pay_with_response (pc,
-                               MHD_HTTP_BAD_REQUEST,
-                                TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:o, 
s:o}",
-                                                             "hint", "fee 
higher than coin value",
-                                                            "code", 
(json_int_t) TALER_EC_PAY_FEES_EXCEED_PAYMENT,
-                                                             "f" /* FIXME */, 
TALER_JSON_from_amount (&dc->amount_with_fee),
-                                                             "fee_deposit", 
TALER_JSON_from_amount (&denom_details->fee_deposit)));
-      return;
+      return TALER_EC_PAY_FEES_EXCEED_PAYMENT;
+    }
+
+    /* If exchange differs, add wire fee */
+    {
+      int new_exchange = GNUNET_YES;
+
+      for (unsigned int j=0;j<i;j++)
+       if (0 == strcasecmp (dc->exchange_url,
+                            pc->dc[j].exchange_url))
+       {
+         new_exchange = GNUNET_NO;
+         break;
+       }
+      if (GNUNET_YES == new_exchange)
+      {
+       if (GNUNET_OK !=
+           TALER_amount_add (&total_wire_fee,
+                             &total_wire_fee,
+                             &dc->wire_fee))
+       {
+         GNUNET_break_op (0);
+         return TALER_EC_PAY_EXCHANGE_REJECTED;
+       }
+      }
     }
   }
 
-  /* Now compare exchange wire fee compared to what we are willing to pay */
+  /* Now compare exchange wire fee compared to what we are willing to
+     pay */
   if (GNUNET_YES !=
-      TALER_amount_cmp_currency (wire_fee,
+      TALER_amount_cmp_currency (&total_wire_fee,
                                  &pc->max_wire_fee))
   {
     GNUNET_break (0);
-    resume_pay_with_response (pc,
-                              MHD_HTTP_INTERNAL_SERVER_ERROR,
-                              TMH_RESPONSE_make_internal_error 
(TALER_EC_PAY_WIRE_FEE_CURRENCY_MISSMATCH,
-                                                                "wire_fee"));
-    return;
+    return TALER_EC_PAY_WIRE_FEE_CURRENCY_MISSMATCH;
   }
 
   if (GNUNET_OK ==
       TALER_amount_subtract (&wire_fee_delta,
-                             wire_fee,
+                             &total_wire_fee,
                              &pc->max_wire_fee))
   {
     /* Actual wire fee is indeed higher than our maximum, compute
@@ -840,11 +707,17 @@ process_pay_with_exchange (void *cls,
   else
   {
     GNUNET_assert (GNUNET_OK ==
-                   TALER_amount_get_zero (wire_fee->currency,
+                   TALER_amount_get_zero (total_wire_fee.currency,
                                           &wire_fee_customer_contribution));
 
   }
 
+  /* Do not count any refunds towards the payment */
+  GNUNET_assert (GNUNET_SYSERR !=
+                TALER_amount_subtract (&acc_amount,
+                                       &acc_amount,
+                                       &pc->total_refunded));
+
   /* Now check that the customer paid enough for the full contract */
   if (-1 == TALER_amount_cmp (&pc->max_fee,
                               &acc_fee))
@@ -865,11 +738,7 @@ process_pay_with_exchange (void *cls,
                           &pc->amount))
     {
       GNUNET_break (0);
-      resume_pay_with_response (pc,
-                                MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                TMH_RESPONSE_make_internal_error 
(TALER_EC_PAY_AMOUNT_OVERFLOW,
-                                                                 "overflow"));
-      return;
+      return TALER_EC_PAY_AMOUNT_OVERFLOW;
     }
     /* add wire fee contribution to the total */
     if (GNUNET_OK ==
@@ -882,11 +751,7 @@ process_pay_with_exchange (void *cls,
                                 &total_needed))
     {
       GNUNET_break_op (0);
-      resume_pay_with_response (pc,
-                                MHD_HTTP_METHOD_NOT_ACCEPTABLE,
-                                TMH_RESPONSE_make_external_error 
(TALER_EC_PAY_PAYMENT_INSUFFICIENT_DUE_TO_FEES,
-                                                                 "insufficient 
funds (including excessive exchange fees to be covered by customer)"));
-      return;
+      return TALER_EC_PAY_PAYMENT_INSUFFICIENT_DUE_TO_FEES;
     }
   }
   else
@@ -916,11 +781,7 @@ process_pay_with_exchange (void *cls,
                                  &wire_fee_customer_contribution))
       {
         GNUNET_break_op (0);
-        resume_pay_with_response (pc,
-                                  MHD_HTTP_METHOD_NOT_ACCEPTABLE,
-                                  TMH_RESPONSE_make_external_error 
(TALER_EC_PAY_PAYMENT_INSUFFICIENT_DUE_TO_FEES,
-                                                                    
"insufficient funds (including excessive exchange fees to be covered by 
customer)"));
-        return;
+        return TALER_EC_PAY_PAYMENT_INSUFFICIENT_DUE_TO_FEES;
       }
     }
 
@@ -929,163 +790,309 @@ process_pay_with_exchange (void *cls,
                                 &pc->amount))
     {
       GNUNET_break_op (0);
-      resume_pay_with_response (pc,
-                                MHD_HTTP_METHOD_NOT_ACCEPTABLE,
-                                TMH_RESPONSE_make_external_error 
(TALER_EC_PAY_PAYMENT_INSUFFICIENT,
-                                                                 "insufficient 
funds"));
-      return;
+      return TALER_EC_PAY_PAYMENT_INSUFFICIENT;
     }
   }
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Exchange and fee structure OK. Initiating deposit operation for 
coins\n");
+  return TALER_EC_NONE;
+}
 
 
-  if (GNUNET_OK != db->start (db->cls))
+/**
+ * Generate full error response based on the @a ec
+ *
+ * @param pc context for which to generate the error
+ * @param ec error code identifying the issue
+ */
+static void
+generate_error_response (struct PayContext *pc,
+                        enum TALER_ErrorCode ec)
+{
+  switch (ec)
   {
+  case TALER_EC_PAY_AMOUNT_OVERFLOW:
+    resume_pay_with_error (pc,
+                          MHD_HTTP_BAD_REQUEST,
+                          ec,
+                          "Overflow adding up amounts");
+    break;
+  case TALER_EC_PAY_FEES_EXCEED_PAYMENT:
+    resume_pay_with_error (pc,
+                          MHD_HTTP_BAD_REQUEST,
+                          ec,
+                          "Deposit fees exceed coin's contribution");
+    break;
+  case TALER_EC_PAY_PAYMENT_INSUFFICIENT_DUE_TO_FEES:
+    resume_pay_with_error (pc,
+                          MHD_HTTP_METHOD_NOT_ACCEPTABLE,
+                          ec,
+                          "insufficient funds (including excessive exchange 
fees to be covered by customer)");
+    break;
+  case TALER_EC_PAY_PAYMENT_INSUFFICIENT:
+    resume_pay_with_error (pc,
+                          MHD_HTTP_METHOD_NOT_ACCEPTABLE,
+                          ec,
+                          "insufficient funds");
+    break;
+  case TALER_EC_PAY_WIRE_FEE_CURRENCY_MISSMATCH:
+    resume_pay_with_error (pc,
+                          MHD_HTTP_INTERNAL_SERVER_ERROR,
+                          ec,
+                          "wire_fee currency does not match");
+    break;
+  case TALER_EC_PAY_EXCHANGE_REJECTED:
+    resume_pay_with_error (pc,
+                          MHD_HTTP_PRECONDITION_FAILED,
+                          ec,
+                          "exchange charges incompatible wire fee");
+    break;
+  default:
+    resume_pay_with_error (pc,
+                          MHD_HTTP_INTERNAL_SERVER_ERROR,
+                          ec,
+                          "unexpected error code");
     GNUNET_break (0);
-    resume_pay_with_response (pc,
-                              MHD_HTTP_INTERNAL_SERVER_ERROR,
-                              TMH_RESPONSE_make_json_pack ("{s:s, s:I}",
-                                                           "hint", "Merchant 
database error: could not start transaction",
-                                                           "code", 
(json_int_t) TALER_EC_PAY_DB_STORE_PAYMENTS_ERROR));
-    return;
+    break;
   }
+}
+
+
+/**
+ * Find the exchange we need to talk to for the next
+ * pending deposit permission.
+ *
+ * @param pc payment context we are processing
+ */
+static void
+find_next_exchange (struct PayContext *pc);
+
+
+/**
+ * Begin of the DB transaction.  If required (from
+ * soft/serialization errors), the transaction can be
+ * restarted here.
+ *
+ * @param pc payment context to transact
+ */
+static void
+begin_transaction (struct PayContext *pc);
+
+
+/**
+ * Callback to handle a deposit permission's response.
+ *
+ * @param cls a `struct DepositConfirmation` (i.e. a pointer
+ *   into the global array of confirmations and an index for this call
+ *   in that array). That way, the last executed callback can detect
+ *   that no other confirmations are on the way, and can pack a response
+ *   for the wallet
+ * @param http_status HTTP response code, #MHD_HTTP_OK
+ *   (200) for successful deposit; 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 sign_key which key did the exchange use to sign the @a proof
+ * @param proof the received JSON reply,
+ *   should be kept as proof (and, in case of errors, be forwarded to
+ *   the customer)
+ */
+static void
+deposit_cb (void *cls,
+            unsigned int http_status,
+           enum TALER_ErrorCode ec,
+            const struct TALER_ExchangePublicKeyP *sign_key,
+            const json_t *proof)
+{
+  struct DepositConfirmation *dc = cls;
+  struct PayContext *pc = dc->pc;
+  enum GNUNET_DB_QueryStatus qs;
+
+  dc->dh = NULL;
+  GNUNET_assert (GNUNET_YES == pc->suspended);
+  pc->pending_at_ce--;
+  if (MHD_HTTP_OK != http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               "Deposit operation failed with HTTP code %u\n",
+               http_status);
+    /* Transaction failed; stop all other ongoing deposits */
+    abort_deposit (pc);
+    db->rollback (db->cls);
 
-  /* Check if transaction is already known, if not store it. */
-  /* FIXME: What if transaction exists, with a failed payment at
-     a different exchange? */
-  qs = db->find_transaction (db->cls,
-                            &pc->h_contract_terms,
-                            &pc->mi->pubkey,
-                            &check_transaction_exists,
-                            pc);
+    if (NULL == proof)
+    {
+      /* We can't do anything meaningful here, the exchange did something 
wrong */
+      resume_pay_with_response (pc,
+                                MHD_HTTP_SERVICE_UNAVAILABLE,
+                                TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:I, 
s:s}",
+                                                             "error", 
"exchange failed",
+                                                            "code", 
(json_int_t) TALER_EC_PAY_EXCHANGE_FAILED,
+                                                            "exchange-code", 
(json_int_t) ec,
+                                                            
"exchange-http-status", (json_int_t) http_status,
+                                                             "hint", "The 
exchange provided an unexpected response"));
+    }
+    else
+    {
+      /* Forward error, adding the "coin_pub" for which the
+         error was being generated */
+      json_t *eproof;
+
+      eproof = json_copy ((json_t *) proof);
+      json_object_set_new (eproof,
+                           "coin_pub",
+                           GNUNET_JSON_from_data_auto (&dc->coin_pub));
+      resume_pay_with_response (pc,
+                                http_status,
+                                TMH_RESPONSE_make_json (eproof));
+      json_decref (eproof);
+    }
+    return;
+  }
+  /* store result to DB */
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Storing successful payment for h_contract_terms `%s' and 
merchant `%s'\n",
+              GNUNET_h2s (&pc->h_contract_terms),
+              TALER_B2S (&pc->mi->pubkey));
+  /* NOTE: not run in any transaction block, simply as a
+     transaction by itself! */
+  qs = db->store_deposit (db->cls,
+                         &pc->h_contract_terms,
+                         &pc->mi->pubkey,
+                         &dc->coin_pub,
+                         dc->exchange_url,
+                         &dc->amount_with_fee,
+                         &dc->deposit_fee,
+                         &dc->refund_fee,
+                         &dc->wire_fee,
+                         sign_key,
+                         proof);
   if (0 > qs)
   {
-    /* single, read-only SQL statements should never cause
-       serialization problems */
-    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+    /* Special report if retries insufficient */
+    abort_deposit (pc);
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+    {
+      begin_transaction (pc);
+      return;
+    }
     /* Always report on hard error as well to enable diagnostics */
     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-    /* FIXME: factor common logic of these calls into a function! */
-    resume_pay_with_response (pc,
-                              MHD_HTTP_INTERNAL_SERVER_ERROR,
-                              TMH_RESPONSE_make_json_pack ("{s:I, s:s}",
-                                                           "code", 
(json_int_t) TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
-                                                           "hint", "Merchant 
database error"));
+    /* Forward error including 'proof' for the body */
+    resume_pay_with_error (pc,
+                          MHD_HTTP_INTERNAL_SERVER_ERROR,
+                          TALER_EC_PAY_DB_STORE_PAY_ERROR,
+                          "Merchant database error");
     return;
   }
-  if (GNUNET_SYSERR == pc->transaction_exists)
+  dc->found_in_db = GNUNET_YES;
+  pc->pending--;
+
+  if (0 != pc->pending_at_ce)
+    return; /* still more to do with current exchange */
+  find_next_exchange (pc);
+}
+
+
+/**
+ * Function called with the result of our exchange lookup.
+ *
+ * @param cls the `struct PayContext`
+ * @param mh NULL if exchange was not found to be acceptable
+ * @param wire_fee current applicable fee for dealing with @a mh, NULL if not 
available
+ * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config
+ */
+static void
+process_pay_with_exchange (void *cls,
+                           struct TALER_EXCHANGE_Handle *mh,
+                           const struct TALER_Amount *wire_fee,
+                           int exchange_trusted)
+{
+  struct PayContext *pc = cls;
+  const struct TALER_EXCHANGE_Keys *keys;
+
+  pc->fo = NULL;
+  GNUNET_assert (GNUNET_YES == pc->suspended);
+  if (NULL == mh)
+  {
+    /* The exchange on offer is not in the set of our (trusted)
+       exchanges.  Reject the payment. */
+    GNUNET_break_op (0);
+    resume_pay_with_error (pc,
+                          MHD_HTTP_PRECONDITION_FAILED,
+                          TALER_EC_PAY_EXCHANGE_REJECTED,
+                          "exchange not supported");
+    return;
+  }
+  pc->mh = mh;
+  keys = TALER_EXCHANGE_get_keys (mh);
+  if (NULL == keys)
   {
     GNUNET_break (0);
-    /* FIXME: factor common logic of these calls into a function! */
-    resume_pay_with_response (pc,
-                              MHD_HTTP_BAD_REQUEST,
-                              TMH_RESPONSE_make_json_pack ("{s:I, s:s}",
-                                                           "code", 
(json_int_t) TALER_EC_PAY_DB_TRANSACTION_ID_CONFLICT,
-                                                           "hint", 
"Transaction ID reused with different transaction details"));
+    resume_pay_with_error (pc,
+                          MHD_HTTP_INTERNAL_SERVER_ERROR,
+                          TALER_EC_PAY_EXCHANGE_KEYS_FAILURE,
+                          "no keys");
     return;
   }
-  if (GNUNET_NO == pc->transaction_exists)
-  {
-    struct GNUNET_TIME_Absolute now;
-    enum GNUNET_DB_QueryStatus qs_st;
-
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Dealing with new transaction `%s'\n",
-                GNUNET_h2s (&pc->h_contract_terms));
-
-    now = GNUNET_TIME_absolute_get ();
-    if (now.abs_value_us > pc->pay_deadline.abs_value_us)
-    {
-      /* Time expired, we don't accept this payment now! */
-      const char *pd_str;
-
-      pd_str = GNUNET_STRINGS_absolute_time_to_string (pc->pay_deadline);
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Attempt to pay coins for expired contract. Deadline: 
`%s'\n",
-                 pd_str);
-      resume_pay_with_response (pc,
-                                MHD_HTTP_BAD_REQUEST,
-                                TMH_RESPONSE_make_json_pack ("{s:I, s:s}",
-                                                             "code", 
(json_int_t) TALER_EC_PAY_OFFER_EXPIRED,
-                                                             "hint", "The time 
to pay for this contract has expired."));
-      return;
-    }
-
-    qs_st = db->store_transaction (db->cls,
-                                   &pc->h_contract_terms,
-                                   &pc->mi->pubkey,
-                                   pc->chosen_exchange,
-                                   &pc->mi->h_wire,
-                                   pc->timestamp,
-                                   pc->refund_deadline,
-                                   &pc->amount);
-    /* Only retry if SOFT error occurred.  Exit in case of OK or HARD failure 
*/
-    if (GNUNET_DB_STATUS_SOFT_ERROR == qs_st)
-    {
-      db->rollback (db->cls);
-      GNUNET_break (0);  // FIXME: implement proper retries!
-      resume_pay_with_response (pc,
-                                MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                TMH_RESPONSE_make_json_pack ("{s:I, s:s}",
-                                                             "code", 
(json_int_t) TALER_EC_PAY_DB_STORE_TRANSACTION_ERROR,
-                                                             "hint", "Soft 
merchant database error: retries not implemented"));
-      return;
-    }
-    /* Exit in case of HARD failure */
-    if (GNUNET_DB_STATUS_HARD_ERROR == qs_st)
-    {
-      GNUNET_break (0);
-      db->rollback (db->cls);
-      resume_pay_with_response (pc,
-                                MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                TMH_RESPONSE_make_json_pack ("{s:I, s:s}",
-                                                             "code", 
(json_int_t) TALER_EC_PAY_DB_STORE_TRANSACTION_ERROR,
-                                                             "hint", "Merchant 
database error: hard error while storing transaction"));
-    }
-
-    /**
-     * Break if we couldn't modify one, and only one line; this
-     * includes hard errors.
-     */
-    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs_st)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Unexpected query status %d while storing /pay 
transaction!\n",
-                  (int) qs_st);
-      resume_pay_with_response (pc,
-                                MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                TMH_RESPONSE_make_json_pack ("{s:I, s:s}",
-                                                             "code", 
(json_int_t) TALER_EC_PAY_DB_STORE_TRANSACTION_ERROR,
-                                                             "hint", "Merchant 
database error: failed to store transaction"));
-      return;
-    }
-  } /* end of if (GNUNET_NO == pc->transaction_esists) */
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Found transaction data for proposal `%s' of merchant `%s', 
initiating deposits\n",
               GNUNET_h2s (&pc->h_contract_terms),
               TALER_B2S (&pc->mi->pubkey));
 
-
-  /* Initiate /deposit operation for all coins */
+  /* Initiate /deposit operation for all coins of
+     the current exchange (!) */
+  GNUNET_assert (0 == pc->pending_at_ce);
   for (unsigned int i=0;i<pc->coins_cnt;i++)
   {
     struct DepositConfirmation *dc = &pc->dc[i];
+    const struct TALER_EXCHANGE_DenomPublicKey *denom_details;
 
     if (GNUNET_YES == dc->found_in_db)
       continue;
-    GNUNET_assert (NULL != pc->mi->j_wire);
+    if (0 != strcmp (dc->exchange_url,
+                    pc->current_exchange))
+      continue;
+    denom_details = TALER_EXCHANGE_get_denomination_key (keys,
+                                                        &dc->denom);
+    if (NULL == denom_details)
+    {
+      GNUNET_break_op (0);
+      resume_pay_with_response (pc,
+                                MHD_HTTP_BAD_REQUEST,
+                                TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:o, 
s:o}",
+                                                             "error", 
"denomination not found",
+                                                            "code", 
TALER_EC_PAY_DENOMINATION_KEY_NOT_FOUND,
+                                                             "denom_pub", 
GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key),
+                                                             "exchange_keys", 
TALER_EXCHANGE_get_keys_raw (mh)));
+      return;
+    }
+    if (GNUNET_OK !=
+        TMH_AUDITORS_check_dk (mh,
+                               denom_details,
+                               exchange_trusted))
+    {
+      GNUNET_break_op (0);
+      resume_pay_with_response (pc,
+                                MHD_HTTP_BAD_REQUEST,
+                                TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:o}",
+                                                             "error", "invalid 
denomination",
+                                                            "code", 
(json_int_t) TALER_EC_PAY_DENOMINATION_KEY_AUDITOR_FAILURE,
+                                                             "denom_pub", 
GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key)));
+      return;
+    }
+    dc->deposit_fee = denom_details->fee_deposit;
+    dc->refund_fee = denom_details->fee_refund;
+    dc->wire_fee = *wire_fee;
+
+    GNUNET_assert (NULL != pc->wm);
+    GNUNET_assert (NULL != pc->wm->j_wire);
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Timing for this payment, wire_deadline: %llu, 
refund_deadline: %llu\n",
                 (unsigned long long) pc->wire_transfer_deadline.abs_value_us,
                 (unsigned long long) pc->refund_deadline.abs_value_us);
-
     dc->dh = TALER_EXCHANGE_deposit (mh,
                                      &dc->amount_with_fee,
                                      pc->wire_transfer_deadline,
-                                     pc->mi->j_wire,
+                                     pc->wm->j_wire,
                                      &pc->h_contract_terms,
                                      &dc->coin_pub,
                                      &dc->ub_sig,
@@ -1101,7 +1108,6 @@ process_pay_with_exchange (void *cls,
       /* Signature was invalid.  If the exchange was unavailable,
        * we'd get that information in the callback. */
       GNUNET_break_op (0);
-      db->rollback (db->cls);
       resume_pay_with_response (pc,
                                 MHD_HTTP_UNAUTHORIZED,
                                 TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:i}",
@@ -1111,11 +1117,51 @@ process_pay_with_exchange (void *cls,
                                                              "coin_idx", i));
       return;
     }
+    pc->pending_at_ce++;
   }
 }
 
 
 /**
+ * Find the exchange we need to talk to for the next
+ * pending deposit permission.
+ *
+ * @param pc payment context we are processing
+ */
+static void
+find_next_exchange (struct PayContext *pc)
+{
+  for (unsigned int i=0;i<pc->coins_cnt;i++)
+  {
+    struct DepositConfirmation *dc = &pc->dc[i];
+
+    if (GNUNET_YES != dc->found_in_db)
+    {
+      pc->current_exchange = dc->exchange_url;
+      pc->fo = TMH_EXCHANGES_find_exchange (pc->current_exchange,
+                                           pc->wm->wire_method,
+                                           &process_pay_with_exchange,
+                                           pc);
+      if (NULL == pc->fo)
+      {
+       GNUNET_break (0);
+       resume_pay_with_error (pc,
+                              MHD_HTTP_INTERNAL_SERVER_ERROR,
+                              TALER_EC_PAY_EXCHANGE_FAILED,
+                              "Failed to lookup exchange by URL");
+       return;
+      }
+      return;
+    }
+  }
+  pc->current_exchange = NULL;
+  /* We are done with all the HTTP requests, go back and try
+     the 'big' database transaction! (It should work now!) */
+  begin_transaction (pc);
+}
+
+
+/**
  * Handle a timeout for the processing of the pay request.
  *
  * @param cls our `struct PayContext`
@@ -1134,10 +1180,10 @@ handle_pay_timeout (void *cls)
     TMH_EXCHANGES_find_exchange_cancel (pc->fo);
     pc->fo = NULL;
   }
-  resume_pay_with_response (pc,
-                            MHD_HTTP_SERVICE_UNAVAILABLE,
-                            TMH_RESPONSE_make_internal_error 
(TALER_EC_PAY_EXCHANGE_TIMEOUT,
-                                                             "exchange not 
reachable"));
+  resume_pay_with_error (pc,
+                        MHD_HTTP_SERVICE_UNAVAILABLE,
+                        TALER_EC_PAY_EXCHANGE_TIMEOUT,
+                        "exchange not reachable");
 }
 
 
@@ -1147,18 +1193,22 @@ handle_pay_timeout (void *cls)
  * @param cls closure
  * @param h_contract_terms hashed proposal data
  * @param coin_pub public key of the coin
+ * @param exchange_url URL of the exchange that issued @a coin_pub
  * @param amount_with_fee amount the exchange will deposit for this coin
  * @param deposit_fee fee the exchange will charge for this coin
  * @param refund_fee fee the exchange will charge for refunding this coin
+ * @param wire_fee wire fee the exchange of this coin charges
  * @param exchange_proof proof from exchange that coin was accepted
  */
 static void
 check_coin_paid (void *cls,
                  const struct GNUNET_HashCode *h_contract_terms,
                  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                const char *exchange_url,
                  const struct TALER_Amount *amount_with_fee,
                  const struct TALER_Amount *deposit_fee,
                  const struct TALER_Amount *refund_fee,
+                const struct TALER_Amount *wire_fee,
                  const json_t *exchange_proof)
 {
   struct PayContext *pc = cls;
@@ -1173,6 +1223,9 @@ check_coin_paid (void *cls,
   for (unsigned int i=0;i<pc->coins_cnt;i++)
   {
     struct DepositConfirmation *dc = &pc->dc[i];
+
+    if (GNUNET_YES == dc->found_in_db)
+      continue; /* processed earlier */
     /* Get matching coin from results*/
     if ( (0 != memcmp (coin_pub,
                        &dc->coin_pub,
@@ -1180,16 +1233,35 @@ check_coin_paid (void *cls,
          (0 != TALER_amount_cmp (amount_with_fee,
                                  &dc->amount_with_fee)) )
       continue;
-
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Coin (%s) already found in our DB.\n",
-                TALER_b2s (coin_pub, sizeof (*coin_pub)));
-
+                TALER_b2s (coin_pub,
+                          sizeof (*coin_pub)));
+    if (GNUNET_OK !=
+       TALER_amount_add (&pc->total_paid,
+                         &pc->total_paid,
+                         amount_with_fee))
+    {
+      /* We accepted this coin for payment on this contract before,
+        and now we can't even add the amount!? */
+      GNUNET_break (0);
+      continue;
+    }
+    if (GNUNET_OK !=
+       TALER_amount_add (&pc->total_fees_paid,
+                         &pc->total_fees_paid,
+                         deposit_fee))
+    {
+      /* We accepted this coin for payment on this contract before,
+        and now we can't even add the amount!? */
+      GNUNET_break (0);
+      continue;
+    }
+    dc->deposit_fee = *deposit_fee;
+    dc->refund_fee = *refund_fee;
+    dc->wire_fee = *wire_fee;
+    dc->amount_with_fee = *amount_with_fee;
     dc->found_in_db = GNUNET_YES;
-    /**
-     * What happens if a (mad) wallet sends new coins on a
-     * contract that it already paid for?
-     */
     pc->pending--;
   }
 }
@@ -1216,18 +1288,24 @@ parse_pay (struct MHD_Connection *connection,
   json_t *coin;
   json_t *merchant;
   unsigned int coins_index;
-  const char *chosen_exchange;
   const char *order_id;
+  const char *mode;
   struct TALER_MerchantPublicKeyP merchant_pub;
   int res;
+  char *last_session_id;
   struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_json ("coins", &coins),
-    GNUNET_JSON_spec_string ("exchange", &chosen_exchange),
-    GNUNET_JSON_spec_string ("order_id", &order_id),
-    GNUNET_JSON_spec_fixed_auto ("merchant_pub", &merchant_pub),
+    GNUNET_JSON_spec_string ("mode",
+                            &mode),
+    GNUNET_JSON_spec_json ("coins",
+                          &coins),
+    GNUNET_JSON_spec_string ("order_id",
+                            &order_id),
+    GNUNET_JSON_spec_fixed_auto ("merchant_pub",
+                                &merchant_pub),
     GNUNET_JSON_spec_end()
   };
   enum GNUNET_DB_QueryStatus qs;
+  const char *session_id;
 
   res = TMH_PARSE_json_data (connection,
                              root,
@@ -1237,8 +1315,16 @@ parse_pay (struct MHD_Connection *connection,
     GNUNET_break (0);
     return res;
   }
+
+  session_id = json_string_value (json_object_get (root,
+                                                   "session_id"));
+  if (NULL != session_id)
+    pc->session_id = GNUNET_strdup (session_id);
+  pc->order_id = GNUNET_strdup (order_id);
+  GNUNET_assert (NULL == pc->contract_terms);
   qs = db->find_contract_terms (db->cls,
                                 &pc->contract_terms,
+                                &last_session_id,
                                 order_id,
                                 &merchant_pub);
   if (0 > qs)
@@ -1267,6 +1353,8 @@ parse_pay (struct MHD_Connection *connection,
     return GNUNET_NO;
   }
 
+  GNUNET_free (last_session_id);
+
   if (GNUNET_OK !=
       TALER_JSON_hash (pc->contract_terms,
                        &pc->h_contract_terms))
@@ -1301,6 +1389,11 @@ parse_pay (struct MHD_Connection *connection,
     }
     return GNUNET_NO;
   }
+  if (0 != strcasecmp ("abort-refund",
+                      mode))
+    pc->mode = PC_MODE_PAY;
+  else
+    pc->mode = PC_MODE_ABORT_REFUND;
   pc->mi = TMH_lookup_instance_json (merchant);
   if (NULL == pc->mi)
   {
@@ -1319,12 +1412,9 @@ parse_pay (struct MHD_Connection *connection,
   }
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "/pay: picked instance %s with key %s\n",
-              pc->mi->id,
-              GNUNET_STRINGS_data_to_string_alloc (&pc->mi->pubkey,
-                                                   sizeof (pc->mi->pubkey)));
+              "/pay: picked instance %s\n",
+              pc->mi->id);
 
-  pc->chosen_exchange = GNUNET_strdup (chosen_exchange);
   {
     struct GNUNET_JSON_Specification espec[] = {
       GNUNET_JSON_spec_absolute_time ("refund_deadline",
@@ -1366,17 +1456,24 @@ parse_pay (struct MHD_Connection *connection,
     }
   }
 
-  /* NOTE: In the future, iterate over all wire hashes
-     available to a given instance here! (#4939) */
-  if (0 != memcmp (&pc->h_wire,
-                   &pc->mi->h_wire,
-                   sizeof (struct GNUNET_HashCode)))
+  /* find wire method */
   {
-    GNUNET_break (0);
-    GNUNET_JSON_parse_free (spec);
-    return TMH_RESPONSE_reply_internal_error (connection,
-                                              TALER_EC_PAY_WIRE_HASH_UNKNOWN,
-                                              "Did not find matching wire 
details");
+    struct WireMethod *wm;
+
+    wm = pc->mi->wm_head;
+    while (0 != memcmp (&pc->h_wire,
+                        &wm->h_wire,
+                        sizeof (struct GNUNET_HashCode)))
+      wm = wm->next;
+    if (NULL == wm)
+    {
+      GNUNET_break (0);
+      GNUNET_JSON_parse_free (spec);
+      return TMH_RESPONSE_reply_internal_error (connection,
+                                                TALER_EC_PAY_WIRE_HASH_UNKNOWN,
+                                                "Did not find matching wire 
details");
+    }
+    pc->wm = wm;
   }
 
   /* parse optional details */
@@ -1449,12 +1546,20 @@ parse_pay (struct MHD_Connection *connection,
   json_array_foreach (coins, coins_index, coin)
   {
     struct DepositConfirmation *dc = &pc->dc[coins_index];
+    const char *exchange_url;
     struct GNUNET_JSON_Specification spec[] = {
-      TALER_JSON_spec_denomination_public_key ("denom_pub", &dc->denom),
-      TALER_JSON_spec_amount ("f" /* FIXME */, &dc->amount_with_fee),
-      GNUNET_JSON_spec_fixed_auto ("coin_pub", &dc->coin_pub),
-      TALER_JSON_spec_denomination_signature ("ub_sig", &dc->ub_sig),
-      GNUNET_JSON_spec_fixed_auto ("coin_sig", &dc->coin_sig),
+      TALER_JSON_spec_denomination_public_key ("denom_pub",
+                                              &dc->denom),
+      TALER_JSON_spec_amount ("contribution",
+                             &dc->amount_with_fee),
+      GNUNET_JSON_spec_string ("exchange_url",
+                              &exchange_url),
+      GNUNET_JSON_spec_fixed_auto ("coin_pub",
+                                  &dc->coin_pub),
+      TALER_JSON_spec_denomination_signature ("ub_sig",
+                                             &dc->ub_sig),
+      GNUNET_JSON_spec_fixed_auto ("coin_sig",
+                                  &dc->coin_sig),
       GNUNET_JSON_spec_end()
     };
 
@@ -1465,9 +1570,9 @@ parse_pay (struct MHD_Connection *connection,
     {
       GNUNET_JSON_parse_free (spec);
       GNUNET_break_op (0);
-      return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+      return res;
     }
-
+    dc->exchange_url = GNUNET_strdup (exchange_url);
     dc->index = coins_index;
     dc->pc = pc;
   }
@@ -1478,29 +1583,94 @@ parse_pay (struct MHD_Connection *connection,
 
 
 /**
- * Process a payment for a proposal.
+ * Function called with information about a refund.
+ * Check if this coin was claimed by the wallet for the
+ * transaction, and if so add the refunded amount to the
+ * pc's "total_refunded" amount.
  *
- * @param connection HTTP connection we are receiving payment on
- * @param root JSON upload with payment data
- * @param pc context we use to handle the payment
- * @return value to return to MHD (#MHD_NO to drop connection,
- *         #MHD_YES to keep handling it)
+ * @param cls closure with a `struct PayContext`
+ * @param coin_pub public coin from which the refund comes from
+ * @param rtransaction_id identificator of the refund
+ * @param reason human-readable explaination of the refund
+ * @param refund_amount refund amount which is being taken from coin_pub
+ * @param refund_fee cost of this refund operation
  */
-static int
-handler_pay_json (struct MHD_Connection *connection,
-                  const json_t *root,
-                  struct PayContext *pc)
+static void
+check_coin_refunded (void *cls,
+                    const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                    uint64_t rtransaction_id,
+                    const char *reason,
+                    const struct TALER_Amount *refund_amount,
+                    const struct TALER_Amount *refund_fee)
+{
+  struct PayContext *pc = cls;
+
+  for (unsigned int i=0;i<pc->coins_cnt;i++)
+  {
+    struct DepositConfirmation *dc = &pc->dc[i];
+
+    /* Get matching coin from results*/
+    if (0 != memcmp (coin_pub,
+                    &dc->coin_pub,
+                    sizeof (struct TALER_CoinSpendPublicKeyP)))
+    {
+      dc->refunded = GNUNET_YES;
+      GNUNET_break (GNUNET_OK ==
+                   TALER_amount_add (&pc->total_refunded,
+                                     &pc->total_refunded,
+                                     refund_amount));
+    }
+  }
+}
+
+
+/**
+ * Begin of the DB transaction.  If required (from
+ * soft/serialization errors), the transaction can be
+ * restarted here.
+ *
+ * @param pc payment context to transact
+ */
+static void
+begin_transaction (struct PayContext *pc)
 {
-  int ret;
   enum GNUNET_DB_QueryStatus qs;
 
-  ret = parse_pay (connection,
-                   root,
-                   pc);
-  if (GNUNET_OK != ret)
-    return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+  /* Avoid re-trying transactions on soft errors forever! */
+  if (pc->retry_counter++ > MAX_RETRIES)
+  {
+    GNUNET_break (0);
+    resume_pay_with_response (pc,
+                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                             TMH_RESPONSE_make_json_pack ("{s:I, s:s}",
+                                                          "code", (json_int_t) 
TALER_EC_PAY_DB_STORE_TRANSACTION_ERROR,
+                                                          "hint", "Soft 
merchant database error: retry counter exceeded"));
+    return;
+  }
+
+  GNUNET_assert (GNUNET_YES == pc->suspended);
 
-  /* Check if this payment attempt has already succeeded */
+  /* First, try to see if we have all we need already done */
+  if (GNUNET_OK != db->start (db->cls))
+  {
+    GNUNET_break (0);
+    resume_pay_with_error (pc,
+                          MHD_HTTP_INTERNAL_SERVER_ERROR,
+                          TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
+                          "Merchant database error (could not start 
transaction)");
+    return;
+  }
+  GNUNET_break (GNUNET_OK ==
+               TALER_amount_get_zero (pc->amount.currency,
+                                      &pc->total_paid));
+  GNUNET_break (GNUNET_OK ==
+               TALER_amount_get_zero (pc->amount.currency,
+                                      &pc->total_fees_paid));
+  GNUNET_break (GNUNET_OK ==
+               TALER_amount_get_zero (pc->amount.currency,
+                                      &pc->total_refunded));
+
+  /* Check if some of these coins already succeeded */
   qs = db->find_payments (db->cls,
                          &pc->h_contract_terms,
                          &pc->mi->pubkey,
@@ -1508,50 +1678,257 @@ handler_pay_json (struct MHD_Connection *connection,
                          pc);
   if (0 > qs)
   {
-    /* single, read-only SQL statements should never cause
-       serialization problems */
-    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+    db->rollback (db->cls);
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+    {
+      begin_transaction (pc);
+      return;
+    }
     /* Always report on hard error as well to enable diagnostics */
     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-    return TMH_RESPONSE_reply_internal_error (connection,
-                                             
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
-                                             "Merchant database error");
+    resume_pay_with_error (pc,
+                          MHD_HTTP_INTERNAL_SERVER_ERROR,
+                          TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
+                          "Merchant database error");
+    return;
+  }
+
+  /* Check if we refunded some of the coins */
+  qs = db->get_refunds_from_contract_terms_hash (db->cls,
+                                                &pc->mi->pubkey,
+                                                &pc->h_contract_terms,
+                                                &check_coin_refunded,
+                                                pc);
+  if (0 > qs)
+  {
+    db->rollback (db->cls);
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+    {
+      begin_transaction (pc);
+      return;
+    }
+    /* Always report on hard error as well to enable diagnostics */
+    GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+    resume_pay_with_error (pc,
+                          MHD_HTTP_INTERNAL_SERVER_ERROR,
+                          TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
+                          "Merchant database error");
+    return;
+  }
+
+  if (PC_MODE_ABORT_REFUND == pc->mode)
+  {
+    json_t *terms;
+
+    /* The wallet is going for a refund,
+       (on aborted operation)! */
+    /* check payment was indeed incomplete */
+    qs = db->find_paid_contract_terms_from_hash (db->cls,
+                                                 &terms,
+                                                 &pc->h_contract_terms,
+                                                 &pc->mi->pubkey);
+    if (0 > qs)
+    {
+      db->rollback (db->cls);
+      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      {
+       begin_transaction (pc);
+       return;
+      }
+      /* Always report on hard error as well to enable diagnostics */
+      GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+      resume_pay_with_error (pc,
+                            MHD_HTTP_INTERNAL_SERVER_ERROR,
+                            TALER_EC_PAY_DB_STORE_PAY_ERROR,
+                            "Merchant database error");
+      return;
+    }
+    if (0 < qs)
+    {
+      /* Payment had been complete! */
+      json_decref (terms);
+      db->rollback (db->cls);
+      resume_pay_with_error (pc,
+                            MHD_HTTP_FORBIDDEN,
+                            TALER_EC_PAY_ABORT_REFUND_REFUSED_PAYMENT_COMPLETE,
+                            "Payment complete, refusing to abort");
+      return;
+    }
+
+    /* Store refund in DB */
+    qs = db->increase_refund_for_contract (db->cls,
+                                          &pc->h_contract_terms,
+                                          &pc->mi->pubkey,
+                                          &pc->total_paid,
+                                          "incomplete payment aborted");
+    if (0 > qs)
+    {
+      db->rollback (db->cls);
+      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      {
+       begin_transaction (pc);
+       return;
+      }
+      /* Always report on hard error as well to enable diagnostics */
+      GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+      resume_pay_with_error (pc,
+                            MHD_HTTP_INTERNAL_SERVER_ERROR,
+                            TALER_EC_PAY_DB_STORE_PAY_ERROR,
+                            "Merchant database error");
+      return;
+    }
+    qs = db->commit (db->cls);
+    if (0 > qs)
+    {
+      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      {
+       db->rollback (db->cls);
+       begin_transaction (pc);
+       return;
+      }
+      resume_pay_with_error (pc,
+                            MHD_HTTP_INTERNAL_SERVER_ERROR,
+                            TALER_EC_PAY_DB_STORE_PAY_ERROR,
+                            "Merchant database error: could not commit");
+      return;
+    }
+
+    {
+      json_t *refunds;
+
+      refunds = json_array ();
+      for (unsigned int i=0;i<pc->coins_cnt;i++)
+      {
+       struct TALER_RefundRequestPS rr;
+       struct TALER_MerchantSignatureP msig;
+       uint64_t rtransactionid;
+
+       if (GNUNET_YES != pc->dc[i].found_in_db)
+         continue;
+       rtransactionid = 0;
+        rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
+       rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
+       rr.h_contract_terms = pc->h_contract_terms;
+       rr.coin_pub = pc->dc[i].coin_pub;
+       rr.merchant = pc->mi->pubkey;
+       rr.rtransaction_id = GNUNET_htonll (rtransactionid);
+       TALER_amount_hton (&rr.refund_amount,
+                          &pc->dc[i].amount_with_fee);
+       TALER_amount_hton (&rr.refund_fee,
+                          &pc->dc[i].refund_fee);
+
+       if (GNUNET_OK !=
+           GNUNET_CRYPTO_eddsa_sign (&pc->mi->privkey.eddsa_priv,
+                                     &rr.purpose,
+                                     &msig.eddsa_sig))
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                     "Failed to sign successful refund confirmation\n");
+         json_decref (refunds);
+         resume_pay_with_error (pc,
+                                MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                TALER_EC_NONE, /* FIXME! */
+                                "Refund approved, but failed to sign 
confirmation");
+         return;
+       }
+       json_array_append_new (refunds,
+                              json_pack ("{s:I, s:o, s:o s:o s:o}",
+                                         "rtransaction_id", (json_int_t) 
rtransactionid,
+                                         "coin_pub", 
GNUNET_JSON_from_data_auto (&rr.coin_pub),
+                                         "merchant_sig", 
GNUNET_JSON_from_data_auto (&msig),
+                                          "refund_amount", 
TALER_JSON_from_amount_nbo (&rr.refund_amount),
+                                          "refund_fee", 
TALER_JSON_from_amount_nbo (&rr.refund_fee)));
+      }
+      resume_pay_with_response (pc,
+                               MHD_HTTP_OK,
+                               TMH_RESPONSE_make_json_pack ("{s:o, s:o, s:o}",
+                                                            
"refund_permissions", refunds,
+                                                            "merchant_pub", 
GNUNET_JSON_from_data_auto (&pc->mi->pubkey),
+                                                             
"h_contract_terms", GNUNET_JSON_from_data_auto (&pc->h_contract_terms)));
+    }
+    return;
   }
+  /* Default PC_MODE_PAY mode */
+
+  /* Final termination case: all coins already known, just
+     generate ultimate outcome. */
   if (0 == pc->pending)
   {
-    struct MHD_Response *resp;
-    int ret;
+    enum TALER_ErrorCode ec;
 
-    /* Payment succeeded in the past; take short cut
-       and accept immediately */
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Payment succeeded in the past; taking short cut");
-    resp = sign_success_response (pc);
-    ret = MHD_queue_response (connection,
-                             MHD_HTTP_OK,
-                             resp);
-    MHD_destroy_response (resp);
-    return ret;
+    ec = check_payment_sufficient (pc);
+    if (TALER_EC_NONE == ec)
+    {
+      /* Payment succeeded, commit! */
+      qs = db->mark_proposal_paid (db->cls,
+                                  &pc->h_contract_terms,
+                                  &pc->mi->pubkey,
+                                   pc->session_id);
+      if (0 <= qs)
+       qs = db->commit (db->cls);
+      if (0 > qs)
+      {
+       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+       {
+         begin_transaction (pc);
+         return;
+       }
+       resume_pay_with_error (pc,
+                              MHD_HTTP_INTERNAL_SERVER_ERROR,
+                              TALER_EC_PAY_DB_STORE_PAYMENTS_ERROR,
+                              "Merchant database error: could not mark 
proposal as 'paid'");
+       return;
+      }
+      resume_pay_with_response (pc,
+                               MHD_HTTP_OK,
+                               sign_success_response (pc));
+      return;
+    }
+    generate_error_response (pc,
+                            ec);
+    return;
   }
 
-  MHD_suspend_connection (connection);
-  pc->suspended = GNUNET_YES;
 
-  /* Find the responsible exchange, this may take a while... */
-  pc->fo = TMH_EXCHANGES_find_exchange (pc->chosen_exchange,
-                                        pc->mi->wire_method,
-                                        &process_pay_with_exchange,
-                                        pc);
+  /* we made no DB changes,
+     so we can just rollback */
+  db->rollback (db->cls);
+
+  /* Ok, we need to first go to the network.
+     Do that interaction in *tiny* transactions. */
+  find_next_exchange (pc);
+}
+
+
+/**
+ * Process a payment for a proposal.
+ *
+ * @param connection HTTP connection we are receiving payment on
+ * @param root JSON upload with payment data
+ * @param pc context we use to handle the payment
+ * @return value to return to MHD (#MHD_NO to drop connection,
+ *         #MHD_YES to keep handling it)
+ */
+static int
+handler_pay_json (struct MHD_Connection *connection,
+                  const json_t *root,
+                  struct PayContext *pc)
+{
+  int ret;
 
-  /* ... so we suspend connection until the last coin has been ack'd
-     or until we have encountered a hard error.  Eventually, we will
-     resume the connection and send back a response using
-     #resume_pay_with_response(). */
+  ret = parse_pay (connection,
+                   root,
+                   pc);
+  if (GNUNET_OK != ret)
+    return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+  MHD_suspend_connection (connection);
+  pc->suspended = GNUNET_YES;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Suspending /pay handling while working with the exchange\n");
   pc->timeout_task = GNUNET_SCHEDULER_add_delayed (PAY_TIMEOUT,
                                                    &handle_pay_timeout,
                                                    pc);
+  begin_transaction (pc);
   return MHD_YES;
 }
 
@@ -1616,13 +1993,7 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
                res ? "OK" : "FAILED");
     return res;
   }
-  if (NULL != pc->chosen_exchange)
-  {
-    // FIXME: explain in comment why this could happen!
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-               "Shouldn't be here. Old MHD version?\n");
-    return MHD_YES;
-  }
+
   res = TMH_PARSE_post_json (connection,
                             &pc->json_parse_context,
                             upload_data,
diff --git a/src/backend/taler-merchant-httpd_proposal.c 
b/src/backend/taler-merchant-httpd_proposal.c
index f3fee47..1dffaf5 100644
--- a/src/backend/taler-merchant-httpd_proposal.c
+++ b/src/backend/taler-merchant-httpd_proposal.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  (C) 2014, 2015, 2016, 2017 INRIA
+  (C) 2014, 2015, 2016, 2018 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero General Public License as published by the Free 
Software
@@ -135,8 +135,6 @@ proposal_put (struct MHD_Connection *connection,
 {
   int res;
   struct MerchantInstance *mi;
-  struct TALER_ProposalDataPS pdps;
-  struct GNUNET_CRYPTO_EddsaSignature merchant_sig;
   struct TALER_Amount total;
   const char *order_id;
   json_t *products;
@@ -158,6 +156,8 @@ proposal_put (struct MHD_Connection *connection,
     GNUNET_JSON_spec_end ()
   };
   enum GNUNET_DB_QueryStatus qs;
+  const char *instance;
+  struct WireMethod *wm;
 
   /* Add order_id if it doesn't exist. */
   if (NULL ==
@@ -179,18 +179,20 @@ proposal_put (struct MHD_Connection *connection,
     }
     off = strftime (buf,
                     sizeof (buf),
-                    "%H:%M:%S",
+                    "%Y.%j.%H.%M.%S",
                     tm_info);
-    snprintf (buf + off,
-             sizeof (buf) - off,
-              "-%llX",
-              (long long unsigned) GNUNET_CRYPTO_random_u64 
(GNUNET_CRYPTO_QUALITY_WEAK,
-                                                             UINT64_MAX));
+    buf[off++] = '-';
+    uint64_t rand = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
+                                              UINT64_MAX);
+    char *last = GNUNET_STRINGS_data_to_string (&rand, sizeof (uint64_t),
+                                                &buf[off], sizeof (buf) - off);
+    *last = '\0';
     json_object_set_new (order,
                          "order_id",
                          json_string (buf));
   }
 
+  /* Add timestamp if it doesn't exist */
   if (NULL == json_object_get (order,
                                "timestamp"))
   {
@@ -248,6 +250,105 @@ proposal_put (struct MHD_Connection *connection,
                          json_integer ((json_int_t) 
default_wire_fee_amortization));
   }
 
+  if (NULL == json_object_get (order,
+                               "pay_url"))
+  {
+    char *url;
+
+    url = TALER_url_absolute_mhd (connection, "/public/pay", NULL);
+    json_object_set_new (order,
+                         "pay_url",
+                         json_string (url));
+    GNUNET_free (url);
+  }
+
+  if (NULL == json_object_get (order,
+                               "products"))
+  {
+    // FIXME:  When there is no explicit product,
+    // should we create a singleton product list?
+    json_object_set_new (order,
+                         "products",
+                         json_array ());
+  }
+
+  instance = json_string_value (json_object_get (order,
+                                                 "instance"));
+
+  if (NULL == instance)
+  {
+    instance = "default";
+  }
+
+  // Fill in merchant information if necessary
+  {
+    // The frontend either fully specifieds the "merchant" field, or just gives
+    // the backend the "instance" name and lets it fill out.
+    struct MerchantInstance *mi = TMH_lookup_instance (instance);
+    json_t *merchant;
+    json_t *locations;
+    json_t *loc;
+    char *label;
+
+    if (NULL == mi)
+    {
+      return TMH_RESPONSE_reply_internal_error (connection,
+                                                
TALER_EC_PROPOSAL_ORDER_PARSE_ERROR,
+                                                "merchant instance not found");
+    }
+    if (NULL == json_object_get (order, "merchant"))
+    {
+      merchant = json_object ();
+      /* FIXME: should the 'instance' field really be included in the
+         contract? This is really internal to the business! */
+      json_object_set_new (merchant,
+                           "instance",
+                           json_string (instance));
+      json_object_set_new (merchant,
+                           "name",
+                           json_string (mi->name));
+      json_object_set_new (merchant,
+                           "jurisdiction",
+                           json_string ("_mj"));
+      json_object_set_new (merchant,
+                           "address",
+                           json_string ("_ma"));
+      json_object_set_new (order,
+                           "merchant",
+                           merchant);
+      json_object_del (order,
+                       "instance");
+
+      locations = json_object_get (order,
+                                   "locations");
+      if (NULL == locations)
+      {
+        locations = json_object ();
+        json_object_set_new (order,
+                             "locations",
+                             locations);
+      }
+
+      GNUNET_assert (0 < GNUNET_asprintf (&label, "%s-address", mi->id));
+      loc = json_object_get (default_locations, label);
+      if (NULL == loc)
+        loc = json_object ();
+      else
+        loc = json_deep_copy (loc);
+      json_object_set_new (locations, "_ma", loc);
+      GNUNET_free (label);
+
+      GNUNET_assert (0 < GNUNET_asprintf (&label, "%s-jurisdiction", mi->id));
+      loc = json_object_get (default_locations, label);
+      if (NULL == loc)
+        loc = json_object ();
+      else
+        loc = json_deep_copy (loc);
+      json_object_set_new (locations, "_mj", loc);
+      GNUNET_free (label);
+    }
+  }
+
   /* extract fields we need to sign separately */
   res = TMH_PARSE_json_data (connection,
                              order,
@@ -282,9 +383,6 @@ proposal_put (struct MHD_Connection *connection,
                                         TALER_EC_CONTRACT_INSTANCE_UNKNOWN,
                                         "Unknown instance given");
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Signing contract on behalf of instance '%s'\n",
-              mi->id);
   /* add fields to the contract that the backend should provide */
   json_object_set (order,
                    "exchanges",
@@ -292,41 +390,65 @@ proposal_put (struct MHD_Connection *connection,
   json_object_set (order,
                    "auditors",
                    j_auditors);
+  /* TODO (#4939-12806): add proper mechanism for selection of wire method(s) 
by merchant! */
+  wm = mi->wm_head;
+
+  if (NULL == wm)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "No wire method available for specified instance\n");
+    GNUNET_JSON_parse_free (spec);
+    return TMH_RESPONSE_reply_not_found (connection,
+                                        TALER_EC_CONTRACT_INSTANCE_UNKNOWN,
+                                        "No wire method configured for 
instance");
+  }
   json_object_set_new (order,
                        "H_wire",
-                      GNUNET_JSON_from_data_auto (&mi->h_wire));
+                      GNUNET_JSON_from_data_auto (&wm->h_wire));
   json_object_set_new (order,
                        "wire_method",
-                      json_string (mi->wire_method));
+                      json_string (wm->wire_method));
   json_object_set_new (order,
                        "merchant_pub",
                       GNUNET_JSON_from_data_auto (&mi->pubkey));
 
-  /* create proposal signature */
-  pdps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
-  pdps.purpose.size = htonl (sizeof (pdps));
-  if (GNUNET_OK !=
-      TALER_JSON_hash (order,
-                       &pdps.hash))
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Inserting order '%s' for instance '%s'\n",
+              order_id,
+              mi->id);
+
   {
-    GNUNET_break (0);
-    GNUNET_JSON_parse_free (spec);
-    return TMH_RESPONSE_reply_internal_error (connection,
-                                              TALER_EC_INTERNAL_LOGIC_ERROR,
-                                              "Could not hash order");
+    json_t *dummy_contract_terms;
+
+    dummy_contract_terms = NULL;
+    qs = db->find_order (db->cls,
+                         &dummy_contract_terms,
+                         order_id,
+                         &mi->pubkey);
+    if (NULL != dummy_contract_terms)
+      json_decref (dummy_contract_terms);
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
+  {
+    if ( (GNUNET_DB_STATUS_SOFT_ERROR == qs) ||
+         (GNUNET_DB_STATUS_HARD_ERROR == qs) )
+    {
+      return TMH_RESPONSE_reply_internal_error (connection,
+                                                
TALER_EC_PROPOSAL_STORE_DB_ERROR,
+                                                "db error: could not check for 
existing order");
+    }
+    return TMH_RESPONSE_reply_external_error (connection,
+                                              TALER_EC_PROPOSAL_STORE_DB_ERROR,
+                                              "proposal already exists");
   }
-
-  GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv,
-                            &pdps.purpose,
-                            &merchant_sig);
 
   for (unsigned int i=0;i<MAX_RETRIES;i++)
   {
-    qs = db->insert_contract_terms (db->cls,
-                                   order_id,
-                                   &mi->pubkey,
-                                   timestamp,
-                                   order);
+    qs = db->insert_order (db->cls,
+                           order_id,
+                           &mi->pubkey,
+                           timestamp,
+                           order);
     if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
       break;
   }
@@ -344,10 +466,9 @@ proposal_put (struct MHD_Connection *connection,
 
   res = TMH_RESPONSE_reply_json_pack (connection,
                                       MHD_HTTP_OK,
-                                      "{s:O, s:o s:o}",
-                                      "data", order,
-                                      "sig", GNUNET_JSON_from_data_auto 
(&merchant_sig),
-                                      "hash", GNUNET_JSON_from_data_auto 
(&pdps.hash));
+                                      "{s:s}",
+                                      "order_id",
+                                      order_id);
   GNUNET_JSON_parse_free (spec);
   return res;
 }
@@ -423,6 +544,8 @@ MH_handler_proposal_put (struct TMH_RequestHandler *rh,
  * proposal's data related to the transaction id given as the URL's
  * parameter.
  *
+ * Binds the proposal to a nonce.
+ *
  * @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)
@@ -439,10 +562,12 @@ MH_handler_proposal_lookup (struct TMH_RequestHandler *rh,
 {
   const char *order_id;
   const char *instance;
+  const char *nonce;
   int res;
   enum GNUNET_DB_QueryStatus qs;
   json_t *contract_terms;
   struct MerchantInstance *mi;
+  char *last_session_id = NULL;
 
   instance = MHD_lookup_connection_value (connection,
                                           MHD_GET_ARGUMENT_KIND,
@@ -465,8 +590,17 @@ MH_handler_proposal_lookup (struct TMH_RequestHandler *rh,
                                           TALER_EC_PARAMETER_MISSING,
                                            "order_id");
 
+  nonce = MHD_lookup_connection_value (connection,
+                                       MHD_GET_ARGUMENT_KIND,
+                                       "nonce");
+  if (NULL == nonce)
+    return TMH_RESPONSE_reply_arg_missing (connection,
+                                          TALER_EC_PARAMETER_MISSING,
+                                           "nonce");
+
   qs = db->find_contract_terms (db->cls,
                                &contract_terms,
+                                &last_session_id,
                                order_id,
                                &mi->pubkey);
   if (0 > qs)
@@ -481,14 +615,108 @@ MH_handler_proposal_lookup (struct TMH_RequestHandler 
*rh,
                                               "An error occurred while 
retrieving proposal data from db");
   }
   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-    return TMH_RESPONSE_reply_not_found (connection,
-                                         TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND,
-                                         "unknown transaction id");
+  {
+    qs = db->find_order (db->cls,
+                         &contract_terms,
+                         order_id,
+                         &mi->pubkey);
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+    {
+      return TMH_RESPONSE_reply_not_found (connection,
+                                           TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND,
+                                           "unknown order id");
+    }
+    GNUNET_assert (NULL != contract_terms);
+    // FIXME:  now we can delete (merchant_pub, order_id) from the 
merchant_orders table
+    json_object_set_new (contract_terms, "nonce", json_string (nonce));
 
-  res = TMH_RESPONSE_reply_json (connection,
-                                 contract_terms,
-                                 MHD_HTTP_OK);
-  json_decref (contract_terms);
+    struct GNUNET_TIME_Absolute timestamp;
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_absolute_time ("timestamp", &timestamp),
+      GNUNET_JSON_spec_end ()
+    };
+    /* extract fields we need to sign separately */
+    res = TMH_PARSE_json_data (connection, contract_terms, spec);
+    if (GNUNET_NO == res)
+    {
+      return MHD_YES;
+    }
+    if (GNUNET_SYSERR == res)
+    {
+      return TMH_RESPONSE_reply_internal_error (connection,
+                                                
TALER_EC_PROPOSAL_ORDER_PARSE_ERROR,
+                                                "Impossible to parse the 
order");
+    }
+
+    for (unsigned int i=0;i<MAX_RETRIES;i++)
+    {
+      qs = db->insert_contract_terms (db->cls,
+                                      order_id,
+                                      &mi->pubkey,
+                                      timestamp,
+                                      contract_terms);
+      if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
+        break;
+    }
+    if (0 > qs)
+    {
+      /* Special report if retries insufficient */
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+      /* Always report on hard error as well to enable diagnostics */
+      GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+      return TMH_RESPONSE_reply_internal_error (connection,
+                                                
TALER_EC_PROPOSAL_STORE_DB_ERROR,
+                                                "db error: could not store 
this proposal's data into db");
+    }
+  }
+
+  GNUNET_assert (NULL != contract_terms);
+  GNUNET_free_non_null (last_session_id);
+
+  const char *stored_nonce = json_string_value (json_object_get 
(contract_terms, "nonce"));
+
+  if (NULL == stored_nonce)
+  {
+    GNUNET_break (0);
+    return TMH_RESPONSE_reply_internal_error (connection,
+                                              
TALER_EC_PROPOSAL_ORDER_PARSE_ERROR,
+                                              "existing proposal has non 
nonce");
+  }
+
+  if (0 != strcmp (stored_nonce, nonce))
+  {
+    return TMH_RESPONSE_reply_bad_request (connection,
+                                           TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND,
+                                           "mismatched nonce");
+  }
+
+  struct TALER_ProposalDataPS pdps;
+  struct GNUNET_CRYPTO_EddsaSignature merchant_sig;
+
+  /* create proposal signature */
+  pdps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
+  pdps.purpose.size = htonl (sizeof (pdps));
+  if (GNUNET_OK !=
+      TALER_JSON_hash (contract_terms,
+                       &pdps.hash))
+  {
+    GNUNET_break (0);
+    return TMH_RESPONSE_reply_internal_error (connection,
+                                              TALER_EC_INTERNAL_LOGIC_ERROR,
+                                              "Could not hash order");
+  }
+
+  GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv,
+                            &pdps.purpose,
+                            &merchant_sig);
+
+  res = TMH_RESPONSE_reply_json_pack (connection,
+                                      MHD_HTTP_OK,
+                                      "{ s:o, s:o }",
+                                      "contract_terms",
+                                      contract_terms,
+                                      "sig",
+                                      GNUNET_JSON_from_data_auto 
(&merchant_sig));
   return res;
 }
 
diff --git a/src/backend/taler-merchant-httpd_refund.c 
b/src/backend/taler-merchant-httpd_refund.c
index e0c326f..8a6060c 100644
--- a/src/backend/taler-merchant-httpd_refund.c
+++ b/src/backend/taler-merchant-httpd_refund.c
@@ -117,6 +117,7 @@ MH_handler_refund_increase (struct TMH_RequestHandler *rh,
   const char *order_id;
   const char *reason;
   const char *merchant;
+  char *last_session_id;
   struct MerchantInstance *mi;
   struct GNUNET_HashCode h_contract_terms;
   struct TALER_MerchantRefundConfirmationPS confirmation;
@@ -183,6 +184,7 @@ MH_handler_refund_increase (struct TMH_RequestHandler *rh,
   /* Convert order id to h_contract_terms */
   qs = db->find_contract_terms (db->cls,
                                &contract_terms,
+                                &last_session_id,
                                order_id,
                                &mi->pubkey);
   if (0 > qs)
@@ -206,6 +208,8 @@ MH_handler_refund_increase (struct TMH_RequestHandler *rh,
                                          "Order id not found in database");
   }
 
+  GNUNET_free (last_session_id);
+
   if (GNUNET_OK !=
       TALER_JSON_hash (contract_terms,
                        &h_contract_terms))
@@ -260,15 +264,12 @@ MH_handler_refund_increase (struct TMH_RequestHandler *rh,
    * the information needed to generate the right response.
    */
 
-  json_decref (contract_terms);
-  json_decref (root);
-  GNUNET_JSON_parse_free (spec);
-
   confirmation.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND_OK);
   confirmation.purpose.size = htonl (sizeof (struct 
TALER_MerchantRefundConfirmationPS));
   GNUNET_CRYPTO_hash (order_id,
                       strlen (order_id),
                       &confirmation.h_order_id);
+
   if (GNUNET_OK !=
       GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv,
                                &confirmation.purpose,
@@ -276,16 +277,49 @@ MH_handler_refund_increase (struct TMH_RequestHandler *rh,
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Failed to sign successful refund confirmation\n");
+    json_decref (contract_terms);
+    json_decref (root);
+    GNUNET_JSON_parse_free (spec);
     return TMH_RESPONSE_reply_internal_error (connection,
-                                              TALER_EC_NONE,
+                                              TALER_EC_NONE, /* FIXME! */
                                               "Refund done, but failed to sign 
confirmation");
 
   }
 
-  return TMH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:o}",
-                                       "sig", GNUNET_JSON_from_data_auto 
(&sig));
+  {
+    int ret;
+    char *refund_pickup_url;
+    char *refund_redirect_url;
+
+    refund_pickup_url = TALER_url_absolute_mhd (connection,
+                                                "/public/refund",
+                                                "instance",
+                                                mi->id,
+                                                "order_id",
+                                                order_id,
+                                                NULL);
+    GNUNET_assert (NULL != refund_pickup_url);
+    refund_redirect_url = TALER_url_absolute_mhd (connection,
+                                                  "public/trigger-pay",
+                                                  "refund_url",
+                                                  refund_pickup_url,
+                                                  NULL);
+    GNUNET_assert (NULL != refund_redirect_url);
+    ret = TMH_RESPONSE_reply_json_pack (connection,
+                                        MHD_HTTP_OK,
+                                        "{s:o, s:s, s:o}",
+                                        "sig",
+                                        GNUNET_JSON_from_data_auto (&sig),
+                                        "refund_redirect_url",
+                                        refund_redirect_url,
+                                        "contract_terms",
+                                        contract_terms);
+    GNUNET_free (refund_pickup_url);
+    GNUNET_free (refund_redirect_url);
+    json_decref (root);
+    GNUNET_JSON_parse_free (spec);
+    return ret;
+  }
 }
 
 
@@ -340,13 +374,11 @@ process_refunds_cb (void *cls,
     return;
   }
 
-  element = json_pack ("{s:o, s:o, s:o, s:o, s:I, s:o, s:o}",
+  element = json_pack ("{s:o, s:o, s:o, s:I, s:o}",
                        "refund_amount", TALER_JSON_from_amount (refund_amount),
                        "refund_fee", TALER_JSON_from_amount (refund_fee),
-                       "h_contract_terms", GNUNET_JSON_from_data_auto 
(prd->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 
(&prd->merchant->pubkey),
                        "merchant_sig", GNUNET_JSON_from_data_auto (&sig));
   if (NULL == element)
   {
@@ -390,6 +422,7 @@ MH_handler_refund_lookup (struct TMH_RequestHandler *rh,
   json_t *contract_terms;
   struct MerchantInstance *mi;
   enum GNUNET_DB_QueryStatus qs;
+  char *last_session_id;
 
   instance = MHD_lookup_connection_value (connection,
                                           MHD_GET_ARGUMENT_KIND,
@@ -428,8 +461,10 @@ MH_handler_refund_lookup (struct TMH_RequestHandler *rh,
   }
 
   /* Convert order id to h_contract_terms */
+  contract_terms = NULL;
   qs = db->find_contract_terms (db->cls,
                                &contract_terms,
+                                &last_session_id,
                                order_id,
                                &mi->pubkey);
   if (0 > qs)
@@ -454,6 +489,8 @@ MH_handler_refund_lookup (struct TMH_RequestHandler *rh,
                                          "Order id not found in database");
   }
 
+  GNUNET_free (last_session_id);
+
   if (GNUNET_OK !=
       TALER_JSON_hash (contract_terms,
                        &h_contract_terms))
@@ -461,25 +498,35 @@ MH_handler_refund_lookup (struct TMH_RequestHandler *rh,
     GNUNET_break (0);
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Could not hash contract terms\n");
+    json_decref (contract_terms);
     return TMH_RESPONSE_reply_internal_error (connection,
                                               TALER_EC_INTERNAL_LOGIC_ERROR,
                                               "Could not hash contract terms");
   }
+  json_decref (contract_terms);
 
-  json_t *response;
-  enum TALER_ErrorCode ec;
-  const char *errmsg;
-
-  response = TM_get_refund_json (mi, &h_contract_terms, &ec, &errmsg);
-
-  if (NULL == response) {
-    return TMH_RESPONSE_reply_internal_error (connection, ec, errmsg);
+  {
+    json_t *response;
+    enum TALER_ErrorCode ec;
+    const char *errmsg;
+
+    response = TM_get_refund_json (mi,
+                                   &h_contract_terms,
+                                   &ec,
+                                   &errmsg);
+    if (NULL == response)
+      return TMH_RESPONSE_reply_internal_error (connection,
+                                                ec,
+                                                errmsg);
+    return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_OK,
+                                         "{s:o, s:o, s:o}",
+                                         "refund_permissions",
+                                         response,
+                                         "merchant_pub",
+                                         GNUNET_JSON_from_data_auto 
(&mi->pubkey),
+                                         "h_contract_terms",
+                                         GNUNET_JSON_from_data_auto 
(&h_contract_terms));
   }
-
-  return TMH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:o}",
-                                       "refund_permissions", response);
 }
 
 
diff --git a/src/backend/taler-merchant-httpd_responses.c 
b/src/backend/taler-merchant-httpd_responses.c
index 1417fa2..71d0406 100644
--- a/src/backend/taler-merchant-httpd_responses.c
+++ b/src/backend/taler-merchant-httpd_responses.c
@@ -181,11 +181,10 @@ TMH_RESPONSE_reply_json_pack (struct MHD_Connection 
*connection,
  * @return a MHD response object
  */
 struct MHD_Response *
-TMH_RESPONSE_make_internal_error (enum TALER_ErrorCode ec,
-                                 const char *hint)
+TMH_RESPONSE_make_error (enum TALER_ErrorCode ec,
+                        const char *hint)
 {
-  return TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:s}",
-                                      "error", "internal error",
+  return TMH_RESPONSE_make_json_pack ("{s:I, s:s}",
                                      "code", (json_int_t) ec,
                                       "hint", hint);
 }
@@ -206,8 +205,7 @@ TMH_RESPONSE_reply_internal_error (struct MHD_Connection 
*connection,
 {
   return TMH_RESPONSE_reply_json_pack (connection,
                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       "{s:s, s:I, s:s}",
-                                       "error", "internal error",
+                                       "{s:I, s:s}",
                                       "code", (json_int_t) ec,
                                        "hint", hint);
 }
@@ -354,32 +352,13 @@ TMH_RESPONSE_reply_external_error (struct MHD_Connection 
*connection,
 {
   return TMH_RESPONSE_reply_json_pack (connection,
                                        MHD_HTTP_BAD_REQUEST,
-                                       "{s:s, s:I, s:s}",
-                                       "error", "client error",
+                                       "{s:I, s:s}",
                                       "code", (json_int_t) ec,
                                        "hint", hint);
 }
 
 
 /**
- * Create a response indicating an external error.
- *
- * @param ec error code to return
- * @param hint hint about the internal error's nature
- * @return a MHD response object
- */
-struct MHD_Response *
-TMH_RESPONSE_make_external_error (enum TALER_ErrorCode ec,
-                                 const char *hint)
-{
-  return TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:s}",
-                                      "error", "client error",
-                                     "code", (json_int_t) ec,
-                                      "hint", hint);
-}
-
-
-/**
  * Send a response indicating a missing argument.
  *
  * @param connection the MHD connection to use
@@ -394,7 +373,7 @@ TMH_RESPONSE_reply_arg_missing (struct MHD_Connection 
*connection,
 {
   return TMH_RESPONSE_reply_json_pack (connection,
                                        MHD_HTTP_BAD_REQUEST,
-                                       "{ s:s, s:I, s:s}",
+                                       "{s:s, s:I, s:s}",
                                        "error", "missing parameter",
                                       "code", (json_int_t) ec,
                                        "parameter", param_name);
diff --git a/src/backend/taler-merchant-httpd_responses.h 
b/src/backend/taler-merchant-httpd_responses.h
index a96b696..3dbd004 100644
--- a/src/backend/taler-merchant-httpd_responses.h
+++ b/src/backend/taler-merchant-httpd_responses.h
@@ -143,20 +143,6 @@ TMH_RESPONSE_reply_bad_request (struct MHD_Connection 
*connection,
 
 
 /**
- * Send a response indicating an internal error.
- *
- * @param connection the MHD connection to use
- * @param ec error code to return
- * @param hint hint about the internal error's nature
- * @return a MHD result code
- */
-int
-TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection,
-                                  enum TALER_ErrorCode ec,
-                                   const char *hint);
-
-
-/**
  * Create a response indicating an internal error.
  *
  * @param ec error code to return
@@ -183,15 +169,29 @@ TMH_RESPONSE_reply_external_error (struct MHD_Connection 
*connection,
 
 
 /**
- * Create a response indicating an external error.
+ * Send a response indicating an internal error.
+ *
+ * @param connection the MHD connection to use
+ * @param ec error code to return
+ * @param hint hint about the internal error's nature
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection,
+                                  enum TALER_ErrorCode ec,
+                                   const char *hint);
+
+
+/**
+ * Create a response indicating an error.
  *
  * @param ec error code to return
  * @param hint hint about the internal error's nature
  * @return a MHD response object
  */
 struct MHD_Response *
-TMH_RESPONSE_make_external_error (enum TALER_ErrorCode ec,
-                                 const char *hint);
+TMH_RESPONSE_make_error (enum TALER_ErrorCode ec,
+                        const char *hint);
 
 
 /**
diff --git a/src/backend/taler-merchant-httpd_tip-authorize.c 
b/src/backend/taler-merchant-httpd_tip-authorize.c
index c55b41b..03d8ed3 100644
--- a/src/backend/taler-merchant-httpd_tip-authorize.c
+++ b/src/backend/taler-merchant-httpd_tip-authorize.c
@@ -20,6 +20,7 @@
  */
 #include "platform.h"
 #include <jansson.h>
+#include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
 #include "taler-merchant-httpd.h"
 #include "taler-merchant-httpd_mhd.h"
@@ -29,13 +30,8 @@
 #include "taler-merchant-httpd_tip-authorize.h"
 
 
-/**
- * Information we keep for individual calls
- * to requests that parse JSON, but keep no other state.
- */
-struct TMH_JsonParseContext
+struct TipAuthContext
 {
-
   /**
    * This field MUST be first.
    * FIXME: Explain why!
@@ -46,21 +42,231 @@ struct TMH_JsonParseContext
    * Placeholder for #TMH_PARSE_post_json() to keep its internal state.
    */
   void *json_parse_context;
+
+  /**
+   * HTTP connection we are handling.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * Merchant instance to use.
+   */
+  const char *instance;
+
+  /**
+   * Justification to use.
+   */
+  const char *justification;
+
+  /**
+   * Pickup URL to use.
+   */
+  const char *pickup_url;
+
+  /**
+   * URL to navigate to after tip.
+   */
+  const char *next_url;
+
+  /**
+   * JSON request received.
+   */
+  json_t *root;
+
+  /**
+   * Handle to pending /reserve/status request.
+   */
+  struct TALER_EXCHANGE_ReserveStatusHandle *rsh;
+
+  /**
+   * Handle for operation to obtain exchange handle.
+   */
+  struct TMH_EXCHANGES_FindOperation *fo;
+
+  /**
+   * Reserve expiration time as provided by the exchange.
+   * Set in #exchange_cont.
+   */
+  struct GNUNET_TIME_Relative idle_reserve_expiration_time;
+
+  /**
+   * Tip amount requested.
+   */
+  struct TALER_Amount amount;
+
+  /**
+   * Private key used by this merchant for the tipping reserve.
+   */
+  struct TALER_ReservePrivateKeyP reserve_priv;
+
+  /**
+   * Flag set to #GNUNET_YES when we have tried /reserve/status of the
+   * tipping reserve already.
+   */
+  int checked_status;
+
+  /**
+   * Flag set to #GNUNET_YES when we have parsed the incoming JSON already.
+   */
+  int parsed_json;
 };
 
 
 /**
- * Custom cleanup routine for a `struct TMH_JsonParseContext`.
+ * Custom cleanup routine for a `struct TipAuthContext`.
  *
  * @param hc the `struct TMH_JsonParseContext` to clean up.
  */
 static void
-json_parse_cleanup (struct TM_HandlerContext *hc)
+cleanup_tac (struct TM_HandlerContext *hc)
+{
+  struct TipAuthContext *tac = (struct TipAuthContext *) hc;
+
+  if (NULL != tac->root)
+  {
+    json_decref (tac->root);
+    tac->root = NULL;
+  }
+  if (NULL != tac->rsh)
+  {
+    TALER_EXCHANGE_reserve_status_cancel (tac->rsh);
+    tac->rsh = NULL;
+  }
+  if (NULL != tac->fo)
+  {
+    TMH_EXCHANGES_find_exchange_cancel (tac->fo);
+    tac->fo = NULL;
+  }
+  TMH_PARSE_post_cleanup_callback (tac->json_parse_context);
+  GNUNET_free (tac);
+}
+
+
+/**
+ * Function called with the result of the /reserve/status request
+ * for the tipping reserve.  Update our database balance with the
+ * result.
+ *
+ * @param cls closure with a `struct TipAuthContext *'
+ * @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[in] json original response in JSON format (useful only for 
diagnostics)
+ * @param balance current balance in the reserve, NULL on error
+ * @param history_length number of entries in the transaction history, 0 on 
error
+ * @param history detailed transaction history, NULL on error
+ */
+static void
+handle_status (void *cls,
+               unsigned int http_status,
+               enum TALER_ErrorCode ec,
+               const json_t *json,
+               const struct TALER_Amount *balance,
+               unsigned int history_length,
+               const struct TALER_EXCHANGE_ReserveHistory *history)
 {
-  struct TMH_JsonParseContext *jpc = (struct TMH_JsonParseContext *) hc;
+  struct TipAuthContext *tac = cls;
 
-  TMH_PARSE_post_cleanup_callback (jpc->json_parse_context);
-  GNUNET_free (jpc);
+  tac->rsh = NULL;
+  if (MHD_HTTP_OK != http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to obtain tipping reserve status from exchange 
(%u/%d)\n"),
+                http_status,
+                ec);
+    MHD_resume_connection (tac->connection);
+    TMH_trigger_daemon ();
+    return;
+  }
+
+  /* Update DB based on status! */
+  for (unsigned int i=0;i<history_length;i++)
+  {
+    switch (history[i].type)
+    {
+    case TALER_EXCHANGE_RTT_DEPOSIT:
+      {
+        enum GNUNET_DB_QueryStatus qs;
+        struct GNUNET_HashCode uuid;
+        struct GNUNET_TIME_Absolute expiration;
+
+        expiration = GNUNET_TIME_absolute_add 
(history[i].details.in_details.timestamp,
+                                               
tac->idle_reserve_expiration_time);
+        GNUNET_CRYPTO_hash (history[i].details.in_details.wire_reference,
+                            history[i].details.in_details.wire_reference_size,
+                            &uuid);
+        qs = db->enable_tip_reserve (db->cls,
+                                     &tac->reserve_priv,
+                                     &uuid,
+                                     &history[i].amount,
+                                     expiration);
+        if (0 > qs)
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      _("Database error updating tipping reserve status: 
%d\n"),
+                      qs);
+        }
+      }
+      break;
+    case TALER_EXCHANGE_RTT_WITHDRAWAL:
+      /* expected */
+      break;
+    case TALER_EXCHANGE_RTT_PAYBACK:
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  _("Encountered unsupported /payback operation on tipping 
reserve\n"));
+      break;
+    case TALER_EXCHANGE_RTT_CLOSE:
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  _("Exchange closed reserve (due to expiration), balance 
calulation is likely wrong. Please create a fresh reserve.\n"));
+      break;
+    }
+  }
+  /* Finally, resume processing */
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Resuming HTTP processing\n");
+  MHD_resume_connection (tac->connection);
+  TMH_trigger_daemon ();
+}
+
+
+/**
+ * Function called with the result of a #TMH_EXCHANGES_find_exchange()
+ * operation.
+ *
+ * @param cls closure with a `struct TipAuthContext *'
+ * @param eh handle to the exchange context
+ * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if 
not available
+ * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config
+ */
+static void
+exchange_cont (void *cls,
+               struct TALER_EXCHANGE_Handle *eh,
+               const struct TALER_Amount *wire_fee,
+               int exchange_trusted)
+{
+  struct TipAuthContext *tac = cls;
+  struct TALER_ReservePublicKeyP reserve_pub;
+  const struct TALER_EXCHANGE_Keys *keys;
+
+  tac->fo = NULL;
+  if (NULL == eh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to contact exchange configured for tipping!\n"));
+    MHD_resume_connection (tac->connection);
+    TMH_trigger_daemon ();
+    return;
+  }
+  keys = TALER_EXCHANGE_get_keys (eh);
+  GNUNET_assert (NULL != keys);
+  tac->idle_reserve_expiration_time
+    = keys->reserve_closing_delay;
+  GNUNET_CRYPTO_eddsa_key_get_public (&tac->reserve_priv.eddsa_priv,
+                                      &reserve_pub.eddsa_pub);
+  tac->rsh = TALER_EXCHANGE_reserve_status (eh,
+                                            &reserve_pub,
+                                            &handle_status,
+                                            tac);
 }
 
 
@@ -81,66 +287,75 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
                           const char *upload_data,
                           size_t *upload_data_size)
 {
-  struct MerchantInstance *mi;
+  struct TipAuthContext *tac;
   int res;
-  struct TALER_Amount amount;
-  const char *instance;
-  const char *justification;
-  const char *pickup_url;
-  const char *next_url;
-  struct GNUNET_JSON_Specification spec[] = {
-    TALER_JSON_spec_amount ("amount", &amount),
-    GNUNET_JSON_spec_string ("instance", &instance),
-    GNUNET_JSON_spec_string ("justification", &justification),
-    GNUNET_JSON_spec_string ("pickup_url", &pickup_url),
-    GNUNET_JSON_spec_string ("next_url", &next_url),
-    GNUNET_JSON_spec_end()
-  };
-  json_t *root;
+  struct MerchantInstance *mi;
+  enum TALER_ErrorCode ec;
   struct GNUNET_TIME_Absolute expiration;
   struct GNUNET_HashCode tip_id;
-  struct TMH_JsonParseContext *ctx;
-  enum TALER_ErrorCode ec;
 
   if (NULL == *connection_cls)
   {
-    ctx = GNUNET_new (struct TMH_JsonParseContext);
-    ctx->hc.cc = &json_parse_cleanup;
-    *connection_cls = ctx;
+    tac = GNUNET_new (struct TipAuthContext);
+    tac->hc.cc = &cleanup_tac;
+    tac->connection = connection;
+    *connection_cls = tac;
   }
   else
   {
-    ctx = *connection_cls;
+    tac = *connection_cls;
   }
-  res = TMH_PARSE_post_json (connection,
-                             &ctx->json_parse_context,
-                             upload_data,
-                             upload_data_size,
-                             &root);
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  /* the POST's body has to be further fetched */
-  if ( (GNUNET_NO == res) ||
-       (NULL == root) )
-    return MHD_YES;
 
-  res = TMH_PARSE_json_data (connection,
-                             root,
-                             spec);
-  if (GNUNET_YES != res)
+  if (GNUNET_NO == tac->parsed_json)
   {
-    GNUNET_break_op (0);
-    json_decref (root);
-    return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+    struct GNUNET_JSON_Specification spec[] = {
+      TALER_JSON_spec_amount ("amount", &tac->amount),
+      GNUNET_JSON_spec_string ("instance", &tac->instance),
+      GNUNET_JSON_spec_string ("justification", &tac->justification),
+      GNUNET_JSON_spec_string ("pickup_url", &tac->pickup_url),
+      GNUNET_JSON_spec_string ("next_url", &tac->next_url),
+      GNUNET_JSON_spec_end()
+    };
+
+    res = TMH_PARSE_post_json (connection,
+                               &tac->json_parse_context,
+                               upload_data,
+                               upload_data_size,
+                               &tac->root);
+    if (GNUNET_SYSERR == res)
+      return MHD_NO;
+    /* the POST's body has to be further fetched */
+    if ( (GNUNET_NO == res) ||
+         (NULL == tac->root) )
+      return MHD_YES;
+
+    if (NULL == json_object_get (tac->root, "pickup_url"))
+    {
+      char *pickup_url = TALER_url_absolute_mhd (connection,
+                                                 "/public/tip-pickup",
+                                                 NULL);
+      GNUNET_assert (NULL != pickup_url);
+      json_object_set_new (tac->root, "pickup_url", json_string (pickup_url));
+      GNUNET_free (pickup_url);
+    }
+
+    res = TMH_PARSE_json_data (connection,
+                               tac->root,
+                               spec);
+    if (GNUNET_YES != res)
+    {
+      GNUNET_break_op (0);
+      return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+    }
+    tac->parsed_json = GNUNET_YES;
   }
 
-  mi = TMH_lookup_instance (instance);
+  mi = TMH_lookup_instance (tac->instance);
   if (NULL == mi)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Instance `%s' not configured\n",
-                instance);
-    json_decref (root);  
+                tac->instance);
     return TMH_RESPONSE_reply_not_found (connection,
                                         
TALER_EC_TIP_AUTHORIZE_INSTANCE_UNKNOWN,
                                         "unknown instance");
@@ -149,78 +364,99 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Instance `%s' not configured for tipping\n",
-                instance);
-    json_decref (root);
+                tac->instance);
     return TMH_RESPONSE_reply_not_found (connection,
                                         
TALER_EC_TIP_AUTHORIZE_INSTANCE_DOES_NOT_TIP,
                                         "exchange for tipping not configured 
for the instance");
   }
+  tac->reserve_priv = mi->tip_reserve;
   ec = db->authorize_tip (db->cls,
-                          justification,
-                          &amount,
+                          tac->justification,
+                          &tac->amount,
                           &mi->tip_reserve,
                          mi->tip_exchange,
                           &expiration,
                           &tip_id);
+  /* If we have insufficient funds according to OUR database,
+     check with exchange to see if the reserve has been topped up
+     in the meantime (or if tips were not withdrawn yet). */
+  if ( (TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS == ec) &&
+       (GNUNET_NO == tac->checked_status) )
+  {
+    MHD_suspend_connection (connection);
+    tac->checked_status = GNUNET_YES;
+    tac->fo = TMH_EXCHANGES_find_exchange (mi->tip_exchange,
+                                           NULL,
+                                           &exchange_cont,
+                                           tac);
+    return MHD_YES;
+  }
+
+  /* handle irrecoverable errors */
   if (TALER_EC_NONE != ec)
   {
     unsigned int rc;
+    const char *msg;
 
     switch (ec)
     {
     case TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS:
       rc = MHD_HTTP_PRECONDITION_FAILED;
+      msg = "Failed to approve tip: merchant has insufficient tipping funds";
       break;
     case TALER_EC_TIP_AUTHORIZE_RESERVE_EXPIRED:
+      msg = "Failed to approve tip: merchant's tipping reserve expired";
       rc = MHD_HTTP_PRECONDITION_FAILED;
       break;
     case TALER_EC_TIP_AUTHORIZE_RESERVE_UNKNOWN:
-      rc = MHD_HTTP_NOT_FOUND;
-      break;
-    case TALER_EC_TIP_AUTHORIZE_RESERVE_NOT_ENABLED:
+      msg = "Failed to approve tip: merchant's tipping reserve does not exist";
       rc = MHD_HTTP_NOT_FOUND;
       break;
     default:
       rc = MHD_HTTP_INTERNAL_SERVER_ERROR;
+      msg = "Failed to approve tip: internal server error";
       break;
     }
-    json_decref (root);      
     return TMH_RESPONSE_reply_rc (connection,
-                                 rc,
-                                 ec,
-                                 "Database error approving tip");
+                                  rc,
+                                  ec,
+                                  msg);
   }
-  if (0)
+
+  /* generate success response */
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Insufficient funds to authorize tip over `%s' at instance 
`%s'\n",
-                TALER_amount2s (&amount),
-                instance);
-    json_decref (root);
-    return TMH_RESPONSE_reply_rc (connection,
-                                  MHD_HTTP_PRECONDITION_FAILED,
-                                  TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS,
-                                  "Insufficient funds for tip");
+    json_t *tip_token;
+    char *tip_token_str;
+    char *tip_redirect_url;
+
+    tip_token = json_pack ("{s:o, s:o, s:o, s:s, s:s, s:s}",
+                           "tip_id", GNUNET_JSON_from_data_auto (&tip_id),
+                           "expiration", GNUNET_JSON_from_time_abs 
(expiration),
+                           "amount", TALER_JSON_from_amount (&tac->amount),
+                           "exchange_url", mi->tip_exchange,
+                           "next_url", tac->next_url,
+                           "pickup_url", tac->pickup_url);
+    tip_token_str = json_dumps (tip_token, JSON_COMPACT);
+    GNUNET_assert (NULL != tip_token_str);
+    tip_redirect_url = TALER_url_absolute_mhd (connection, 
"public/trigger-pay",
+                                               "tip_token", tip_token_str,
+                                               NULL);
+    GNUNET_assert (NULL != tip_redirect_url);
+    /* FIXME:  This is pretty redundant, but we want to support some older
+     * merchant implementations.  Newer ones should only get the
+     * tip_redirect_url. */
+    res = TMH_RESPONSE_reply_json_pack (connection,
+                                        MHD_HTTP_OK,
+                                        "{s:o, s:o, s:s, s:o, s:s}",
+                                        "tip_id", GNUNET_JSON_from_data_auto 
(&tip_id),
+                                        "expiration", 
GNUNET_JSON_from_time_abs (expiration),
+                                        "exchange_url", mi->tip_exchange,
+                                        "tip_token", tip_token,
+                                        "tip_redirect_url", tip_redirect_url);
+    GNUNET_free (tip_token_str);
+    GNUNET_free (tip_redirect_url);
+    return res;
   }
-  json_t *tip_token = json_pack ("{s:o, s:o, s:o, s:s, s:s, s:s}",
-                                 "tip_id", GNUNET_JSON_from_data_auto 
(&tip_id),
-                                 "expiration", GNUNET_JSON_from_time_abs 
(expiration),
-                                 "amount", TALER_JSON_from_amount (&amount),
-                                 "exchange_url", mi->tip_exchange,
-                                 "next_url", next_url,
-                                 "pickup_url", pickup_url);
-  char *tip_token_str = json_dumps (tip_token,  JSON_ENSURE_ASCII | 
JSON_COMPACT);
-  json_decref (tip_token);
-  json_decref (root);
-  int ret = TMH_RESPONSE_reply_json_pack (connection,
-                                          MHD_HTTP_OK,
-                                          "{s:o, s:o, s:s, s:s}",
-                                          "tip_id", GNUNET_JSON_from_data_auto 
(&tip_id),
-                                          "expiration", 
GNUNET_JSON_from_time_abs (expiration),
-                                          "exchange_url", mi->tip_exchange,
-                                          "tip_token", tip_token_str);
-  GNUNET_free (tip_token_str);
-  return ret;
 }
 
 /* end of taler-merchant-httpd_tip-authorize.c */
diff --git a/src/backend/taler-merchant-httpd_tip-enable.c 
b/src/backend/taler-merchant-httpd_tip-enable.c
deleted file mode 100644
index a96613e..0000000
--- a/src/backend/taler-merchant-httpd_tip-enable.c
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
-  This file is part of TALER
-  (C) 2014-2017 Taler Systems SA
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU Affero General Public License as published by the Free 
Software
-  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 backend/taler-merchant-httpd_tip-enable.c
- * @brief implement API for authorizing tips to be paid to visitors
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <jansson.h>
-#include <taler/taler_json_lib.h>
-#include "taler-merchant-httpd.h"
-#include "taler-merchant-httpd_mhd.h"
-#include "taler-merchant-httpd_parsing.h"
-#include "taler-merchant-httpd_exchanges.h"
-#include "taler-merchant-httpd_responses.h"
-#include "taler-merchant-httpd_tip-enable.h"
-
-
-/**
- * Information we keep for individual calls
- * to requests that parse JSON, but keep no other state.
- */
-struct TMH_JsonParseContext
-{
-
-  /**
-   * This field MUST be first.
-   * FIXME: Explain why!
-   */
-  struct TM_HandlerContext hc;
-
-  /**
-   * Placeholder for #TMH_PARSE_post_json() to keep its internal state.
-   */
-  void *json_parse_context;
-};
-
-
-/**
- * Custom cleanup routine for a `struct TMH_JsonParseContext`.
- *
- * @param hc the `struct TMH_JsonParseContext` to clean up.
- */
-static void
-json_parse_cleanup (struct TM_HandlerContext *hc)
-{
-  struct TMH_JsonParseContext *jpc = (struct TMH_JsonParseContext *) hc;
-
-  TMH_PARSE_post_cleanup_callback (jpc->json_parse_context);
-  GNUNET_free (jpc);
-}
-
-
-/**
- * Handle a "/tip-enable" request.
- *
- * @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
-MH_handler_tip_enable (struct TMH_RequestHandler *rh,
-                          struct MHD_Connection *connection,
-                          void **connection_cls,
-                          const char *upload_data,
-                          size_t *upload_data_size)
-{
-  enum GNUNET_DB_QueryStatus qs;
-  int res;
-  struct TALER_Amount credit;
-  struct GNUNET_TIME_Absolute expiration;
-  struct TALER_ReservePrivateKeyP reserve_priv;
-  struct GNUNET_HashCode credit_uuid;
-  struct GNUNET_JSON_Specification spec[] = {
-    TALER_JSON_spec_amount ("credit", &credit),
-    GNUNET_JSON_spec_absolute_time ("expiration", &expiration),
-    GNUNET_JSON_spec_fixed_auto ("reserve_priv", &reserve_priv),
-    GNUNET_JSON_spec_fixed_auto ("credit_uuid", &credit_uuid),
-    GNUNET_JSON_spec_end()
-  };
-  struct TMH_JsonParseContext *ctx;
-  json_t *root;
-
-  if (NULL == *connection_cls)
-  {
-    ctx = GNUNET_new (struct TMH_JsonParseContext);
-    ctx->hc.cc = &json_parse_cleanup;
-    *connection_cls = ctx;
-  }
-  else
-  {
-    ctx = *connection_cls;
-  }
-  res = TMH_PARSE_post_json (connection,
-                             &ctx->json_parse_context,
-                             upload_data,
-                             upload_data_size,
-                             &root);
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  /* the POST's body has to be further fetched */
-  if ( (GNUNET_NO == res) ||
-       (NULL == root) )
-    return MHD_YES;
-
-  res = TMH_PARSE_json_data (connection,
-                             root,
-                             spec);
-  json_decref (root);
-  if (GNUNET_YES != res)
-  {
-    GNUNET_break_op (0);
-    return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
-  }
-
-  qs = db->enable_tip_reserve (db->cls,
-                               &reserve_priv,
-                               &credit_uuid,
-                               &credit,
-                               expiration);
-  if (0 > qs)
-  {
-    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
-    return TMH_RESPONSE_reply_internal_error (connection,
-                                             
TALER_EC_TIP_ENABLE_DB_TRANSACTION_ERROR,
-                                              "Database error approving tip");
-  }
-  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-  {
-    return TMH_RESPONSE_reply_json_pack (connection,
-                                         MHD_HTTP_OK,
-                                         "{s:s}",
-                                         "status", "duplicate submission");
-  }
-  return TMH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:s}",
-                                       "status", "ok");
-}
-
-/* end of taler-merchant-httpd_tip-enable.c */
diff --git a/src/backend/taler-merchant-httpd_tip-pickup.c 
b/src/backend/taler-merchant-httpd_tip-pickup.c
index 76bf7ce..3cc73af 100644
--- a/src/backend/taler-merchant-httpd_tip-pickup.c
+++ b/src/backend/taler-merchant-httpd_tip-pickup.c
@@ -79,9 +79,9 @@ struct PickupContext
   void *json_parse_context;
 
   /**
-   * URI of the exchange this tip uses.
+   * URL of the exchange this tip uses.
    */
-  char *exchange_uri;
+  char *exchange_url;
 
   /**
    * Operation we run to find the exchange (and get its /keys).
@@ -162,7 +162,7 @@ pickup_cleanup (struct TM_HandlerContext *hc)
     pc->fo = NULL;
   }
   TMH_PARSE_post_cleanup_callback (pc->json_parse_context);
-  GNUNET_free_non_null (pc->exchange_uri);
+  GNUNET_free_non_null (pc->exchange_url);
   GNUNET_free (pc);
 }
 
@@ -203,23 +203,27 @@ run_pickup (struct MHD_Connection *connection,
   if (TALER_EC_NONE != ec)
   {
     unsigned int response_code;
+    const char *human;
 
     switch (ec)
     {
     case TALER_EC_TIP_PICKUP_TIP_ID_UNKNOWN:
       response_code = MHD_HTTP_NOT_FOUND;
+      human = "tip identifier not known to this service";
       break;
     case TALER_EC_TIP_PICKUP_NO_FUNDS:
-      response_code = MHD_HTTP_SERVICE_UNAVAILABLE;
+      response_code = MHD_HTTP_CONFLICT;
+      human = "withdrawn funds exceed amounts approved for tip";
       break;
     default:
       response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+      human = "database failure";
       break;
     }
     return TMH_RESPONSE_reply_rc (connection,
                                  response_code,
                                  ec,
-                                 "Database error approving tip");
+                                 human);
   }
   sigs = json_array ();
   GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv.eddsa_priv,
@@ -375,7 +379,7 @@ prepare_pickup (struct PickupContext *pc)
 
   qs = db->lookup_tip_by_id (db->cls,
                              &pc->tip_id,
-                             &pc->exchange_uri,
+                             &pc->exchange_url,
                              NULL, NULL);
   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   {
@@ -405,10 +409,10 @@ prepare_pickup (struct PickupContext *pc)
     return TMH_RESPONSE_reply_rc (pc->connection,
                                  response_code,
                                  ec,
-                                 "Could not determine exchange URI for the 
given tip id");
+                                 "Could not determine exchange URL for the 
given tip id");
 
   }
-  pc->fo = TMH_EXCHANGES_find_exchange (pc->exchange_uri,
+  pc->fo = TMH_EXCHANGES_find_exchange (pc->exchange_url,
                                        NULL,
                                        &exchange_found_cb,
                                        pc);
diff --git a/src/backend/taler-merchant-httpd_tip-query.c 
b/src/backend/taler-merchant-httpd_tip-query.c
index 7e236f8..7139b3f 100644
--- a/src/backend/taler-merchant-httpd_tip-query.c
+++ b/src/backend/taler-merchant-httpd_tip-query.c
@@ -1,9 +1,9 @@
 /*
   This file is part of TALER
-  (C) 2017 Taler Systems SA
+  (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
+  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
@@ -15,15 +15,14 @@
 */
 /**
  * @file backend/taler-merchant-httpd_tip-query.c
- * @brief implementation of /tip-query handler
+ * @brief implement API for authorizing tips to be paid to visitors
  * @author Christian Grothoff
  * @author Florian Dold
  */
 #include "platform.h"
-#include <microhttpd.h>
 #include <jansson.h>
+#include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
 #include "taler-merchant-httpd.h"
 #include "taler-merchant-httpd_mhd.h"
 #include "taler-merchant-httpd_parsing.h"
@@ -33,8 +32,346 @@
 
 
 /**
- * Manages a /tip-query call, checking if a tip authorization
- * exists and, if so, returning its details.
+ * Maximum number of retries for database operations.
+ */
+#define MAX_RETRIES 5
+
+
+struct TipQueryContext
+{
+  /**
+   * This field MUST be first.
+   * FIXME: Explain why!
+   */
+  struct TM_HandlerContext hc;
+
+  /**
+   * HTTP connection we are handling.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * Merchant instance to use.
+   */
+  const char *instance;
+
+  /**
+   * Handle to pending /reserve/status request.
+   */
+  struct TALER_EXCHANGE_ReserveStatusHandle *rsh;
+
+  /**
+   * Handle for operation to obtain exchange handle.
+   */
+  struct TMH_EXCHANGES_FindOperation *fo;
+
+  /**
+   * Reserve expiration time as provided by the exchange.
+   * Set in #exchange_cont.
+   */
+  struct GNUNET_TIME_Relative idle_reserve_expiration_time;
+
+  /**
+   * Tip amount requested.
+   */
+  struct TALER_Amount amount_deposited;
+
+  /**
+   * Tip amount requested.
+   */
+  struct TALER_Amount amount_withdrawn;
+
+  /**
+   * Amount authorized.
+   */
+  struct TALER_Amount amount_authorized;
+
+  /**
+   * Private key used by this merchant for the tipping reserve.
+   */
+  struct TALER_ReservePrivateKeyP reserve_priv;
+
+  /**
+   * No tips were authorized yet.
+   */
+  int none_authorized;
+
+  /**
+   * Response to return, NULL if we don't have one yet.
+   */
+  struct MHD_Response *response;
+
+  /**
+   * HTTP status code to use for the reply, i.e 200 for "OK".
+   * Special value UINT_MAX is used to indicate hard errors
+   * (no reply, return #MHD_NO).
+   */
+  unsigned int response_code;
+
+  /**
+   * #GNUNET_NO if the @e connection was not suspended,
+   * #GNUNET_YES if the @e connection was suspended,
+   * #GNUNET_SYSERR if @e connection was resumed to as
+   * part of #MH_force_pc_resume during shutdown.
+   */
+  int suspended;
+};
+
+
+/**
+ * Custom cleanup routine for a `struct TipQueryContext`.
+ *
+ * @param hc the `struct TMH_JsonParseContext` to clean up.
+ */
+static void
+cleanup_tqc (struct TM_HandlerContext *hc)
+{
+  struct TipQueryContext *tqc = (struct TipQueryContext *) hc;
+
+  if (NULL != tqc->rsh)
+  {
+    TALER_EXCHANGE_reserve_status_cancel (tqc->rsh);
+    tqc->rsh = NULL;
+  }
+  if (NULL != tqc->fo)
+  {
+    TMH_EXCHANGES_find_exchange_cancel (tqc->fo);
+    tqc->fo = NULL;
+  }
+  GNUNET_free (tqc);
+}
+
+
+/**
+ * Resume the given context and send the given response.  Stores the response
+ * in the @a pc and signals MHD to resume the connection.  Also ensures MHD
+ * runs immediately.
+ *
+ * @param pc payment context
+ * @param response_code response code to use
+ * @param response response data to send back
+ */
+static void
+resume_with_response (struct TipQueryContext *tqc,
+                      unsigned int response_code,
+                      struct MHD_Response *response)
+{
+  tqc->response_code = response_code;
+  tqc->response = response;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Resuming /tip-query response (%u)\n",
+              response_code);
+  GNUNET_assert (GNUNET_YES == tqc->suspended);
+  tqc->suspended = GNUNET_NO;
+  MHD_resume_connection (tqc->connection);
+  TMH_trigger_daemon (); /* we resumed, kick MHD */
+}
+
+
+/**
+ * Function called with the result of the /reserve/status request
+ * for the tipping reserve.  Update our database balance with the
+ * result.
+ *
+ * @param cls closure with a `struct TipAuthContext *'
+ * @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[in] json original response in JSON format (useful only for 
diagnostics)
+ * @param balance current balance in the reserve, NULL on error
+ * @param history_length number of entries in the transaction history, 0 on 
error
+ * @param history detailed transaction history, NULL on error
+ */
+static void
+handle_status (void *cls,
+               unsigned int http_status,
+               enum TALER_ErrorCode ec,
+               const json_t *json,
+               const struct TALER_Amount *balance,
+               unsigned int history_length,
+               const struct TALER_EXCHANGE_ReserveHistory *history)
+{
+  struct TipQueryContext *tqc = cls;
+  struct GNUNET_TIME_Absolute expiration;
+
+  tqc->rsh = NULL;
+  if (MHD_HTTP_OK != http_status)
+  {
+    GNUNET_break_op (0);
+    resume_with_response (tqc, MHD_HTTP_SERVICE_UNAVAILABLE,
+                          TMH_RESPONSE_make_error (TALER_EC_NONE /* FIXME */,
+                                                   "Unable to obtain reserve 
status from exchange"));
+    return;
+  }
+
+  if (0 == history_length)
+  {
+    GNUNET_break_op (0);
+    resume_with_response (tqc, MHD_HTTP_SERVICE_UNAVAILABLE,
+                          TMH_RESPONSE_make_error (TALER_EC_NONE /* FIXME */,
+                                                   "Exchange returned empty 
reserve history"));
+    return;
+  }
+
+  if (TALER_EXCHANGE_RTT_DEPOSIT != history[0].type)
+  {
+    GNUNET_break_op (0);
+    resume_with_response (tqc, MHD_HTTP_SERVICE_UNAVAILABLE,
+                          TMH_RESPONSE_make_error (TALER_EC_NONE /* FIXME */,
+                                                   "Exchange returned invalid 
reserve history"));
+    return;
+  }
+
+  if (GNUNET_OK != TALER_amount_get_zero (history[0].amount.currency, 
&tqc->amount_withdrawn))
+  {
+    GNUNET_break_op (0);
+    resume_with_response (tqc, MHD_HTTP_SERVICE_UNAVAILABLE,
+                          TMH_RESPONSE_make_error (TALER_EC_NONE /* FIXME */,
+                                                   "Exchange returned invalid 
reserve history"));
+    return;
+  }
+
+  if (GNUNET_YES == tqc->none_authorized)
+    memcpy (&tqc->amount_authorized, &tqc->amount_withdrawn, sizeof (struct 
TALER_Amount));
+  memcpy (&tqc->amount_deposited, &tqc->amount_withdrawn, sizeof (struct 
TALER_Amount));
+
+  /* Update DB based on status! */
+  for (unsigned int i=0;i<history_length;i++)
+  {
+    switch (history[i].type)
+    {
+    case TALER_EXCHANGE_RTT_DEPOSIT:
+      {
+        enum GNUNET_DB_QueryStatus qs;
+        struct GNUNET_HashCode uuid;
+
+        expiration = GNUNET_TIME_absolute_add 
(history[i].details.in_details.timestamp,
+                                               
tqc->idle_reserve_expiration_time);
+        GNUNET_CRYPTO_hash (history[i].details.in_details.wire_reference,
+                            history[i].details.in_details.wire_reference_size,
+                            &uuid);
+        qs = db->enable_tip_reserve (db->cls,
+                                     &tqc->reserve_priv,
+                                     &uuid,
+                                     &history[i].amount,
+                                     expiration);
+        if (GNUNET_OK != TALER_amount_add (&tqc->amount_deposited,
+                                           &tqc->amount_deposited,
+                                           &history[i].amount))
+        {
+          GNUNET_break_op (0);
+          resume_with_response (tqc, MHD_HTTP_SERVICE_UNAVAILABLE,
+                                TMH_RESPONSE_make_error (TALER_EC_NONE /* 
FIXME */,
+                                                         "Exchange returned 
invalid reserve history (amount overflow)"));
+          return;
+        }
+
+        if (0 > qs)
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      _("Database error updating tipping reserve status: 
%d\n"),
+                      qs);
+        }
+      }
+      break;
+    case TALER_EXCHANGE_RTT_WITHDRAWAL:
+      if (GNUNET_OK != TALER_amount_add (&tqc->amount_withdrawn,
+                                         &tqc->amount_withdrawn,
+                                         &history[i].amount))
+      {
+        GNUNET_break_op (0);
+        resume_with_response (tqc, MHD_HTTP_SERVICE_UNAVAILABLE,
+                              TMH_RESPONSE_make_error (TALER_EC_NONE /* FIXME 
*/,
+                                                       "Exchange returned 
invalid reserve history (amount overflow)"));
+        return;
+      }
+      break;
+    case TALER_EXCHANGE_RTT_PAYBACK:
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  _("Encountered unsupported /payback operation on tipping 
reserve\n"));
+      break;
+    case TALER_EXCHANGE_RTT_CLOSE:
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  _("Exchange closed reserve (due to expiration), balance 
calulation is likely wrong. Please create a fresh reserve.\n"));
+      break;
+    }
+  }
+
+  {
+    struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
+    struct TALER_Amount amount_available;
+    GNUNET_CRYPTO_eddsa_key_get_public (&tqc->reserve_priv.eddsa_priv,
+                                        &reserve_pub);
+    if (GNUNET_SYSERR == TALER_amount_subtract (&amount_available, 
&tqc->amount_deposited, &tqc->amount_withdrawn))
+    {
+        GNUNET_break_op (0);
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "amount overflow, deposited %s 
but withdrawn %s\n",
+                    TALER_amount_to_string (&tqc->amount_deposited),
+                    TALER_amount_to_string (&tqc->amount_withdrawn));
+
+        resume_with_response (tqc, MHD_HTTP_SERVICE_UNAVAILABLE,
+                              TMH_RESPONSE_make_error (TALER_EC_NONE /* FIXME 
*/,
+                                                       "Exchange returned 
invalid reserve history (amount overflow)"));
+    }
+    resume_with_response (tqc, MHD_HTTP_OK,
+                          TMH_RESPONSE_make_json_pack ("{s:o, s:o, s:o, s:o, 
s:o}",
+                                                       "reserve_pub",
+                                                       
GNUNET_JSON_from_data_auto (&reserve_pub),
+                                                       "reserve_expiration",
+                                                       
GNUNET_JSON_from_time_abs (expiration),
+                                                       "amount_authorized",
+                                                       TALER_JSON_from_amount 
(&tqc->amount_authorized),
+                                                       "amount_picked_up",
+                                                       TALER_JSON_from_amount 
(&tqc->amount_withdrawn),
+                                                       "amount_available",
+                                                       TALER_JSON_from_amount 
(&amount_available)));
+  }
+}
+
+
+/**
+ * Function called with the result of a #TMH_EXCHANGES_find_exchange()
+ * operation.
+ *
+ * @param cls closure with a `struct TipQueryContext *'
+ * @param eh handle to the exchange context
+ * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if 
not available
+ * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config
+ */
+static void
+exchange_cont (void *cls,
+               struct TALER_EXCHANGE_Handle *eh,
+               const struct TALER_Amount *wire_fee,
+               int exchange_trusted)
+{
+  struct TipQueryContext *tqc = cls;
+  struct TALER_ReservePublicKeyP reserve_pub;
+  const struct TALER_EXCHANGE_Keys *keys;
+
+  tqc->fo = NULL;
+  if (NULL == eh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to contact exchange configured for tipping!\n"));
+    MHD_resume_connection (tqc->connection);
+    TMH_trigger_daemon ();
+    return;
+  }
+  keys = TALER_EXCHANGE_get_keys (eh);
+  GNUNET_assert (NULL != keys);
+  tqc->idle_reserve_expiration_time
+    = keys->reserve_closing_delay;
+  GNUNET_CRYPTO_eddsa_key_get_public (&tqc->reserve_priv.eddsa_priv,
+                                      &reserve_pub.eddsa_pub);
+  tqc->rsh = TALER_EXCHANGE_reserve_status (eh,
+                                            &reserve_pub,
+                                            &handle_status,
+                                            tqc);
+}
+
+
+/**
+ * Handle a "/tip-query" request.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
@@ -45,58 +382,116 @@
  */
 int
 MH_handler_tip_query (struct TMH_RequestHandler *rh,
-                       struct MHD_Connection *connection,
-                       void **connection_cls,
-                       const char *upload_data,
-                       size_t *upload_data_size)
+                          struct MHD_Connection *connection,
+                          void **connection_cls,
+                          const char *upload_data,
+                          size_t *upload_data_size)
 {
-  const char *tip_id_str;
-  struct GNUNET_HashCode tip_id;
-
-  tip_id_str = MHD_lookup_connection_value (connection,
-                                            MHD_GET_ARGUMENT_KIND,
-                                            "tip_id");
-  if (NULL == tip_id_str)
-    return TMH_RESPONSE_reply_arg_missing (connection,
-                                          TALER_EC_PARAMETER_MISSING,
-                                           "tip_id");
+  struct TipQueryContext *tqc;
+  int res;
+  struct MerchantInstance *mi;
 
-  if (GNUNET_OK != GNUNET_STRINGS_string_to_data (tip_id_str,
-                                                  strlen (tip_id_str), &tip_id,
-                                                  sizeof (struct 
GNUNET_HashCode)))
-    return TMH_RESPONSE_reply_arg_invalid (connection,
-                                          TALER_EC_PARAMETER_MISSING,
-                                           "tip_id");
+  if (NULL == *connection_cls)
+  {
+    tqc = GNUNET_new (struct TipQueryContext);
+    tqc->hc.cc = &cleanup_tqc;
+    tqc->connection = connection;
+    *connection_cls = tqc;
+  }
+  else
+  {
+    tqc = *connection_cls;
+  }
 
-  enum GNUNET_DB_QueryStatus qs;
-  struct TALER_Amount tip_amount;
-  struct GNUNET_TIME_Absolute tip_timestamp;
-  char *tip_exchange_uri;
+  if (0 != tqc->response_code)
+  {
+    /* We are *done* processing the request, just queue the response (!) */
+    if (UINT_MAX == tqc->response_code)
+    {
+      GNUNET_break (0);
+      return MHD_NO; /* hard error */
+    }
+    res = MHD_queue_response (connection,
+                             tqc->response_code,
+                             tqc->response);
+    MHD_destroy_response (tqc->response);
+    tqc->response = NULL;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Queueing response (%u) for /tip-query (%s).\n",
+               (unsigned int) tqc->response_code,
+               res ? "OK" : "FAILED");
+    return res;
+  }
 
-  qs = db->lookup_tip_by_id (db->cls,
-                             &tip_id,
-                             &tip_exchange_uri,
-                             &tip_amount,
-                             &tip_timestamp);
+  tqc->instance = MHD_lookup_connection_value (connection,
+                                               MHD_GET_ARGUMENT_KIND,
+                                               "instance");
+  if (NULL == tqc->instance)
+    return TMH_RESPONSE_reply_arg_missing (connection,
+                                          TALER_EC_PARAMETER_MISSING,
+                                           "instance");
 
-  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+  mi = TMH_lookup_instance (tqc->instance);
+  if (NULL == mi)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Instance `%s' not configured\n",
+                tqc->instance);
+    return TMH_RESPONSE_reply_not_found (connection,
+                                        
TALER_EC_TIP_AUTHORIZE_INSTANCE_UNKNOWN,
+                                        "unknown instance");
+  }
+  if (NULL == mi->tip_exchange)
   {
-    return TMH_RESPONSE_reply_rc (connection,
-                                  MHD_HTTP_NOT_FOUND,
-                                  TALER_EC_TIP_QUERY_TIP_ID_UNKNOWN,
-                                  "tip id not found");
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Instance `%s' not configured for tipping\n",
+                tqc->instance);
+    return TMH_RESPONSE_reply_not_found (connection,
+                                        
TALER_EC_TIP_AUTHORIZE_INSTANCE_DOES_NOT_TIP,
+                                        "exchange for tipping not configured 
for the instance");
   }
-  else if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+  tqc->reserve_priv = mi->tip_reserve;
+
   {
-      return TMH_RESPONSE_reply_json_pack (connection,
-                                           MHD_HTTP_OK,
-                                           "{s:s, s:s}",
-                                           "exchange", tip_exchange_uri,
-                                           "timestamp", 
GNUNET_JSON_from_time_abs (tip_timestamp),
-                                           "amount", TALER_JSON_from_amount 
(&tip_amount));
+    int qs;
+    for (unsigned int i=0;i<MAX_RETRIES;i++)
+    {
+      qs = db->get_authorized_tip_amount (db->cls,
+                                          &tqc->reserve_priv,
+                                          &tqc->amount_authorized);
+      if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
+        break;
+    }
+    if (0 > qs)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Database hard error on get_authorized_tip_amount\n");
+      return TMH_RESPONSE_reply_internal_error (connection,
+                                                TALER_EC_NONE /* FIXME */,
+                                                "Merchant database error");
+    }
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+    {
+      // We'll amount_authorized to zero later once
+      // we know the currency
+      tqc->none_authorized = GNUNET_YES;
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "DB::::: authorized amount: 
NONE\n");
+    }
+    else
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "DB::::: authorized amount: %s\n", 
TALER_amount_to_string (&tqc->amount_authorized));
+    }
   }
-  return TMH_RESPONSE_reply_rc (connection,
-                                MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                TALER_EC_INTERNAL_INVARIANT_FAILURE,
-                                "tip lookup failure");
+
+
+  MHD_suspend_connection (connection);
+  tqc->suspended = GNUNET_YES;
+
+  tqc->fo = TMH_EXCHANGES_find_exchange (mi->tip_exchange,
+                                         NULL,
+                                         &exchange_cont,
+                                         tqc);
+  return MHD_YES;
 }
+
+/* end of taler-merchant-httpd_tip-query.c */
diff --git a/src/backend/taler-merchant-httpd_tip-query.h 
b/src/backend/taler-merchant-httpd_tip-query.h
index ec8358d..f3a9ebf 100644
--- a/src/backend/taler-merchant-httpd_tip-query.h
+++ b/src/backend/taler-merchant-httpd_tip-query.h
@@ -16,7 +16,6 @@
 /**
  * @file backend/taler-merchant-httpd_tip-query.h
  * @brief headers for /tip-query handler
- * @author Christian Grothoff
  * @author Florian Dold
  */
 #ifndef TALER_MERCHANT_HTTPD_TIP_QUERY_H
@@ -25,8 +24,7 @@
 #include "taler-merchant-httpd.h"
 
 /**
- * Manages a /tip-query call, checking if a tip authorization
- * exists and, if so, returning its details.
+ * Manages a /tip-query call.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
diff --git a/src/backend/taler-merchant-httpd_track-transaction.c 
b/src/backend/taler-merchant-httpd_track-transaction.c
index 7a4d0c0..99bd33b 100644
--- a/src/backend/taler-merchant-httpd_track-transaction.c
+++ b/src/backend/taler-merchant-httpd_track-transaction.c
@@ -33,6 +33,41 @@
 
 
 /**
+ * Information about a wire transfer for a /track/transaction response.
+ */
+struct TransactionWireTransfer
+{
+
+  /**
+   * Wire transfer identifier this struct is about.
+   */
+  struct TALER_WireTransferIdentifierRawP wtid;
+
+  /**
+   * When was this wire transfer executed?
+   */
+  struct GNUNET_TIME_Absolute execution_time;
+
+  /**
+   * Number of coins of the selected transaction that
+   * is covered by this wire transfer.
+   */
+  unsigned int num_coins;
+
+  /**
+   * Information about the coins of the selected transaction
+   * that are part of the wire transfer.
+   */
+  struct TALER_MERCHANT_CoinWireTransfer *coins;
+
+  /**
+   * URL of the exchange that executed the wire transfer.
+   */
+  char *exchange_url;
+};
+
+
+/**
  * Map containing all the known merchant instances
  */
 extern struct GNUNET_CONTAINER_MultiHashMap *by_id_map;
@@ -53,13 +88,11 @@ extern struct GNUNET_CONTAINER_MultiHashMap *by_id_map;
  *
  * @param num_transfers how many wire transfers make up the transaction
  * @param transfers data on each wire transfer
- * @param exchange_uri URI of the exchange that made the transfer
  * @return MHD response object
  */
 static struct MHD_Response *
 make_track_transaction_ok (unsigned int num_transfers,
-                          const struct TALER_MERCHANT_TransactionWireTransfer 
*transfers,
-                          const char *exchange_uri)
+                          const struct TransactionWireTransfer *transfers)
 {
   struct MHD_Response *ret;
   json_t *j_transfers;
@@ -68,7 +101,7 @@ make_track_transaction_ok (unsigned int num_transfers,
   j_transfers = json_array ();
   for (unsigned int i=0;i<num_transfers;i++)
   {
-    const struct TALER_MERCHANT_TransactionWireTransfer *transfer = 
&transfers[i];
+    const struct TransactionWireTransfer *transfer = &transfers[i];
 
     sum = transfer->coins[0].amount_with_fee;
     for (unsigned int j=1;j<transfer->num_coins;j++)
@@ -84,7 +117,7 @@ make_track_transaction_ok (unsigned int num_transfers,
     GNUNET_assert (0 ==
                    json_array_append_new (j_transfers,
                                           json_pack ("{s:s, s:o, s:o, s:o}",
-                                                     "exchange", exchange_uri,
+                                                     "exchange", 
transfer->exchange_url,
                                                      "wtid", 
GNUNET_JSON_from_data_auto (&transfer->wtid),
                                                      "execution_time", 
GNUNET_JSON_from_time_abs (transfer->execution_time),
                                                      "amount", 
TALER_JSON_from_amount (&sum))));
@@ -121,7 +154,7 @@ struct TrackCoinContext
   struct TrackCoinContext *prev;
 
   /**
-   * Our Context for a /track/transaction operation.
+   * Our context for a /track/transaction operation.
    */
   struct TrackTransactionContext *tctx;
 
@@ -131,6 +164,11 @@ struct TrackCoinContext
   struct TALER_CoinSpendPublicKeyP coin_pub;
 
   /**
+   * Exchange that was used for the transaction.
+   */
+  char *exchange_url;
+
+  /**
    * Handle for the request to resolve the WTID for this coin.
    */
   struct TALER_EXCHANGE_TrackTransactionHandle *dwh;
@@ -190,11 +228,6 @@ struct TrackTransactionContext
   struct TrackCoinContext *tcc_tail;
 
   /**
-   * Exchange that was used for the transaction.
-   */
-  char *exchange_uri;
-
-  /**
    * Task run on timeout.
    */
   struct GNUNET_SCHEDULER_Task *timeout_task;
@@ -212,6 +245,11 @@ struct TrackTransactionContext
   struct TALER_EXCHANGE_Handle *eh;
 
   /**
+   * URL of the exchange we currently have in @e eh.
+   */
+  const char *current_exchange;
+  
+  /**
    * Handle we use to resolve transactions for a given WTID.
    */
   struct TALER_EXCHANGE_TrackTransferHandle *wdh;
@@ -300,6 +338,11 @@ free_tctx (struct TrackTransactionContext *tctx)
       TALER_EXCHANGE_track_transaction_cancel (tcc->dwh);
       tcc->dwh = NULL;
     }
+    if (NULL != tcc->exchange_url)
+    {
+      GNUNET_free (tcc->exchange_url);
+      tcc->exchange_url = NULL;
+    }
     GNUNET_free (tcc);
   }
   if (NULL != tctx->wdh)
@@ -317,11 +360,6 @@ free_tctx (struct TrackTransactionContext *tctx)
     GNUNET_SCHEDULER_cancel (tctx->timeout_task);
     tctx->timeout_task = NULL;
   }
-  if (NULL != tctx->exchange_uri)
-  {
-    GNUNET_free (tctx->exchange_uri);
-    tctx->exchange_uri = NULL;
-  }
   GNUNET_free (tctx);
 }
 
@@ -435,7 +473,7 @@ wire_deposits_cb (void *cls,
   for (unsigned int i=0;i<MAX_RETRIES;i++)
   {
     qs = db->store_transfer_to_proof (db->cls,
-                                     tctx->exchange_uri,
+                                     tctx->current_exchange,
                                      &tctx->current_wtid,
                                      tctx->current_execution_time,
                                      exchange_pub,
@@ -587,8 +625,9 @@ wtid_cb (void *cls,
   tctx->current_wtid = *wtid;
   tctx->current_execution_time = execution_time;
   pcc.p_ret = NULL;
+  /* FIXME: change to avoid using callback! */
   qs = db->find_proof_by_wtid (db->cls,
-                              tctx->exchange_uri,
+                              tctx->current_exchange,
                               wtid,
                               &proof_cb,
                               &pcc);
@@ -601,8 +640,8 @@ wtid_cb (void *cls,
     resume_track_transaction_with_response
       (tcc->tctx,
        MHD_HTTP_INTERNAL_SERVER_ERROR,
-       TMH_RESPONSE_make_internal_error 
(TALER_EC_TRACK_TRANSACTION_DB_FETCH_FAILED,
-                                        "Fail to query database about 
proofs"));
+       TMH_RESPONSE_make_error (TALER_EC_TRACK_TRANSACTION_DB_FETCH_FAILED,
+                               "Fail to query database about proofs"));
     return;
   }
   /* WARNING: if two transactions got aggregated under the same
@@ -630,35 +669,16 @@ wtid_cb (void *cls,
 
 
 /**
- * This function is called to trace the wire transfers for
- * all of the coins of the transaction of the @a tctx.  Once
- * we have traced all coins, we build the response.
+ * We have obtained all WTIDs, now prepare the response 
  *
- * @param tctx track context with established connection to exchange
+ * @param tctx handle for the operation 
  */
 static void
-trace_coins (struct TrackTransactionContext *tctx)
+generate_response (struct TrackTransactionContext *tctx)
 {
   struct TrackCoinContext *tcc;
   unsigned int num_wtid;
 
-  GNUNET_assert (NULL != tctx->eh);
-  for (tcc = tctx->tcc_head; NULL != tcc; tcc = tcc->next)
-    if (GNUNET_YES != tcc->have_wtid)
-      break;
-  if (NULL != tcc)
-  {
-    /* we are not done requesting WTIDs, do the next one */
-    tcc->dwh = TALER_EXCHANGE_track_transaction (tctx->eh,
-                                                 &tctx->mi->privkey,
-                                                 &tctx->h_wire,
-                                                 &tctx->h_contract_terms,
-                                                 &tcc->coin_pub,
-                                                 &wtid_cb,
-                                                 tcc);
-    return;
-  }
-  /* We have obtained all WTIDs, now prepare the response */
   num_wtid = 0;
   /* count how many disjoint wire transfer identifiers there are;
      note that there should only usually be one, so while this
@@ -687,7 +707,7 @@ trace_coins (struct TrackTransactionContext *tctx)
     /* on-stack allocation is fine, as the number of coins and the
        number of wire-transfers per-transaction is expected to be tiny. */
     struct MHD_Response *resp;
-    struct TALER_MERCHANT_TransactionWireTransfer wts[num_wtid];
+    struct TransactionWireTransfer wts[num_wtid];
     unsigned int wtid_off;
 
     wtid_off = 0;
@@ -710,10 +730,11 @@ trace_coins (struct TrackTransactionContext *tctx)
       if (GNUNET_NO == found)
       {
         unsigned int num_coins;
-        struct TALER_MERCHANT_TransactionWireTransfer *wt;
+        struct TransactionWireTransfer *wt;
 
         wt = &wts[wtid_off++];
         wt->wtid = tcc->wtid;
+       wt->exchange_url = tcc->exchange_url;
         wt->execution_time = tcc->execution_time;
         /* count number of coins with this wtid */
         num_coins = 0;
@@ -751,8 +772,7 @@ trace_coins (struct TrackTransactionContext *tctx)
     GNUNET_assert (wtid_off == num_wtid);
 
     resp = make_track_transaction_ok (num_wtid,
-                                     wts,
-                                     tctx->exchange_uri);
+                                     wts);
     for (wtid_off=0;wtid_off < num_wtid; wtid_off++)
       GNUNET_free (wts[wtid_off].coins);
     resume_track_transaction_with_response (tctx,
@@ -763,6 +783,59 @@ trace_coins (struct TrackTransactionContext *tctx)
 
 
 /**
+ * Find the exchange to trace the next coin(s).
+ *
+ * @param tctx operation context
+ */ 
+static void
+find_exchange (struct TrackTransactionContext *tctx);
+
+
+/**
+ * This function is called to trace the wire transfers for
+ * all of the coins of the transaction of the @a tctx.  Once
+ * we have traced all coins, we build the response.
+ *
+ * @param tctx track context with established connection to exchange
+ */
+static void
+trace_coins (struct TrackTransactionContext *tctx)
+{
+  struct TrackCoinContext *tcc;
+
+  GNUNET_assert (NULL != tctx->eh);
+  for (tcc = tctx->tcc_head; NULL != tcc; tcc = tcc->next)
+    if (GNUNET_YES != tcc->have_wtid)
+      break;
+  if (NULL != tcc)
+  {
+    if (0 != strcmp (tcc->exchange_url,
+                    tctx->current_exchange))
+    {
+      /* exchange changed, find matching one first! */
+      tctx->eh = NULL;
+      tctx->current_exchange = NULL;
+      find_exchange (tctx);
+      return;
+    }
+    /* we are not done requesting WTIDs from the current
+       exchange; do the next one */
+    tcc->dwh = TALER_EXCHANGE_track_transaction (tctx->eh,
+                                                 &tctx->mi->privkey,
+                                                 &tctx->h_wire,
+                                                 &tctx->h_contract_terms,
+                                                 &tcc->coin_pub,
+                                                 &wtid_cb,
+                                                 tcc);
+    return;
+  }
+  tctx->current_exchange = NULL;
+  tctx->eh = NULL;
+  generate_response (tctx);
+}
+
+
+/**
  * Function called with the result of our exchange lookup.
  *
  * @param cls the `struct TrackTransactionContext`
@@ -805,48 +878,12 @@ handle_track_transaction_timeout (void *cls)
   }
   resume_track_transaction_with_response (tctx,
                                           MHD_HTTP_SERVICE_UNAVAILABLE,
-                                          TMH_RESPONSE_make_internal_error 
(TALER_EC_PAY_EXCHANGE_TIMEOUT,
+                                          TMH_RESPONSE_make_error 
(TALER_EC_PAY_EXCHANGE_TIMEOUT,
                                                                            
"exchange not reachable"));
 }
 
 
 /**
- * Function called with information about a transaction.
- * Responsible to fill up the "context" for the whole
- * tracking operation.
- *
- * @param cls closure
- * @param transaction_id of the contract
- * @param merchant's public key
- * @param exchange_uri URI of the exchange
- * @param h_contract hash of the contract
- * @param h_wire hash of our wire details
- * @param timestamp time of the confirmation
- * @param refund refund deadline
- * @param total_amount total amount we receive for the contract after fees
- */
-static void
-transaction_cb (void *cls,
-               const struct TALER_MerchantPublicKeyP *merchant_pub,
-                const char *exchange_uri,
-                const struct GNUNET_HashCode *h_contract_terms,
-                const struct GNUNET_HashCode *h_wire,
-                struct GNUNET_TIME_Absolute timestamp,
-                struct GNUNET_TIME_Absolute refund,
-                const struct TALER_Amount *total_amount)
-{
-  struct TrackTransactionContext *tctx = cls;
-
-  tctx->h_contract_terms = *h_contract_terms;
-  tctx->exchange_uri = GNUNET_strdup (exchange_uri);
-  tctx->h_wire = *h_wire;
-  tctx->timestamp = timestamp;
-  tctx->refund_deadline = refund;
-  tctx->total_amount = *total_amount;
-}
-
-
-/**
  * Information about the wire transfer corresponding to
  * a deposit operation.  Note that it is in theory possible
  * that we have a @a transaction_id and @a coin_pub in the
@@ -865,7 +902,7 @@ transaction_cb (void *cls,
  */
 static void
 transfer_cb (void *cls,
-             const struct GNUNET_HashCode *h_contract_terms,
+            const struct GNUNET_HashCode *h_contract_terms,
              const struct TALER_CoinSpendPublicKeyP *coin_pub,
              const struct TALER_WireTransferIdentifierRawP *wtid,
              struct GNUNET_TIME_Absolute execution_time,
@@ -889,6 +926,7 @@ transfer_cb (void *cls,
  * @param cls closure
  * @param transaction_id of the contract
  * @param coin_pub public key of the coin
+ * @param exchange_url URL of exchange that issued @a coin_pub
  * @param amount_with_fee amount the exchange will deposit for this coin
  * @param deposit_fee fee the exchange will charge for this coin
  * @param refund_fee fee the exchange will charge for refunding this coin
@@ -898,9 +936,11 @@ static void
 coin_cb (void *cls,
          const struct GNUNET_HashCode *h_contract_terms,
          const struct TALER_CoinSpendPublicKeyP *coin_pub,
+        const char *exchange_url,
          const struct TALER_Amount *amount_with_fee,
          const struct TALER_Amount *deposit_fee,
          const struct TALER_Amount *refund_fee,
+         const struct TALER_Amount *wire_fee,
          const json_t *exchange_proof)
 {
   struct TrackTransactionContext *tctx = cls;
@@ -910,6 +950,7 @@ coin_cb (void *cls,
   tcc = GNUNET_new (struct TrackCoinContext);
   tcc->tctx = tctx;
   tcc->coin_pub = *coin_pub;
+  tcc->exchange_url = GNUNET_strdup (exchange_url);
 
   tcc->amount_with_fee = *amount_with_fee;
   tcc->deposit_fee = *deposit_fee;
@@ -929,6 +970,35 @@ coin_cb (void *cls,
 
 
 /**
+ * Find the exchange to trace the next coin(s).
+ *
+ * @param tctx operation context
+ */ 
+static void
+find_exchange (struct TrackTransactionContext *tctx)
+{
+  struct TrackCoinContext *tcc = tctx->tcc_head;
+
+  while ( (NULL != tcc) &&
+         (GNUNET_YES == tcc->have_wtid) )
+    tcc = tcc->next;
+  if (NULL != tcc)
+  {
+    tctx->current_exchange = tcc->exchange_url;
+    tctx->fo = TMH_EXCHANGES_find_exchange (tctx->current_exchange,
+                                           NULL,
+                                           
&process_track_transaction_with_exchange,
+                                           tctx);
+    
+  }
+  else
+  {
+    generate_response (tctx);
+  }
+}
+
+
+/**
  * Handle a "/track/transaction" request.
  *
  * @param rh context of the handler
@@ -951,8 +1021,8 @@ MH_handler_track_transaction (struct TMH_RequestHandler 
*rh,
   int ret;
   enum GNUNET_DB_QueryStatus qs;
   struct GNUNET_HashCode h_instance;
-  struct GNUNET_HashCode h_contract_terms;
   struct json_t *contract_terms;
+  char *last_session_id;
 
   if (NULL == *connection_cls)
   {
@@ -1027,6 +1097,7 @@ MH_handler_track_transaction (struct TMH_RequestHandler 
*rh,
 
   qs = db->find_contract_terms (db->cls,
                                &contract_terms,
+                                &last_session_id,
                                order_id,
                                &tctx->mi->pubkey);
   if (0 > qs)
@@ -1040,54 +1111,49 @@ MH_handler_track_transaction (struct TMH_RequestHandler 
*rh,
     return TMH_RESPONSE_reply_not_found (connection,
                                         TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND,
                                         "Given order_id doesn't map to any 
proposal");
+
+  GNUNET_free (last_session_id);
+
   if (GNUNET_OK !=
       TALER_JSON_hash (contract_terms,
-                       &h_contract_terms))
+                       &tctx->h_contract_terms))
   {
     json_decref (contract_terms);
     return TMH_RESPONSE_reply_internal_error (connection,
                                              TALER_EC_INTERNAL_LOGIC_ERROR,
                                               "Failed to hash contract terms");
   }
-  json_decref (contract_terms);
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Trying to track h_contract_terms '%s'\n",
-              GNUNET_h2s (&h_contract_terms));
-
-  qs = db->find_transaction (db->cls,
-                            &h_contract_terms,
-                            &tctx->mi->pubkey,
-                            &transaction_cb,
-                            tctx);
-  if (0 > qs)
-  {
-    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
-    return TMH_RESPONSE_reply_internal_error (connection,
-                                             
TALER_EC_TRACK_TRANSACTION_DB_FETCH_TRANSACTION_ERROR,
-                                              "Database error finding 
transaction");
-  }
-  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "h_contract_terms not found\n");
-    return TMH_RESPONSE_reply_not_found (connection,
-                                        
TALER_EC_TRACK_TRANSACTION_TRANSACTION_UNKNOWN,
-                                         "h_contract_terms is unknown");
-  }
-  if ( (0 != memcmp (&tctx->h_contract_terms,
-                     &h_contract_terms,
-                     sizeof (struct GNUNET_HashCode))) ||
-       (NULL == tctx->exchange_uri) )
-  {
-    GNUNET_break (0);
-    return TMH_RESPONSE_reply_internal_error (connection,
-                                             
TALER_EC_TRACK_TRANSACTION_DB_FETCH_TRANSACTION_ERROR,
-                                              "Database error: failed to 
obtain correct data from database");
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_absolute_time ("refund_deadline",
+                                      &tctx->refund_deadline),
+      GNUNET_JSON_spec_absolute_time ("timestamp",
+                                      &tctx->timestamp),
+      TALER_JSON_spec_amount ("amount",
+                              &tctx->total_amount),
+      GNUNET_JSON_spec_fixed_auto ("H_wire",
+                                   &tctx->h_wire),
+      GNUNET_JSON_spec_end()
+    };
+
+    if (GNUNET_YES != GNUNET_JSON_parse (contract_terms,
+                                         spec,
+                                         NULL, NULL))
+    {
+      GNUNET_break (0);
+      GNUNET_JSON_parse_free (spec);
+      json_decref (contract_terms);
+      return TMH_RESPONSE_reply_internal_error (connection,
+                                                TALER_EC_INTERNAL_LOGIC_ERROR,
+                                                "Failed to parse contract 
terms from DB");
+    }
+    json_decref (contract_terms);
   }
+
   tctx->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
   qs = db->find_payments (db->cls,
-                         &h_contract_terms,
+                         &tctx->h_contract_terms,
                          &tctx->mi->pubkey,
                          &coin_cb,
                          tctx);
@@ -1111,16 +1177,13 @@ MH_handler_track_transaction (struct TMH_RequestHandler 
*rh,
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Suspending /track/transaction handling while working with the 
exchange\n");
   MHD_suspend_connection (connection);
-  tctx->fo = TMH_EXCHANGES_find_exchange (tctx->exchange_uri,
-                                          NULL,
-                                          
&process_track_transaction_with_exchange,
-                                          tctx);
-
   tctx->timeout_task
     = GNUNET_SCHEDULER_add_delayed (TRACK_TIMEOUT,
                                    &handle_track_transaction_timeout,
                                    tctx);
+  find_exchange (tctx);
   return MHD_YES;
 }
 
+
 /* end of taler-merchant-httpd_track-transaction.c */
diff --git a/src/backend/taler-merchant-httpd_track-transfer.c 
b/src/backend/taler-merchant-httpd_track-transfer.c
index f66457a..197947f 100644
--- a/src/backend/taler-merchant-httpd_track-transfer.c
+++ b/src/backend/taler-merchant-httpd_track-transfer.c
@@ -91,9 +91,9 @@ struct TrackTransferContext
   struct GNUNET_SCHEDULER_Task *timeout_task;
 
   /**
-   * URI of the exchange.
+   * URL of the exchange.
    */
-  char *uri;
+  char *url;
 
   /**
    * Wire method used for the transfer.
@@ -183,10 +183,10 @@ free_transfer_track_context (struct TrackTransferContext 
*rctx)
     TALER_EXCHANGE_track_transfer_cancel (rctx->wdh);
     rctx->wdh = NULL;
   }
-  if (NULL != rctx->uri)
+  if (NULL != rctx->url)
   {
-    GNUNET_free (rctx->uri);
-    rctx->uri = NULL;
+    GNUNET_free (rctx->url);
+    rctx->url = NULL;
   }
   if (NULL != rctx->wire_method)
   {
@@ -433,6 +433,7 @@ track_transfer_cleanup (struct TM_HandlerContext *hc)
  * @param cls closure with our `struct TrackTransferContext *`
  * @param transaction_id of the contract
  * @param coin_pub public key of the coin
+ * @param exchange_url URL of the exchange that issued @a coin_pub
  * @param amount_with_fee amount the exchange will transfer for this coin
  * @param deposit_fee fee the exchange will charge for this coin
  * @param refund_fee fee the exchange will charge for refunding this coin
@@ -442,9 +443,11 @@ static void
 check_transfer (void *cls,
                 const struct GNUNET_HashCode *h_contract_terms,
                 const struct TALER_CoinSpendPublicKeyP *coin_pub,
+               const char *exchange_url,
                 const struct TALER_Amount *amount_with_fee,
                 const struct TALER_Amount *deposit_fee,
                 const struct TALER_Amount *refund_fee,
+                const struct TALER_Amount *wire_fee,
                 const json_t *exchange_proof)
 {
   struct TrackTransferContext *rctx = cls;
@@ -617,7 +620,7 @@ wire_transfer_cb (void *cls,
   for (unsigned int i=0;i<MAX_RETRIES;i++)
   {
     qs = db->store_transfer_to_proof (db->cls,
-                                     rctx->uri,
+                                     rctx->url,
                                      &rctx->wtid,
                                      execution_time,
                                      exchange_pub,
@@ -748,8 +751,8 @@ wire_transfer_cb (void *cls,
     resume_track_transfer_with_response
       (rctx,
        MHD_HTTP_INTERNAL_SERVER_ERROR,
-       TMH_RESPONSE_make_internal_error 
(TALER_EC_TRACK_TRANSFER_JSON_RESPONSE_ERROR,
-                                         "Fail to elaborate the response."));
+       TMH_RESPONSE_make_error (TALER_EC_TRACK_TRANSFER_JSON_RESPONSE_ERROR,
+                               "Fail to elaborate the response."));
     return;
   }
 
@@ -816,7 +819,7 @@ handle_track_transfer_timeout (void *cls)
   }
   resume_track_transfer_with_response (rctx,
                                        MHD_HTTP_SERVICE_UNAVAILABLE,
-                                       TMH_RESPONSE_make_internal_error 
(TALER_EC_TRACK_TRANSFER_EXCHANGE_TIMEOUT,
+                                       TMH_RESPONSE_make_error 
(TALER_EC_TRACK_TRANSFER_EXCHANGE_TIMEOUT,
                                                                         
"exchange not reachable"));
 }
 
@@ -843,8 +846,8 @@ proof_cb (void *cls,
   {
     rctx->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
     rctx->response
-      = TMH_RESPONSE_make_internal_error 
(TALER_EC_TRACK_TRANSFER_JSON_RESPONSE_ERROR,
-                                         "Fail to elaborate response.");
+      = TMH_RESPONSE_make_error (TALER_EC_TRACK_TRANSFER_JSON_RESPONSE_ERROR,
+                                "Fail to elaborate response.");
     return;
   }
 
@@ -875,7 +878,7 @@ MH_handler_track_transfer (struct TMH_RequestHandler *rh,
 {
   struct TrackTransferContext *rctx;
   const char *str;
-  const char *uri;
+  const char *url;
   const char *instance_str;
   const char *wire_method;
   int ret;
@@ -925,14 +928,14 @@ MH_handler_track_transfer (struct TMH_RequestHandler *rh,
     return MHD_YES; /* still work in progress */
   }
 
-  uri = MHD_lookup_connection_value (connection,
+  url = MHD_lookup_connection_value (connection,
                                      MHD_GET_ARGUMENT_KIND,
                                      "exchange");
-  if (NULL == uri)
+  if (NULL == url)
     return TMH_RESPONSE_reply_arg_missing (connection,
                                           TALER_EC_PARAMETER_MISSING,
                                            "exchange");
-  rctx->uri = GNUNET_strdup (uri);
+  rctx->url = GNUNET_strdup (url);
 
   wire_method = MHD_lookup_connection_value (connection,
                                             MHD_GET_ARGUMENT_KIND,
@@ -985,7 +988,7 @@ MH_handler_track_transfer (struct TMH_RequestHandler *rh,
 
   /* Check if reply is already in database! */
   qs = db->find_proof_by_wtid (db->cls,
-                              rctx->uri,
+                              rctx->url,
                               &rctx->wtid,
                               &proof_cb,
                               rctx);
@@ -1019,7 +1022,7 @@ MH_handler_track_transfer (struct TMH_RequestHandler *rh,
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Suspending /track/transfer handling while working with the 
exchange\n");
   MHD_suspend_connection (connection);
-  rctx->fo = TMH_EXCHANGES_find_exchange (uri,
+  rctx->fo = TMH_EXCHANGES_find_exchange (url,
                                           NULL,
                                           
&process_track_transfer_with_exchange,
                                           rctx);
diff --git a/src/backend/taler-merchant-httpd_trigger-pay.c 
b/src/backend/taler-merchant-httpd_trigger-pay.c
new file mode 100644
index 0000000..1b0a7ba
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_trigger-pay.c
@@ -0,0 +1,96 @@
+/*
+  This file is part of TALER
+  (C) 2017 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 backend/taler-merchant-httpd_trigger-pay.c
+ * @brief implementation of /public/trigger-pay handler
+ * @author Florian Dold
+ */
+#include "platform.h"
+#include <string.h>
+#include <microhttpd.h>
+#include <jansson.h>
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+#include "taler-merchant-httpd.h"
+#include "taler-merchant-httpd_mhd.h"
+#include "taler-merchant-httpd_parsing.h"
+#include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_responses.h"
+#include "taler-merchant-httpd_trigger-pay.h"
+
+
+/**
+ * Add a header to the response from a query parameter.
+ *
+ *
+ * @param connection connection to take query parameters from
+ * @param arg_name name of query parameter
+ * @param response response that receives the header
+ * @param header_name name of the header to set
+ */
+void
+add_header_from_arg (struct MHD_Connection *connection, const char *arg_name,
+                     struct MHD_Response *response, const char *header_name)
+{
+  const char *arg = MHD_lookup_connection_value (connection,
+                                                 MHD_GET_ARGUMENT_KIND,
+                                                 arg_name);
+  if (NULL == arg)
+    return;
+
+  MHD_add_response_header (response, header_name, arg);
+}
+
+
+/**
+ * Serves a request to browsers to trigger a payment.
+ * Contains all the logic to handle different platforms, so that the frontend
+ * does not have to handle that.
+ *
+ * @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
+MH_handler_trigger_pay (struct TMH_RequestHandler *rh,
+                        struct MHD_Connection *connection,
+                        void **connection_cls,
+                        const char *upload_data,
+                        size_t *upload_data_size)
+{
+  struct MHD_Response *response;
+
+
+  // FIXME: Taler wallet detection!
+  char *data = "<html><body><p>Processing payment ...</p></body></html>";
+
+  response = MHD_create_response_from_buffer (strlen (data), data, 
MHD_RESPMEM_PERSISTENT);
+
+  add_header_from_arg (connection, "session_id", response, 
"X-Taler-Session-Id");
+  add_header_from_arg (connection, "contract_url", response, 
"X-Taler-Contract-Url");
+  add_header_from_arg (connection, "h_contract_terms", response, 
"X-Taler-Contract-Hash");
+  add_header_from_arg (connection, "tip_token", response, "X-Taler-Tip");
+  add_header_from_arg (connection, "refund_url", response, 
"X-Taler-Refund-Url");
+  add_header_from_arg (connection, "resource_url", response, 
"X-Taler-Resource-Url");
+
+  MHD_queue_response (connection, 402, response);
+  MHD_destroy_response (response);
+
+  return MHD_YES;
+}
diff --git a/src/backend/taler-merchant-httpd_tip-query.h 
b/src/backend/taler-merchant-httpd_trigger-pay.h
similarity index 62%
copy from src/backend/taler-merchant-httpd_tip-query.h
copy to src/backend/taler-merchant-httpd_trigger-pay.h
index ec8358d..b395772 100644
--- a/src/backend/taler-merchant-httpd_tip-query.h
+++ b/src/backend/taler-merchant-httpd_trigger-pay.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  (C) 2017 Taler Systems SA
+  (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
@@ -14,19 +14,19 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file backend/taler-merchant-httpd_tip-query.h
- * @brief headers for /tip-query handler
- * @author Christian Grothoff
+ * @file backend/taler-merchant-httpd_trigger-pay.h
+ * @brief headers for /trigger-pay handler
  * @author Florian Dold
  */
-#ifndef TALER_MERCHANT_HTTPD_TIP_QUERY_H
-#define TALER_MERCHANT_HTTPD_TIP_QUERY_H
+#ifndef TALER_MERCHANT_HTTPD_TRIGGER_PAY_H
+#define TALER_MERCHANT_HTTPD_TRIGGER_PAY_H
 #include <microhttpd.h>
 #include "taler-merchant-httpd.h"
 
 /**
- * Manages a /tip-query call, checking if a tip authorization
- * exists and, if so, returning its details.
+ * Serves a request to browsers to trigger a payment.
+ * Contains all the logic to handle different platforms, so that the frontend
+ * does not have to handle that.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
@@ -36,10 +36,10 @@
  * @return MHD result code
  */
 int
-MH_handler_tip_query (struct TMH_RequestHandler *rh,
-                      struct MHD_Connection *connection,
-                      void **connection_cls,
-                      const char *upload_data,
-                      size_t *upload_data_size);
+MH_handler_trigger_pay (struct TMH_RequestHandler *rh,
+                        struct MHD_Connection *connection,
+                        void **connection_cls,
+                        const char *upload_data,
+                        size_t *upload_data_size);
 
 #endif
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index cb1d9c6..881f682 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -76,6 +76,7 @@ postgres_drop_tables (void *cls)
     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_tip_pickups 
CASCADE;"),
     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS 
merchant_tip_reserve_credits CASCADE;"),
     GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_tip_reserves 
CASCADE;"),
+    GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_orders 
CASCADE;"),
     GNUNET_PQ_EXECUTE_STATEMENT_END
   };
 
@@ -95,6 +96,16 @@ postgres_initialize (void *cls)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_ExecuteStatement es[] = {
+    /* Orders created by the frontend, not signed or given a nonce yet.
+       The contract terms will change (nonce will be added) when moved to the
+       contract terms table */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_orders ("
+                            "order_id VARCHAR NOT NULL"
+                            ",merchant_pub BYTEA NOT NULL CHECK 
(LENGTH(merchant_pub)=32)"
+                            ",contract_terms BYTEA NOT NULL"
+                            ",timestamp INT8 NOT NULL"
+                            ",PRIMARY KEY (order_id, merchant_pub)"
+                            ");"),
     /* Offers we made to customers */
     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS 
merchant_contract_terms ("
                             "order_id VARCHAR NOT NULL"
@@ -104,28 +115,16 @@ postgres_initialize (void *cls)
                             ",timestamp INT8 NOT NULL"
                             ",row_id BIGSERIAL UNIQUE"
                             ",paid boolean DEFAULT FALSE NOT NULL"
+                            ",last_session_id VARCHAR DEFAULT '' NOT NULL"
                             ",PRIMARY KEY (order_id, merchant_pub)"
                            ",UNIQUE (h_contract_terms, merchant_pub)"
                             ");"),
-    /* Contracts that were paid via some exchange (or attempted to be paid???) 
*/
-    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_transactions 
("
-                            " h_contract_terms BYTEA NOT NULL CHECK 
(LENGTH(h_contract_terms)=64)"
-                            ",exchange_uri VARCHAR NOT NULL"
-                            ",merchant_pub BYTEA NOT NULL CHECK 
(LENGTH(merchant_pub)=32)"
-                            ",h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64)"
-                            ",timestamp INT8 NOT NULL"
-                            ",refund_deadline INT8 NOT NULL"
-                            ",total_amount_val INT8 NOT NULL"
-                            ",total_amount_frac INT4 NOT NULL"
-                            ",total_amount_curr VARCHAR(" 
TALER_CURRENCY_LEN_STR ") NOT NULL"
-                            ",PRIMARY KEY (h_contract_terms, merchant_pub)"
-                            ",FOREIGN KEY (h_contract_terms, merchant_pub) 
REFERENCES merchant_contract_terms (h_contract_terms, merchant_pub)"
-                            ");"),
     /* Table with the proofs for each coin we deposited at the exchange */
     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_deposits ("
                             " h_contract_terms BYTEA NOT NULL"
                             ",merchant_pub BYTEA NOT NULL CHECK 
(LENGTH(merchant_pub)=32)"
                             ",coin_pub BYTEA NOT NULL CHECK 
(LENGTH(coin_pub)=32)"
+                            ",exchange_url VARCHAR NOT NULL"
                             ",amount_with_fee_val INT8 NOT NULL"
                             ",amount_with_fee_frac INT4 NOT NULL"
                             ",amount_with_fee_curr VARCHAR(" 
TALER_CURRENCY_LEN_STR ") NOT NULL"
@@ -135,18 +134,21 @@ postgres_initialize (void *cls)
                             ",refund_fee_val INT8 NOT NULL"
                             ",refund_fee_frac INT4 NOT NULL"
                             ",refund_fee_curr VARCHAR(" TALER_CURRENCY_LEN_STR 
") NOT NULL"
+                            ",wire_fee_val INT8 NOT NULL"
+                            ",wire_fee_frac INT4 NOT NULL"
+                            ",wire_fee_curr VARCHAR(" TALER_CURRENCY_LEN_STR 
") NOT NULL"
                             ",signkey_pub BYTEA NOT NULL CHECK 
(LENGTH(signkey_pub)=32)"
                             ",exchange_proof BYTEA NOT NULL"
                             ",PRIMARY KEY (h_contract_terms, coin_pub)"
-                            ",FOREIGN KEY (h_contract_terms, merchant_pub) 
REFERENCES merchant_transactions (h_contract_terms, merchant_pub)"
+                            ",FOREIGN KEY (h_contract_terms, merchant_pub) 
REFERENCES merchant_contract_terms (h_contract_terms, merchant_pub)"
                             ");"),
     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_proofs ("
-                            " exchange_uri VARCHAR NOT NULL"
+                            " exchange_url VARCHAR NOT NULL"
                             ",wtid BYTEA CHECK (LENGTH(wtid)=32)"
                             ",execution_time INT8 NOT NULL"
                             ",signkey_pub BYTEA NOT NULL CHECK 
(LENGTH(signkey_pub)=32)"
                             ",proof BYTEA NOT NULL"
-                            ",PRIMARY KEY (wtid, exchange_uri)"
+                            ",PRIMARY KEY (wtid, exchange_url)"
                             ");"),
     /* Note that h_contract_terms + coin_pub may actually be unknown to
        us, e.g. someone else deposits something for us at the exchange.
@@ -200,7 +202,7 @@ postgres_initialize (void *cls)
     /* table where we remember when tipping reserves where established / 
enabled */
     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS 
merchant_tip_reserve_credits ("
                             " reserve_priv BYTEA NOT NULL CHECK 
(LENGTH(reserve_priv)=32)"
-                            ",credit_uuid BYTEA NOT NULL CHECK 
(LENGTH(credit_uuid)=64)"
+                            ",credit_uuid BYTEA UNIQUE NOT NULL CHECK 
(LENGTH(credit_uuid)=64)"
                             ",timestamp INT8 NOT NULL"
                             ",amount_val INT8 NOT NULL"
                             ",amount_frac INT4 NOT NULL"
@@ -211,7 +213,7 @@ postgres_initialize (void *cls)
     GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_tips ("
                             " reserve_priv BYTEA NOT NULL CHECK 
(LENGTH(reserve_priv)=32)"
                             ",tip_id BYTEA NOT NULL CHECK (LENGTH(tip_id)=64)"
-                           ",exchange_uri VARCHAR NOT NULL"
+                           ",exchange_url VARCHAR NOT NULL"
                             ",justification VARCHAR NOT NULL"
                             ",timestamp INT8 NOT NULL"
                             ",amount_val INT8 NOT NULL" /* overall tip amount 
*/
@@ -234,25 +236,12 @@ postgres_initialize (void *cls)
     GNUNET_PQ_EXECUTE_STATEMENT_END
   };
   struct GNUNET_PQ_PreparedStatement ps[] = {
-    GNUNET_PQ_make_prepare ("insert_transaction",
-                            "INSERT INTO merchant_transactions"
-                            "(h_contract_terms"
-                            ",exchange_uri"
-                            ",merchant_pub"
-                            ",h_wire"
-                            ",timestamp"
-                            ",refund_deadline"
-                            ",total_amount_val"
-                            ",total_amount_frac"
-                            ",total_amount_curr"
-                            ") VALUES "
-                            "($1, $2, $3, $4, $5, $6, $7, $8, $9)",
-                            9),
     GNUNET_PQ_make_prepare ("insert_deposit",
                             "INSERT INTO merchant_deposits"
                             "(h_contract_terms"
                             ",merchant_pub"
                             ",coin_pub"
+                            ",exchange_url"
                             ",amount_with_fee_val"
                             ",amount_with_fee_frac"
                             ",amount_with_fee_curr"
@@ -262,10 +251,13 @@ postgres_initialize (void *cls)
                             ",refund_fee_val"
                             ",refund_fee_frac"
                             ",refund_fee_curr"
+                            ",wire_fee_val"
+                            ",wire_fee_frac"
+                            ",wire_fee_curr"
                             ",signkey_pub"
                             ",exchange_proof) VALUES "
-                            "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, 
$12, $13, $14)",
-                            14),
+                            "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, 
$12, $13, $14, $15, $16, $17, $18)",
+                            18),
     GNUNET_PQ_make_prepare ("insert_transfer",
                             "INSERT INTO merchant_transfers"
                             "(h_contract_terms"
@@ -290,7 +282,7 @@ postgres_initialize (void *cls)
                             10),
     GNUNET_PQ_make_prepare ("insert_proof",
                             "INSERT INTO merchant_proofs"
-                            "(exchange_uri"
+                            "(exchange_url"
                             ",wtid"
                             ",execution_time"
                             ",signkey_pub"
@@ -307,9 +299,19 @@ postgres_initialize (void *cls)
                             " VALUES "
                             "($1, $2, $3, $4, $5)",
                             5),
+    GNUNET_PQ_make_prepare ("insert_order",
+                            "INSERT INTO merchant_orders"
+                            "(order_id"
+                            ",merchant_pub"
+                            ",timestamp"
+                            ",contract_terms)"
+                            " VALUES "
+                            "($1, $2, $3, $4)",
+                            4),
     GNUNET_PQ_make_prepare ("mark_proposal_paid",
                             "UPDATE merchant_contract_terms SET"
-                            " paid=TRUE WHERE h_contract_terms=$1"
+                            " paid=TRUE, last_session_id=$3"
+                            " WHERE h_contract_terms=$1"
                             " AND merchant_pub=$2",
                             2),
     GNUNET_PQ_make_prepare ("insert_wire_fee",
@@ -352,6 +354,14 @@ postgres_initialize (void *cls)
                             " WHERE h_contract_terms=$1"
                             "   AND merchant_pub=$2",
                             2),
+    GNUNET_PQ_make_prepare ("find_paid_contract_terms_from_hash",
+                            "SELECT"
+                            " contract_terms"
+                            " FROM merchant_contract_terms"
+                            " WHERE h_contract_terms=$1"
+                            "   AND merchant_pub=$2"
+                            "   AND paid=TRUE",
+                            2),
     GNUNET_PQ_make_prepare ("end_transaction",
                             "COMMIT",
                             0),
@@ -377,11 +387,20 @@ postgres_initialize (void *cls)
     GNUNET_PQ_make_prepare ("find_contract_terms",
                             "SELECT"
                             " contract_terms"
+                            ",last_session_id"
                             " FROM merchant_contract_terms"
                             " WHERE"
                             " order_id=$1"
                             " AND merchant_pub=$2",
                             2),
+    GNUNET_PQ_make_prepare ("find_order",
+                            "SELECT"
+                            " contract_terms"
+                            " FROM merchant_orders"
+                            " WHERE"
+                            " order_id=$1"
+                            " AND merchant_pub=$2",
+                            2),
     GNUNET_PQ_make_prepare ("find_contract_terms_by_date",
                             "SELECT"
                             " contract_terms"
@@ -438,22 +457,10 @@ postgres_initialize (void *cls)
                             " ORDER BY row_id DESC, timestamp DESC"
                             " LIMIT $4",
                             4),
-    GNUNET_PQ_make_prepare ("find_transaction",
-                            "SELECT"
-                            " exchange_uri"
-                            ",h_wire"
-                            ",timestamp"
-                            ",refund_deadline"
-                            ",total_amount_val"
-                            ",total_amount_frac"
-                            ",total_amount_curr"
-                            " FROM merchant_transactions"
-                            " WHERE h_contract_terms=$1"
-                            " AND merchant_pub=$2",
-                            2),
     GNUNET_PQ_make_prepare ("find_deposits",
                             "SELECT"
                             " coin_pub"
+                           ",exchange_url"
                             ",amount_with_fee_val"
                             ",amount_with_fee_frac"
                             ",amount_with_fee_curr"
@@ -463,6 +470,9 @@ postgres_initialize (void *cls)
                             ",refund_fee_val"
                             ",refund_fee_frac"
                             ",refund_fee_curr"
+                            ",wire_fee_val"
+                            ",wire_fee_frac"
+                            ",wire_fee_curr"
                             ",exchange_proof"
                             " FROM merchant_deposits"
                             " WHERE h_contract_terms=$1"
@@ -479,6 +489,10 @@ postgres_initialize (void *cls)
                             ",refund_fee_val"
                             ",refund_fee_frac"
                             ",refund_fee_curr"
+                            ",wire_fee_val"
+                            ",wire_fee_frac"
+                            ",wire_fee_curr"
+                           ",exchange_url"
                             ",exchange_proof"
                             " FROM merchant_deposits"
                             " WHERE h_contract_terms=$1"
@@ -508,6 +522,10 @@ postgres_initialize (void *cls)
                             ",merchant_deposits.refund_fee_val"
                             ",merchant_deposits.refund_fee_frac"
                             ",merchant_deposits.refund_fee_curr"
+                            ",merchant_deposits.wire_fee_val"
+                            ",merchant_deposits.wire_fee_frac"
+                            ",merchant_deposits.wire_fee_curr"
+                            ",merchant_deposits.exchange_url"
                             ",merchant_deposits.exchange_proof"
                             " FROM merchant_transfers"
                             "   JOIN merchant_deposits"
@@ -521,7 +539,7 @@ postgres_initialize (void *cls)
                             " proof"
                             " FROM merchant_proofs"
                             " WHERE wtid=$1"
-                            "  AND exchange_uri=$2",
+                            "  AND exchange_url=$2",
                             2),
     GNUNET_PQ_make_prepare ("lookup_tip_reserve_balance",
                             "SELECT"
@@ -532,6 +550,16 @@ postgres_initialize (void *cls)
                             " FROM merchant_tip_reserves"
                             " WHERE reserve_priv=$1",
                             1),
+    GNUNET_PQ_make_prepare ("find_tip_authorizations",
+                            "SELECT"
+                            " amount_val"
+                            ",amount_frac"
+                            ",amount_curr"
+                            ",justification"
+                            ",tip_id"
+                            " FROM merchant_tips"
+                            " WHERE reserve_priv=$1",
+                            1),
     GNUNET_PQ_make_prepare ("update_tip_reserve_balance",
                             "UPDATE merchant_tip_reserves SET"
                             " expiration=$2"
@@ -554,7 +582,7 @@ postgres_initialize (void *cls)
                             "INSERT INTO merchant_tips"
                             "(reserve_priv"
                             ",tip_id"
-                           ",exchange_uri"
+                           ",exchange_url"
                             ",justification"
                             ",timestamp"
                             ",amount_val"
@@ -586,7 +614,7 @@ postgres_initialize (void *cls)
                             2),
     GNUNET_PQ_make_prepare ("find_tip_by_id",
                             "SELECT"
-                            " exchange_uri"
+                            " exchange_url"
                             ",timestamp"
                             ",amount_val"
                             ",amount_frac"
@@ -622,6 +650,11 @@ postgres_initialize (void *cls)
                             " VALUES "
                             "($1, $2, $3, $4, $5, $6)",
                             6),
+    GNUNET_PQ_make_prepare ("lookup_tip_credit_uuid",
+                            "SELECT 1 "
+                            "FROM merchant_tip_reserve_credits "
+                            "WHERE credit_uuid=$1 AND reserve_priv=$2",
+                            2),
     GNUNET_PQ_PREPARED_STATEMENT_END
   };
 
@@ -751,9 +784,9 @@ postgres_commit (void *cls)
  */
 static enum GNUNET_DB_QueryStatus
 postgres_find_contract_terms_from_hash (void *cls,
-                                       json_t **contract_terms,
-                                       const struct GNUNET_HashCode 
*h_contract_terms,
-                                       const struct TALER_MerchantPublicKeyP 
*merchant_pub)
+                                        json_t **contract_terms,
+                                        const struct GNUNET_HashCode 
*h_contract_terms,
+                                        const struct TALER_MerchantPublicKeyP 
*merchant_pub)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
@@ -776,17 +809,54 @@ postgres_find_contract_terms_from_hash (void *cls,
 
 
 /**
+ * Retrieve proposal data given its proposal data's hashcode
+ *
+ * @param cls closure
+ * @param contract_terms where to store the retrieved proposal data
+ * @param h_contract_terms proposal data's hashcode that will be used to
+ * perform the lookup
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_find_paid_contract_terms_from_hash (void *cls,
+                                             json_t **contract_terms,
+                                             const struct GNUNET_HashCode 
*h_contract_terms,
+                                             const struct 
TALER_MerchantPublicKeyP *merchant_pub)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
+    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    TALER_PQ_result_spec_json ("contract_terms",
+                               contract_terms),
+    GNUNET_PQ_result_spec_end
+  };
+
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                  
"find_paid_contract_terms_from_hash",
+                                                  params,
+                                                  rs);
+}
+
+
+/**
  * Retrieve proposal data given its order id.  Ignores if the
  * proposal has been paid or not.
  *
  * @param cls closure
  * @param[out] contract_terms where to store the retrieved contract terms
+ * @param[out] last_session_id where to store the result
  * @param order id order id used to perform the lookup
  * @return transaction status
  */
 static enum GNUNET_DB_QueryStatus
 postgres_find_contract_terms (void *cls,
                               json_t **contract_terms,
+                              char **last_session_id,
                               const char *order_id,
                               const struct TALER_MerchantPublicKeyP 
*merchant_pub)
 {
@@ -800,6 +870,8 @@ postgres_find_contract_terms (void *cls,
   struct GNUNET_PQ_ResultSpec rs[] = {
     TALER_PQ_result_spec_json ("contract_terms",
                                contract_terms),
+    GNUNET_PQ_result_spec_string ("last_session_id",
+                                  last_session_id),
     GNUNET_PQ_result_spec_end
   };
 
@@ -817,6 +889,47 @@ postgres_find_contract_terms (void *cls,
 
 
 /**
+ * Retrieve order given its order id and the instance's merchant public key.
+ *
+ * @param cls closure
+ * @param[out] contract_terms where to store the retrieved contract terms
+ * @param order id order id used to perform the lookup
+ * @param merchant_pub merchant public key that identifies the instance
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_find_order (void *cls,
+                     json_t **contract_terms,
+                     const char *order_id,
+                     const struct TALER_MerchantPublicKeyP *merchant_pub)
+{
+  struct PostgresClosure *pg = cls;
+
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (order_id),
+    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    TALER_PQ_result_spec_json ("contract_terms",
+                               contract_terms),
+    GNUNET_PQ_result_spec_end
+  };
+
+  *contract_terms = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Finding contract term, order_id: '%s', merchant_pub: '%s'.\n",
+              order_id,
+              TALER_B2S (merchant_pub));
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                  "find_order",
+                                                  params,
+                                                  rs);
+}
+
+
+/**
  * Insert proposal data and its hashcode into db
  *
  * @param cls closure
@@ -864,6 +977,43 @@ postgres_insert_contract_terms (void *cls,
 
 
 /**
+ * Insert order into the DB.
+ *
+ * @param cls closure
+ * @param order_id identificator of the proposal being stored
+ * @param merchant_pub merchant's public key
+ * @param timestamp timestamp of this proposal data
+ * @param contract_terms proposal data to store
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_insert_order (void *cls,
+                       const char *order_id,
+                       const struct TALER_MerchantPublicKeyP *merchant_pub,
+                       struct GNUNET_TIME_Absolute timestamp,
+                       const json_t *contract_terms)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (order_id),
+    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+    GNUNET_PQ_query_param_absolute_time (&timestamp),
+    TALER_PQ_query_param_json (contract_terms),
+    GNUNET_PQ_query_param_end
+  };
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "inserting order: order_id: %s, merchant_pub: %s.\n",
+              order_id,
+              TALER_B2S (merchant_pub));
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                            "insert_order",
+                                            params);
+}
+
+
+/**
  * Mark contract terms as payed.  Needed by /history as only payed
  * contracts must be shown.
  *
@@ -876,17 +1026,21 @@ postgres_insert_contract_terms (void *cls,
  * @param cls closure
  * @param h_contract_terms hash of the contract that is now paid
  * @param merchant_pub merchant's public key
+ * @param last_session_id session id used for the payment, NULL
+ *        if payment was not session-bound
  * @return transaction status
  */
 enum GNUNET_DB_QueryStatus
 postgres_mark_proposal_paid (void *cls,
                              const struct GNUNET_HashCode *h_contract_terms,
-                             const struct TALER_MerchantPublicKeyP 
*merchant_pub)
+                             const struct TALER_MerchantPublicKeyP 
*merchant_pub,
+                             const char *last_session_id)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+    GNUNET_PQ_query_param_string ((last_session_id == NULL) ? "" : 
last_session_id),
     GNUNET_PQ_query_param_end
   };
 
@@ -901,53 +1055,6 @@ postgres_mark_proposal_paid (void *cls,
 
 
 /**
- * Insert transaction data into the database.
- *
- * @param cls closure
- * @param h_contract_terms hashcode of the proposal data associated with the
- * transaction being stored
- * @param merchant_pub merchant's public key
- * @param exchange_uri URI of the exchange
- * @param h_wire hash of our wire details
- * @param timestamp time of the confirmation
- * @param refund refund deadline
- * @param total_amount total amount we receive for the contract after fees
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_store_transaction (void *cls,
-                            const struct GNUNET_HashCode *h_contract_terms,
-                           const struct TALER_MerchantPublicKeyP *merchant_pub,
-                            const char *exchange_uri,
-                            const struct GNUNET_HashCode *h_wire,
-                            struct GNUNET_TIME_Absolute timestamp,
-                            struct GNUNET_TIME_Absolute refund,
-                            const struct TALER_Amount *total_amount)
-{
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
-    GNUNET_PQ_query_param_string (exchange_uri),
-    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
-    GNUNET_PQ_query_param_auto_from_type (h_wire),
-    GNUNET_PQ_query_param_absolute_time (&timestamp),
-    GNUNET_PQ_query_param_absolute_time (&refund),
-    TALER_PQ_query_param_amount (total_amount),
-    GNUNET_PQ_query_param_end
-  };
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Storing transaction with h_contract_terms '%s', merchant_pub 
'%s'.\n",
-              GNUNET_h2s (h_contract_terms),
-              TALER_B2S (merchant_pub));
-  check_connection (pg);
-  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                            "insert_transaction",
-                                            params);
-}
-
-
-/**
  * Insert payment confirmation from the exchange into the database.
  *
  * @param cls closure
@@ -957,6 +1064,7 @@ postgres_store_transaction (void *cls,
  * @param amount_with_fee amount the exchange will deposit for this coin
  * @param deposit_fee fee the exchange will charge for this coin
  * @param refund_fee fee the exchange will charge for refunding this coin
+ * @param wire_fee wire fee changed by the exchange
  * @param signkey_pub public key used by the exchange for @a exchange_proof
  * @param exchange_proof proof from exchange that coin was accepted
  * @return transaction status
@@ -966,9 +1074,11 @@ postgres_store_deposit (void *cls,
                         const struct GNUNET_HashCode *h_contract_terms,
                         const struct TALER_MerchantPublicKeyP *merchant_pub,
                         const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                       const char *exchange_url,
                         const struct TALER_Amount *amount_with_fee,
                         const struct TALER_Amount *deposit_fee,
                         const struct TALER_Amount *refund_fee,
+                       const struct TALER_Amount *wire_fee,
                         const struct TALER_ExchangePublicKeyP *signkey_pub,
                         const json_t *exchange_proof)
 {
@@ -977,9 +1087,11 @@ postgres_store_deposit (void *cls,
     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
     GNUNET_PQ_query_param_auto_from_type (coin_pub),
+    GNUNET_PQ_query_param_string (exchange_url),
     TALER_PQ_query_param_amount (amount_with_fee),
     TALER_PQ_query_param_amount (deposit_fee),
     TALER_PQ_query_param_amount (refund_fee),
+    TALER_PQ_query_param_amount (wire_fee),
     GNUNET_PQ_query_param_auto_from_type (signkey_pub),
     TALER_PQ_query_param_json (exchange_proof),
     GNUNET_PQ_query_param_end
@@ -989,7 +1101,7 @@ postgres_store_deposit (void *cls,
               "Storing payment for h_contract_terms `%s', coin_pub: `%s', 
amount_with_fee: %s\n",
               GNUNET_h2s (h_contract_terms),
               TALER_B2S (coin_pub),
-              TALER_amount_to_string (amount_with_fee));
+              TALER_amount2s (amount_with_fee));
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Merchant pub is `%s'\n",
               TALER_B2S (merchant_pub));
@@ -1036,7 +1148,7 @@ postgres_store_coin_to_transfer (void *cls,
  * Insert wire transfer confirmation from the exchange into the database.
  *
  * @param cls closure
- * @param exchange_uri URI of the exchange
+ * @param exchange_url URL of the exchange
  * @param wtid identifier of the wire transfer
  * @param execution_time when was @a wtid executed
  * @param signkey_pub public key used by the exchange for @a exchange_proof
@@ -1045,7 +1157,7 @@ postgres_store_coin_to_transfer (void *cls,
  */
 static enum GNUNET_DB_QueryStatus
 postgres_store_transfer_to_proof (void *cls,
-                                  const char *exchange_uri,
+                                  const char *exchange_url,
                                   const struct 
TALER_WireTransferIdentifierRawP *wtid,
                                   struct GNUNET_TIME_Absolute execution_time,
                                   const struct TALER_ExchangePublicKeyP 
*signkey_pub,
@@ -1053,7 +1165,7 @@ postgres_store_transfer_to_proof (void *cls,
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (exchange_uri),
+    GNUNET_PQ_query_param_string (exchange_url),
     GNUNET_PQ_query_param_auto_from_type (wtid),
     GNUNET_PQ_query_param_absolute_time (&execution_time),
     GNUNET_PQ_query_param_auto_from_type (signkey_pub),
@@ -1106,10 +1218,11 @@ postgres_find_contract_terms_history (void *cls,
                                                 rs);
   if (qs <= 0)
     return qs;
-  cb (cb_cls,
-      order_id,
-      0,
-      contract_terms);
+  if (NULL != cb)
+    cb (cb_cls,
+        order_id,
+        0,
+        contract_terms);
   GNUNET_PQ_cleanup_result (rs);
   return qs;
 }
@@ -1255,6 +1368,128 @@ postgres_find_contract_terms_by_date_and_range (void 
*cls,
 
 
 /**
+ * Closure for #find_tip_authorizations_cb().
+ */
+struct GetAuthorizedTipAmountContext
+{
+  /**
+   * Total authorized amount.
+   */
+  struct TALER_Amount authorized_amount;
+
+  /**
+   * Transaction status code to set.
+   */
+  enum GNUNET_DB_QueryStatus qs;
+
+};
+
+
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls of type `struct GetAuthorizedTipAmountContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+find_tip_authorizations_cb (void *cls,
+                            PGresult *result,
+                            unsigned int num_results)
+{
+  struct GetAuthorizedTipAmountContext *ctx = cls;
+  unsigned int i;
+
+  for (i = 0; i < num_results; i++)
+  {
+    struct TALER_Amount amount;
+    char *just;
+    struct GNUNET_HashCode h;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_string ("justification", &just),
+      GNUNET_PQ_result_spec_auto_from_type ("tip_id", &h),
+      TALER_PQ_result_spec_amount ("amount",
+                                    &amount),
+      GNUNET_PQ_result_spec_end
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
+    {
+      GNUNET_break (0);
+      ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      return;
+    }
+
+    if (0 == i)
+    {
+      memcpy (&ctx->authorized_amount, &amount, sizeof (struct TALER_Amount));
+    }
+    else if (GNUNET_OK !=
+             TALER_amount_add (&ctx->authorized_amount,
+                               &ctx->authorized_amount, &amount))
+    {
+      GNUNET_break (0);
+      ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      return;
+    }
+  }
+
+  if (0 == i)
+  {
+    ctx->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+  }
+  else
+  {
+    /* one aggregated result */
+    ctx->qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+  }
+}
+
+
+/**
+ * Get the total amount of authorized tips for a tipping reserve.
+ *
+ * @param cls closure, typically a connection to the db
+ * @param reserve_priv which reserve to check
+ * @param[out] authorzed_amount amount we've authorized so far for tips
+ * @return transaction status, usually
+ *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
+ *      #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the reserve_priv
+ *      does not identify a known tipping reserve
+ */
+enum GNUNET_DB_QueryStatus
+postgres_get_authorized_tip_amount (void *cls,
+                                    const struct TALER_ReservePrivateKeyP 
*reserve_priv,
+                                    struct TALER_Amount *authorized_amount)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (reserve_priv),
+    GNUNET_PQ_query_param_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+  struct GetAuthorizedTipAmountContext ctx = { 0 };
+
+  check_connection (pg);
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                            "find_tip_authorizations",
+                                            params,
+                                            &find_tip_authorizations_cb,
+                                            &ctx);
+  if (0 >= qs)
+    return qs;
+  memcpy (authorized_amount, &ctx.authorized_amount, sizeof (struct 
TALER_Amount));
+  return ctx.qs;
+}
+
+
+/**
  * Return proposals whose timestamp are older than `date`.
  * The rows are sorted having the youngest first.
  *
@@ -1301,75 +1536,6 @@ postgres_find_contract_terms_by_date (void *cls,
 
 
 /**
- * Find information about a transaction.
- *
- * @param cls our plugin handle
- * @param h_contract_terms value used to perform the lookup
- * @param merchant_pub merchant's public key
- * @param cb function to call with transaction data
- * @param cb_cls closure for @a cb
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_find_transaction (void *cls,
-                           const struct GNUNET_HashCode *h_contract_terms,
-                          const struct TALER_MerchantPublicKeyP *merchant_pub,
-                           TALER_MERCHANTDB_TransactionCallback cb,
-                           void *cb_cls)
-{
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
-    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
-    GNUNET_PQ_query_param_end
-  };
-  char *exchange_uri;
-  struct GNUNET_HashCode h_wire;
-  struct GNUNET_TIME_Absolute timestamp;
-  struct GNUNET_TIME_Absolute refund_deadline;
-  struct TALER_Amount total_amount;
-  struct GNUNET_PQ_ResultSpec rs[] = {
-    GNUNET_PQ_result_spec_string ("exchange_uri",
-                                 &exchange_uri),
-    GNUNET_PQ_result_spec_auto_from_type ("h_wire",
-                                         &h_wire),
-    GNUNET_PQ_result_spec_absolute_time ("timestamp",
-                                        &timestamp),
-    GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
-                                        &refund_deadline),
-    TALER_PQ_result_spec_amount ("total_amount",
-                                &total_amount),
-    GNUNET_PQ_result_spec_end
-  };
-  enum GNUNET_DB_QueryStatus qs;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Finding transaction for h_contract_terms '%s', merchant_pub: 
'%s'.\n",
-              GNUNET_h2s (h_contract_terms),
-              TALER_B2S (merchant_pub));
-
-  check_connection (pg);
-  qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                "find_transaction",
-                                                params,
-                                                rs);
-  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
-  {
-    cb (cb_cls,
-       merchant_pub,
-        exchange_uri,
-        h_contract_terms,
-        &h_wire,
-        timestamp,
-        refund_deadline,
-        &total_amount);
-    GNUNET_PQ_cleanup_result (rs);
-  }
-  return qs;
-}
-
-
-/**
  * Closure for #find_payments_cb().
  */
 struct FindPaymentsContext
@@ -1417,16 +1583,22 @@ find_payments_cb (void *cls,
     struct TALER_Amount amount_with_fee;
     struct TALER_Amount deposit_fee;
     struct TALER_Amount refund_fee;
+    struct TALER_Amount wire_fee;
     json_t *exchange_proof;
+    char *exchange_url;
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
                                             &coin_pub),
+      GNUNET_PQ_result_spec_string ("exchange_url",
+                                   &exchange_url),
       TALER_PQ_result_spec_amount ("amount_with_fee",
                                    &amount_with_fee),
       TALER_PQ_result_spec_amount ("deposit_fee",
                                    &deposit_fee),
       TALER_PQ_result_spec_amount ("refund_fee",
                                    &refund_fee),
+      TALER_PQ_result_spec_amount ("wire_fee",
+                                   &wire_fee),
       TALER_PQ_result_spec_json ("exchange_proof",
                                  &exchange_proof),
       GNUNET_PQ_result_spec_end
@@ -1445,9 +1617,11 @@ find_payments_cb (void *cls,
     fpc->cb (fpc->cb_cls,
             fpc->h_contract_terms,
             &coin_pub,
+            exchange_url,
             &amount_with_fee,
             &deposit_fee,
             &refund_fee,
+            &wire_fee,
             exchange_proof);
     GNUNET_PQ_cleanup_result (rs);
   }
@@ -1552,6 +1726,8 @@ find_payments_by_coin_cb (void *cls,
     struct TALER_Amount amount_with_fee;
     struct TALER_Amount deposit_fee;
     struct TALER_Amount refund_fee;
+    struct TALER_Amount wire_fee;
+    char *exchange_url;
     json_t *exchange_proof;
     struct GNUNET_PQ_ResultSpec rs[] = {
       TALER_PQ_result_spec_amount ("amount_with_fee",
@@ -1560,6 +1736,10 @@ find_payments_by_coin_cb (void *cls,
                                    &deposit_fee),
       TALER_PQ_result_spec_amount ("refund_fee",
                                    &refund_fee),
+      TALER_PQ_result_spec_amount ("wire_fee",
+                                   &wire_fee),
+      GNUNET_PQ_result_spec_string ("exchange_url",
+                                   &exchange_url),
       TALER_PQ_result_spec_json ("exchange_proof",
                                  &exchange_proof),
       GNUNET_PQ_result_spec_end
@@ -1578,9 +1758,11 @@ find_payments_by_coin_cb (void *cls,
     fpc->cb (fpc->cb_cls,
             fpc->h_contract_terms,
             fpc->coin_pub,
+            exchange_url,
             &amount_with_fee,
             &deposit_fee,
             &refund_fee,
+            &wire_fee,
             exchange_proof);
     GNUNET_PQ_cleanup_result (rs);
   }
@@ -1803,6 +1985,8 @@ find_deposits_cb (void *cls,
     struct TALER_Amount amount_with_fee;
     struct TALER_Amount deposit_fee;
     struct TALER_Amount refund_fee;
+    struct TALER_Amount wire_fee;
+    char *exchange_url;
     json_t *exchange_proof;
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
@@ -1815,6 +1999,10 @@ find_deposits_cb (void *cls,
                                    &deposit_fee),
       TALER_PQ_result_spec_amount ("refund_fee",
                                    &refund_fee),
+      TALER_PQ_result_spec_amount ("wire_fee",
+                                   &wire_fee),
+      GNUNET_PQ_result_spec_string ("exchange_url",
+                                   &exchange_url),
       TALER_PQ_result_spec_json ("exchange_proof",
                                  &exchange_proof),
       GNUNET_PQ_result_spec_end
@@ -1833,9 +2021,11 @@ find_deposits_cb (void *cls,
     fdc->cb (fdc->cb_cls,
             &h_contract_terms,
             &coin_pub,
+            exchange_url,
             &amount_with_fee,
             &deposit_fee,
             &refund_fee,
+            &wire_fee,
             exchange_proof);
     GNUNET_PQ_cleanup_result (rs);
   }
@@ -2566,7 +2756,7 @@ postgres_increase_refund_for_contract (void *cls,
  * Lookup proof information about a wire transfer.
  *
  * @param cls closure
- * @param exchange_uri from which exchange are we looking for proof
+ * @param exchange_url from which exchange are we looking for proof
  * @param wtid wire transfer identifier for the search
  * @param cb function to call with proof data
  * @param cb_cls closure for @a cb
@@ -2574,7 +2764,7 @@ postgres_increase_refund_for_contract (void *cls,
  */
 static enum GNUNET_DB_QueryStatus
 postgres_find_proof_by_wtid (void *cls,
-                             const char *exchange_uri,
+                             const char *exchange_url,
                              const struct TALER_WireTransferIdentifierRawP 
*wtid,
                              TALER_MERCHANTDB_ProofCallback cb,
                              void *cb_cls)
@@ -2582,7 +2772,7 @@ postgres_find_proof_by_wtid (void *cls,
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (wtid),
-    GNUNET_PQ_query_param_string (exchange_uri),
+    GNUNET_PQ_query_param_string (exchange_url),
     GNUNET_PQ_query_param_end
   };
   json_t *proof;
@@ -2653,6 +2843,35 @@ postgres_enable_tip_reserve (void *cls,
 
   /* ensure that credit_uuid is new/unique */
   {
+    struct GNUNET_PQ_QueryParam params[] = {
+      GNUNET_PQ_query_param_auto_from_type (credit_uuid),
+      GNUNET_PQ_query_param_auto_from_type (reserve_priv),
+      GNUNET_PQ_query_param_end
+    };
+
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_end
+    };
+    qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   "lookup_tip_credit_uuid",
+                                                   params,
+                                                   rs);
+    if (0 > qs)
+    {
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      postgres_rollback (pg);
+      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+        goto RETRY;
+      return qs;
+    }
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
+    {
+      /* UUID already exists, we are done! */
+      return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+    }
+  }
+
+  {
     struct GNUNET_TIME_Absolute now;
     struct GNUNET_PQ_QueryParam params[] = {
       GNUNET_PQ_query_param_auto_from_type (reserve_priv),
@@ -2675,12 +2894,6 @@ postgres_enable_tip_reserve (void *cls,
         goto RETRY;
       return qs;
     }
-    /* UUID already exists, we are done! */
-    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-    {
-      postgres_rollback (pg);
-      return qs;
-    }
   }
 
   /* Obtain existing reserve balance */
@@ -2781,7 +2994,7 @@ postgres_enable_tip_reserve (void *cls,
  * @param justification why was the tip approved
  * @param amount how high is the tip (with fees)
  * @param reserve_priv which reserve is debited
- * @param exchange_uri which exchange manages the tip
+ * @param exchange_url which exchange manages the tip
  * @param[out] expiration set to when the tip expires
  * @param[out] tip_id set to the unique ID for the tip
  * @return taler error code
@@ -2797,7 +3010,7 @@ postgres_authorize_tip (void *cls,
                         const char *justification,
                         const struct TALER_Amount *amount,
                         const struct TALER_ReservePrivateKeyP *reserve_priv,
-                       const char *exchange_uri,
+                       const char *exchange_url,
                         struct GNUNET_TIME_Absolute *expiration,
                         struct GNUNET_HashCode *tip_id)
 {
@@ -2841,7 +3054,7 @@ postgres_authorize_tip (void *cls,
     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
       goto RETRY;
     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-      return TALER_EC_TIP_AUTHORIZE_RESERVE_NOT_ENABLED;
+      return TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
     return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
   }
   if (0 == GNUNET_TIME_absolute_get_remaining (old_expiration).rel_value_us)
@@ -2888,7 +3101,7 @@ postgres_authorize_tip (void *cls,
     struct GNUNET_PQ_QueryParam params[] = {
       GNUNET_PQ_query_param_auto_from_type (reserve_priv),
       GNUNET_PQ_query_param_auto_from_type (tip_id),
-      GNUNET_PQ_query_param_string (exchange_uri),
+      GNUNET_PQ_query_param_string (exchange_url),
       GNUNET_PQ_query_param_string (justification),
       GNUNET_PQ_query_param_absolute_time (&now),
       TALER_PQ_query_param_amount (amount), /* overall amount */
@@ -2922,7 +3135,7 @@ postgres_authorize_tip (void *cls,
  *
  * @param cls closure, typically a connection to the d
  * @param tip_id the unique ID for the tip
- * @param[out] exchange_uri set to the URI of the exchange (unless NULL)
+ * @param[out] exchange_url set to the URL of the exchange (unless NULL)
  * @param[out] amount set to the authorized amount (unless NULL)
  * @param[out] timestamp set to the timestamp of the tip authorization (unless 
NULL)
  * @return transaction status, usually
@@ -2932,11 +3145,11 @@ postgres_authorize_tip (void *cls,
 static enum GNUNET_DB_QueryStatus
 postgres_lookup_tip_by_id (void *cls,
                            const struct GNUNET_HashCode *tip_id,
-                           char **exchange_uri,
+                           char **exchange_url,
                            struct TALER_Amount *amount,
                            struct GNUNET_TIME_Absolute *timestamp)
 {
-  char *res_exchange_uri;
+  char *res_exchange_url;
   struct TALER_Amount res_amount;
   struct GNUNET_TIME_Absolute res_timestamp;
 
@@ -2946,8 +3159,8 @@ postgres_lookup_tip_by_id (void *cls,
     GNUNET_PQ_query_param_end
   };
   struct GNUNET_PQ_ResultSpec rs[] = {
-    GNUNET_PQ_result_spec_string ("exchange_uri",
-                                 &res_exchange_uri),
+    GNUNET_PQ_result_spec_string ("exchange_url",
+                                 &res_exchange_url),
     GNUNET_PQ_result_spec_absolute_time ("timestamp",
                                          &res_timestamp),
     TALER_PQ_result_spec_amount ("amount",
@@ -2962,12 +3175,12 @@ postgres_lookup_tip_by_id (void *cls,
                                                 rs);
   if (0 >= qs)
   {
-    if (NULL != exchange_uri)
-      *exchange_uri = NULL;
+    if (NULL != exchange_url)
+      *exchange_url = NULL;
     return qs;
   }
-  if (NULL != exchange_uri)
-    *exchange_uri = strdup (res_exchange_uri);
+  if (NULL != exchange_url)
+    *exchange_url = strdup (res_exchange_url);
   if (NULL != amount)
     *amount = res_amount;
   if (NULL != timestamp)
@@ -3211,23 +3424,25 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
   plugin->cls = pg;
   plugin->drop_tables = &postgres_drop_tables;
   plugin->initialize = &postgres_initialize;
-  plugin->store_transaction = &postgres_store_transaction;
   plugin->store_deposit = &postgres_store_deposit;
   plugin->store_coin_to_transfer = &postgres_store_coin_to_transfer;
   plugin->store_transfer_to_proof = &postgres_store_transfer_to_proof;
   plugin->store_wire_fee_by_exchange = &postgres_store_wire_fee_by_exchange;
-  plugin->find_transaction = &postgres_find_transaction;
   plugin->find_payments_by_hash_and_coin = 
&postgres_find_payments_by_hash_and_coin;
   plugin->find_payments = &postgres_find_payments;
   plugin->find_transfers_by_hash = &postgres_find_transfers_by_hash;
   plugin->find_deposits_by_wtid = &postgres_find_deposits_by_wtid;
   plugin->find_proof_by_wtid = &postgres_find_proof_by_wtid;
   plugin->insert_contract_terms = &postgres_insert_contract_terms;
+  plugin->insert_order = &postgres_insert_order;
+  plugin->find_order = &postgres_find_order;
   plugin->find_contract_terms = &postgres_find_contract_terms;
   plugin->find_contract_terms_history = &postgres_find_contract_terms_history;
   plugin->find_contract_terms_by_date = &postgres_find_contract_terms_by_date;
+  plugin->get_authorized_tip_amount = &postgres_get_authorized_tip_amount;
   plugin->find_contract_terms_by_date_and_range = 
&postgres_find_contract_terms_by_date_and_range;
   plugin->find_contract_terms_from_hash = 
&postgres_find_contract_terms_from_hash;
+  plugin->find_paid_contract_terms_from_hash = 
&postgres_find_paid_contract_terms_from_hash;
   plugin->get_refunds_from_contract_terms_hash = 
&postgres_get_refunds_from_contract_terms_hash;
   plugin->lookup_wire_fee = &postgres_lookup_wire_fee;
   plugin->increase_refund_for_contract = 
&postgres_increase_refund_for_contract;
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
index 0bbea3f..33a05dd 100644
--- a/src/backenddb/test_merchantdb.c
+++ b/src/backenddb/test_merchantdb.c
@@ -14,7 +14,7 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file merchant/test_merchantdb_postgres.c
+ * @file merchant/test_merchantdb.c
  * @brief testcase for merchant's postgres db plugin
  * @author Marcello Stanisci
  * @author Christian Grothoff
@@ -44,11 +44,11 @@
 #define CURRENCY "EUR"
 
 /**
- * URI we use for the exchange in the database.
+ * URL we use for the exchange in the database.
  * Note that an exchange does not actually have
  * to run at this address.
  */
-#define EXCHANGE_URI "http://localhost:8888/";
+#define EXCHANGE_URL "http://localhost:8888/";
 
 /**
  * Global return value for the test.  Initially -1, set to 0 upon
@@ -113,6 +113,11 @@ static struct TALER_Amount amount_with_fee;
 static struct TALER_Amount deposit_fee;
 
 /**
+ * Wire fee of the exchange.
+ */
+static struct TALER_Amount wire_fee;
+
+/**
  * Refund fee for the coin.
  */
 static struct TALER_Amount refund_fee;
@@ -190,47 +195,6 @@ static json_t *contract_terms_future;
 
 
 /**
- * Function called with information about a transaction.
- *
- * @param cls closure
- * @param transaction_id of the contract
- * @param merchant_pub public key of the merchant
- * @param exchange_uri URI of the exchange
- * @param h_wire hash of our wire details
- * @param timestamp time of the confirmation
- * @param refund_deadline refund deadline
- * @param total_amount total amount we receive for the contract after fees
- */
-static void
-transaction_cb (void *cls,
-               const struct TALER_MerchantPublicKeyP *amerchant_pub,
-                const char *aexchange_uri,
-                const struct GNUNET_HashCode *ah_contract_terms,
-                const struct GNUNET_HashCode *ah_wire,
-                struct GNUNET_TIME_Absolute atimestamp,
-                struct GNUNET_TIME_Absolute arefund_deadline,
-                const struct TALER_Amount *atotal_amount)
-{
-#define CHECK(a) do { if (! (a)) { GNUNET_break (0); result = 3; } } while (0)
-  CHECK (0 == memcmp (amerchant_pub,
-                      &merchant_pub,
-                     sizeof (struct TALER_MerchantPublicKeyP)));
-  CHECK (0 == memcmp (ah_contract_terms,
-                      &h_contract_terms,
-                      sizeof (struct GNUNET_HashCode)));
-  CHECK (0 == strcmp (aexchange_uri,
-                      EXCHANGE_URI));
-  CHECK (0 == memcmp (ah_wire,
-                      &h_wire,
-                      sizeof (struct GNUNET_HashCode)));
-  CHECK (atimestamp.abs_value_us == timestamp.abs_value_us);
-  CHECK (arefund_deadline.abs_value_us == refund_deadline.abs_value_us);
-  CHECK (0 == TALER_amount_cmp (atotal_amount,
-                                &amount_with_fee));
-}
-
-
-/**
  * Function called with information about a refund.
  *
  * @param cls closure
@@ -272,12 +236,16 @@ pd_cb (void *cls,
 }
 
 
+#define CHECK(a) do { if (! (a)) { GNUNET_break (0); result = 3; } } while (0)
+
+
 /**
  * Function called with information about a coin that was deposited.
  *
  * @param cls closure
  * @param transaction_id of the contract
- * @param coin_pub public key of the coin
+ * @param acoin_pub public key of the coin
+ * @param aexchange_url exchange associated with @a acoin_pub in DB
  * @param aamount_with_fee amount the exchange will deposit for this coin
  * @param adeposit_fee fee the exchange will charge for this coin
  * @param adeposit_fee fee the exchange will charge for refunding this coin
@@ -287,9 +255,11 @@ static void
 deposit_cb (void *cls,
             const struct GNUNET_HashCode *ah_contract_terms,
             const struct TALER_CoinSpendPublicKeyP *acoin_pub,
+           const char *aexchange_url,
             const struct TALER_Amount *aamount_with_fee,
             const struct TALER_Amount *adeposit_fee,
             const struct TALER_Amount *arefund_fee,
+            const struct TALER_Amount *awire_fee,
             const json_t *aexchange_proof)
 {
   CHECK ((0 == memcmp (ah_contract_terms,
@@ -298,10 +268,14 @@ deposit_cb (void *cls,
   CHECK (0 == memcmp (acoin_pub,
                       &coin_pub,
                       sizeof (struct TALER_CoinSpendPublicKeyP)));
+  CHECK (0 == strcmp (aexchange_url,
+                      EXCHANGE_URL));
   CHECK (0 == TALER_amount_cmp (aamount_with_fee,
                                 &amount_with_fee));
   CHECK (0 == TALER_amount_cmp (adeposit_fee,
                                 &deposit_fee));
+  CHECK (0 == TALER_amount_cmp (awire_fee,
+                                &wire_fee));
   CHECK (1 == json_equal ((json_t *) aexchange_proof,
                           deposit_proof));
 }
@@ -529,10 +503,10 @@ test_tipping ()
   struct TALER_Amount total;
   struct TALER_Amount amount;
   struct TALER_Amount inc;
-  char *uri;
+  char *url;
 
   RND_BLK (&tip_reserve_priv);
-  if (TALER_EC_TIP_AUTHORIZE_RESERVE_NOT_ENABLED !=
+  if (TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS !=
       plugin->authorize_tip (plugin->cls,
                              "testing tips reserve unknown",
                              &amount,
@@ -641,20 +615,20 @@ test_tipping ()
   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
       plugin->lookup_tip_by_id (plugin->cls,
                                      &tip_id,
-                                     &uri,
+                                     &url,
                                       NULL, NULL))
   {
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
   if (0 != strcmp ("http://localhost:8081/";,
-                  uri))
+                  url))
   {
-    GNUNET_free (uri);
+    GNUNET_free (url);
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-  GNUNET_free (uri);
+  GNUNET_free (url);
   if (TALER_EC_NONE !=
       plugin->authorize_tip (plugin->cls,
                              "testing tips more",
@@ -812,6 +786,9 @@ run (void *cls)
                  TALER_string_to_amount (CURRENCY ":0.000010",
                                          &deposit_fee));
   GNUNET_assert (GNUNET_OK ==
+                 TALER_string_to_amount (CURRENCY ":0.000001",
+                                         &wire_fee));
+  GNUNET_assert (GNUNET_OK ==
                  TALER_string_to_amount (CURRENCY ":0.000010",
                                          &refund_fee));
   GNUNET_assert (GNUNET_OK ==
@@ -861,17 +838,48 @@ run (void *cls)
                                         timestamp,
                                         contract_terms));
 
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+          plugin->find_paid_contract_terms_from_hash (plugin->cls,
+                                                      &out,
+                                                      &h_contract_terms,
+                                                      &merchant_pub));
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->mark_proposal_paid (plugin->cls,
                                       &h_contract_terms,
-                                      &merchant_pub));
+                                      &merchant_pub,
+                                      "my-session-123"));
 
 
+  {
+    char *last_session_id;
+    FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+            plugin->find_contract_terms (plugin->cls,
+                                         &out,
+                                         &last_session_id,
+                                         order_id,
+                                         &merchant_pub));
+    FAILIF (0 != strcmp (last_session_id, "my-session-123"));
+    GNUNET_free (last_session_id);
+  }
+
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->find_contract_terms (plugin->cls,
-                                      &out,
-                                      order_id,
-                                      &merchant_pub));
+          plugin->mark_proposal_paid (plugin->cls,
+                                      &h_contract_terms,
+                                      &merchant_pub,
+                                      NULL));
+
+
+  {
+    char *last_session_id;
+    FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+            plugin->find_contract_terms (plugin->cls,
+                                         &out,
+                                         &last_session_id,
+                                         order_id,
+                                         &merchant_pub));
+    FAILIF (0 != strcmp (last_session_id, ""));
+    GNUNET_free (last_session_id);
+  }
 
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
          plugin->find_contract_terms_history (plugin->cls,
@@ -881,6 +889,11 @@ run (void *cls)
                                               NULL));
 
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+          plugin->find_paid_contract_terms_from_hash (plugin->cls,
+                                                      &out,
+                                                      &h_contract_terms,
+                                                      &merchant_pub));
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->find_contract_terms_from_hash (plugin->cls,
                                                 &out,
                                                 &h_contract_terms,
@@ -913,7 +926,8 @@ run (void *cls)
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->mark_proposal_paid (plugin->cls,
                                       &h_contract_terms_future,
-                                      &merchant_pub));
+                                      &merchant_pub,
+                                      "hello"));
   FAILIF (2 !=
           plugin->find_contract_terms_by_date_and_range (plugin->cls,
                                                         fake_now,
@@ -933,22 +947,15 @@ run (void *cls)
                                               NULL));
 
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->store_transaction (plugin->cls,
-                                     &h_contract_terms,
-                                    &merchant_pub,
-                                     EXCHANGE_URI,
-                                     &h_wire,
-                                     timestamp,
-                                     refund_deadline,
-                                     &amount_with_fee));
-  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->store_deposit (plugin->cls,
                                  &h_contract_terms,
                                 &merchant_pub,
                                  &coin_pub,
+                                EXCHANGE_URL,
                                  &amount_with_fee,
                                  &deposit_fee,
                                  &refund_fee,
+                                &wire_fee,
                                  &signkey_pub,
                                  deposit_proof));
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
@@ -958,17 +965,11 @@ run (void *cls)
                                           &wtid));
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->store_transfer_to_proof (plugin->cls,
-                                           EXCHANGE_URI,
+                                           EXCHANGE_URL,
                                            &wtid,
                                            GNUNET_TIME_UNIT_ZERO_ABS,
                                            &signkey_pub,
                                            transfer_proof));
-  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->find_transaction (plugin->cls,
-                                    &h_contract_terms,
-                                   &merchant_pub,
-                                    &transaction_cb,
-                                    NULL));
 
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->find_payments (plugin->cls,
@@ -988,7 +989,7 @@ run (void *cls)
                                          NULL));
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->find_proof_by_wtid (plugin->cls,
-                                      EXCHANGE_URI,
+                                      EXCHANGE_URL,
                                       &wtid,
                                       &proof_cb,
                                       NULL));
diff --git a/src/include/taler_merchant_service.h 
b/src/include/taler_merchant_service.h
index 0b739d6..2dd0b20 100644
--- a/src/include/taler_merchant_service.h
+++ b/src/include/taler_merchant_service.h
@@ -55,7 +55,7 @@ typedef void
  * Does a GET /refund.
  *
  * @param ctx execution context
- * @param backend_uri base URL of the merchant backend
+ * @param backend_url base URL of the merchant backend
  * @param order_id order id used to perform the lookup
  * @param cb callback which will work the response gotten from the backend
  * @param cb_cls closure to pass to the callback
@@ -63,7 +63,7 @@ typedef void
  */
 struct TALER_MERCHANT_RefundLookupOperation *
 TALER_MERCHANT_refund_lookup (struct GNUNET_CURL_Context *ctx,
-                              const char *backend_uri,
+                              const char *backend_url,
                               const char *order_id,
                               const char *instance,
                               TALER_MERCHANT_RefundLookupCallback cb,
@@ -103,7 +103,7 @@ typedef void
  * Increase the refund associated to a order
  *
  * @param ctx the CURL context used to connect to the backend
- * @param backend_uri backend's base URL, including final "/"
+ * @param backend_url backend's base URL, including final "/"
  * @param order_id id of the order whose refund is to be increased
  * @param refund amount to which increase the refund
  * @param reason human-readable reason justifying the refund
@@ -113,7 +113,7 @@ typedef void
  */
 struct TALER_MERCHANT_RefundIncreaseOperation *
 TALER_MERCHANT_refund_increase (struct GNUNET_CURL_Context *ctx,
-                                const char *backend_uri,
+                                const char *backend_url,
                                 const char *order_id,
                                 const struct TALER_Amount *refund,
                                 const char *reason,
@@ -147,25 +147,21 @@ struct TALER_MERCHANT_ProposalOperation;
  *                    0 if the backend's reply is bogus (fails to follow the 
protocol)
  * @param ec taler-specific error code
  * @param obj raw JSON reply, or error details if the request failed
- * @param contract_terms completed contract, NULL on error
- * @param sig merchant's signature over the contract, NULL on error
- * @param hash proposal data's hashcode, NULL on error
+ * @param order_id order id of the newly created order
  */
 typedef void
 (*TALER_MERCHANT_ProposalCallback) (void *cls,
                                     unsigned int http_status,
                                    enum TALER_ErrorCode ec,
                                     const json_t *obj,
-                                    const json_t *contract_terms,
-                                    const struct TALER_MerchantSignatureP *sig,
-                                    const struct GNUNET_HashCode *hash);
+                                    const char *order_id);
 
 
 /**
  * PUT an order to the backend and receives the related proposal.
  *
  * @param ctx execution context
- * @param backend_uri URI of the backend
+ * @param backend_url URL of the backend
  * @param order basic information about this purchase, to be extended by the
  * backend
  * @param proposal_cb the callback to call when a reply for this request is 
available
@@ -174,7 +170,7 @@ typedef void
  */
 struct TALER_MERCHANT_ProposalOperation *
 TALER_MERCHANT_order_put (struct GNUNET_CURL_Context *ctx,
-                          const char *backend_uri,
+                          const char *backend_url,
                           const json_t *order,
                           TALER_MERCHANT_ProposalCallback proposal_cb,
                           void *proposal_cb_cls);
@@ -207,7 +203,10 @@ struct TALER_MERCHANT_ProposalLookupOperation;
 typedef void
 (*TALER_MERCHANT_ProposalLookupOperationCallback) (void *cls,
                                                    unsigned int http_status,
-                                                   const json_t *body);
+                                                   const json_t *body,
+                                                   const json_t 
*contract_terms,
+                                                   const struct 
TALER_MerchantSignatureP *sig,
+                                                   const struct 
GNUNET_HashCode *hash);
 
 
 /**
@@ -215,17 +214,20 @@ typedef void
  * retrieve a proposal data by providing its transaction id.
  *
  * @param ctx execution context
- * @param backend_uri base URL of the merchant backend
- * @param transaction_id transaction id used to perform the lookup
+ * @param backend_url base URL of the merchant backend
+ * @param order_id order id used to perform the lookup
+ * @param nonce nonce to use, only used when requesting the proposal the first 
time,
+ *              can be NULL to omit the nonce (after the first request)
  * @param plo_cb callback which will work the response gotten from the backend
  * @param plo_cb_cls closure to pass to @a history_cb
  * @return handle for this operation, NULL upon errors
  */
 struct TALER_MERCHANT_ProposalLookupOperation *
 TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context *ctx,
-                                const char *backend_uri,
-                                const char *transaction_id,
+                                const char *backend_url,
+                                const char *order_id,
                                 const char *instance,
+                                const struct GNUNET_CRYPTO_EddsaPublicKey 
*nonce,
                                 TALER_MERCHANT_ProposalLookupOperationCallback 
plo_cb,
                                 void *plo_cb_cls);
 
@@ -249,7 +251,7 @@ TALER_MERCHANT_proposal_lookup_cancel (struct 
TALER_MERCHANT_ProposalLookupOpera
  * or backends (API for frontends).  The difference is that for the
  * frontend API, we need the private keys of the coins, while for
  * the backend API we need the public keys and signatures received
- * from the wallet.  Also, the frontend returns a redirect URI on
+ * from the wallet.  Also, the frontend returns a redirect URL on
  * success, while the backend just returns a success status code.
  */
 struct TALER_MERCHANT_Pay;
@@ -310,6 +312,16 @@ struct TALER_MERCHANT_PayCoin
    */
   struct TALER_Amount amount_without_fee;
 
+  /**
+   * Fee the exchange charges for refunds of this coin.
+   */
+  struct TALER_Amount refund_fee;
+
+  /**
+   * URL of the exchange that issued @e coin_priv.
+   */
+  const char *exchange_url;
+
 };
 
 
@@ -317,7 +329,7 @@ struct TALER_MERCHANT_PayCoin
  * Pay a merchant.  API for wallets that have the coin's private keys.
  *
  * @param ctx execution context
- * @param merchant_uri base URI of the merchant
+ * @param merchant_url base URL of the merchant
  * @param instance which merchant instance will receive this payment
  * @param h_wire hash of the merchant’s account details
  * @param h_contract hash of the contact of the merchant with the customer
@@ -329,7 +341,6 @@ struct TALER_MERCHANT_PayCoin
  * @param timestamp timestamp when the contract was finalized, must match 
approximately the current time of the merchant
  * @param refund_deadline date until which the merchant can issue a refund to 
the customer via the merchant (can be zero if refunds are not allowed)
  * @param pay_deadline maximum time limit to pay for this contract
- * @param exchange_uri URI of the exchange that the coins belong to
  * @param num_coins number of coins used to pay
  * @param coins array of coins we use to pay
  * @param coin_sig the signature made with purpose 
#TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s 
private key.
@@ -339,7 +350,7 @@ struct TALER_MERCHANT_PayCoin
  */
 struct TALER_MERCHANT_Pay *
 TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx,
-                          const char *merchant_uri,
+                          const char *merchant_url,
                           const char *instance,
                            const struct GNUNET_HashCode *h_contract,
                            const struct TALER_Amount *amount,
@@ -350,7 +361,6 @@ TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx,
                            struct GNUNET_TIME_Absolute refund_deadline,
                            struct GNUNET_TIME_Absolute pay_deadline,
                            const struct GNUNET_HashCode *h_wire,
-                          const char *exchange_uri,
                            const char *order_id,
                            unsigned int num_coins,
                            const struct TALER_MERCHANT_PayCoin *coins,
@@ -359,6 +369,99 @@ TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx,
 
 
 /**
+ * Entry in the array of refunded coins.
+ */
+struct TALER_MERCHANT_RefundEntry
+{
+  /**
+   * Merchant signature affirming the refund.
+   */
+  struct TALER_MerchantSignatureP merchant_sig;
+
+  /**
+   * Public key of the refunded coin.
+   */
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+
+  /**
+   * Refund transaction ID.
+   */
+  uint64_t rtransaction_id;
+};
+
+
+/**
+ * Callbacks of this type are used to serve the result of submitting a
+ * /pay request to a merchant.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, 200 or 300-level response codes
+ *                    can indicate success, depending on whether the 
interaction
+ *                    was with a merchant frontend or backend;
+ *                    0 if the merchant's reply is bogus (fails to follow the 
protocol)
+ * @param ec taler-specific error code
+ * @param merchant_pub public key of the merchant
+ * @param h_contract hash of the contract
+ * @param num_refunds size of the @a res array, 0 on errors
+ * @param res merchant signatures refunding coins, NULL on errors
+ * @param obj the received JSON reply, with error details if the request failed
+ */
+typedef void
+(*TALER_MERCHANT_PayRefundCallback) (void *cls,
+                                    unsigned int http_status,
+                                    enum TALER_ErrorCode ec,
+                                    const struct TALER_MerchantPublicKeyP 
*merchant_pub,
+                                    const struct GNUNET_HashCode *h_contract,
+                                    unsigned int num_refunds,
+                                    const struct TALER_MERCHANT_RefundEntry 
*res,
+                                    const json_t *obj);
+
+
+/**
+ * Run a payment abort operation, asking for refunds for coins
+ * that were previously spend on a /pay that failed to go through.
+ *
+ * @param ctx execution context
+ * @param merchant_url base URL of the merchant
+ * @param instance which merchant instance will receive this payment
+ * @param h_wire hash of the merchant’s account details
+ * @param h_contract hash of the contact of the merchant with the customer
+ * @param transaction_id transaction id for the transaction between merchant 
and customer
+ * @param amount total value of the contract to be paid to the merchant
+ * @param max_fee maximum fee covered by the merchant (according to the 
contract)
+ * @param merchant_pub the public key of the merchant (used to identify the 
merchant for refund requests)
+ * @param merchant_sig signature from the merchant over the original contract
+ * @param timestamp timestamp when the contract was finalized, must match 
approximately the current time of the merchant
+ * @param refund_deadline date until which the merchant can issue a refund to 
the customer via the merchant (can be zero if refunds are not allowed)
+ * @param pay_deadline maximum time limit to pay for this contract
+ * @param num_coins number of coins used to pay
+ * @param coins array of coins we use to pay
+ * @param coin_sig the signature made with purpose 
#TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s 
private key.
+ * @param payref_cb the callback to call when a reply for this request is 
available
+ * @param payref_cb_cls closure for @a pay_cb
+ * @return a handle for this request
+ */
+struct TALER_MERCHANT_Pay *
+TALER_MERCHANT_pay_abort (struct GNUNET_CURL_Context *ctx,
+                         const char *merchant_url,
+                         const char *instance,
+                         const struct GNUNET_HashCode *h_contract,
+                         const struct TALER_Amount *amount,
+                         const struct TALER_Amount *max_fee,
+                         const struct TALER_MerchantPublicKeyP *merchant_pub,
+                         const struct TALER_MerchantSignatureP *merchant_sig,
+                         struct GNUNET_TIME_Absolute timestamp,
+                         struct GNUNET_TIME_Absolute refund_deadline,
+                         struct GNUNET_TIME_Absolute pay_deadline,
+                         const struct GNUNET_HashCode *h_wire,
+                         const char *order_id,
+                         unsigned int num_coins,
+                         const struct TALER_MERCHANT_PayCoin *coins,
+                         TALER_MERCHANT_PayRefundCallback payref_cb,
+                         void *payref_cb_cls);
+
+
+/**
  * Information we need from the frontend (ok, the frontend sends just JSON)
  * when forwarding a payment to the backend.
  */
@@ -399,6 +502,16 @@ struct TALER_MERCHANT_PaidCoin
    */
   struct TALER_Amount amount_without_fee;
 
+  /**
+   * Fee the exchange charges for refunds of this coin.
+   */
+  struct TALER_Amount refund_fee;
+
+  /**
+   * What is the URL of the exchange that issued @a coin_pub?
+   */
+  const char *exchange_url;
+
 };
 
 
@@ -409,7 +522,7 @@ struct TALER_MERCHANT_PaidCoin
  * in the type of @a coins compared to #TALER_MERCHANT_pay().
  *
  * @param ctx execution context
- * @param merchant_uri base URI of the merchant
+ * @param merchant_url base URL of the merchant
  * @param instance which merchant instance will receive this payment
  * @param h_contract hash of the contact of the merchant with the customer
  * @param amount total value of the contract to be paid to the merchant
@@ -420,7 +533,6 @@ struct TALER_MERCHANT_PaidCoin
  * @param pay_deadline maximum time limit to pay for this contract
  * @param timestamp timestamp when the contract was finalized, must match 
approximately the current time of the merchant
  * @param wire_transfer_deadline date by which the merchant would like the 
exchange to execute the wire transfer (can be zero if there is no specific date 
desired by the frontend). If non-zero, must be larger than @a refund_deadline.
- * @param exchange_uri URI of the exchange that the coins belong to
  * @param num_coins number of coins used to pay
  * @param coins array of coins we use to pay
  * @param coin_sig the signature made with purpose 
#TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s 
private key.
@@ -430,10 +542,9 @@ struct TALER_MERCHANT_PaidCoin
  */
 struct TALER_MERCHANT_Pay *
 TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx,
-                            const char *merchant_uri,
+                            const char *merchant_url,
                              const struct TALER_MerchantPublicKeyP 
*merchant_pub,
                             const char *order_id,
-                            const char *exchange_uri,
                              unsigned int num_coins,
                              const struct TALER_MERCHANT_PaidCoin *coins,
                              TALER_MERCHANT_PayCallback pay_cb,
@@ -517,7 +628,7 @@ typedef void
  * Request backend to return deposits associated with a given wtid.
  *
  * @param ctx execution context
- * @param backend_uri base URI of the backend
+ * @param backend_url base URL of the backend
  * @param instance which merchant instance is going to be tracked
  * @param wire_method wire method used for the wire transfer
  * @param wtid base32 string indicating a wtid
@@ -528,11 +639,11 @@ typedef void
  */
 struct TALER_MERCHANT_TrackTransferHandle *
 TALER_MERCHANT_track_transfer (struct GNUNET_CURL_Context *ctx,
-                               const char *backend_uri,
+                               const char *backend_url,
                                const char *instance,
                               const char *wire_method,
                                const struct TALER_WireTransferIdentifierRawP 
*wtid,
-                               const char *exchange_uri,
+                               const char *exchange_url,
                                TALER_MERCHANT_TrackTransferCallback 
track_transfer_cb,
                                void *track_transfer_cb_cls);
 
@@ -578,36 +689,6 @@ struct TALER_MERCHANT_CoinWireTransfer
 
 };
 
-/**
- * Information about a wire transfer for a /track/transaction response.
- */
-struct TALER_MERCHANT_TransactionWireTransfer
-{
-
-  /**
-   * Wire transfer identifier this struct is about.
-   */
-  struct TALER_WireTransferIdentifierRawP wtid;
-
-  /**
-   * When was this wire transfer executed?
-   */
-  struct GNUNET_TIME_Absolute execution_time;
-
-  /**
-   * Number of coins of the selected transaction that
-   * is covered by this wire transfer.
-   */
-  unsigned int num_coins;
-
-  /**
-   * Information about the coins of the selected transaction
-   * that are part of the wire transfer.
-   */
-  struct TALER_MERCHANT_CoinWireTransfer *coins;
-
-};
-
 
 /**
  * Callbacks of this type are used to work the result of submitting a
@@ -631,7 +712,7 @@ typedef void
  * Request backend to return deposits associated with a given wtid.
  *
  * @param ctx execution context
- * @param backend_uri base URI of the backend
+ * @param backend_url base URL of the backend
  * @param instance which merchant instance is going to be tracked
  * @param transaction_id which transaction should we trace
  * @param track_transaction_cb the callback to call when a reply for this 
request is available
@@ -640,7 +721,7 @@ typedef void
  */
 struct TALER_MERCHANT_TrackTransactionHandle *
 TALER_MERCHANT_track_transaction (struct GNUNET_CURL_Context *ctx,
-                                  const char *backend_uri,
+                                  const char *backend_url,
                                   const char *instance,
                                   const char *order_id,
                                   TALER_MERCHANT_TrackTransactionCallback 
track_transaction_cb,
@@ -680,7 +761,7 @@ typedef void
  * Issue a /history request to the backend.
  *
  * @param ctx execution context
- * @param backend_uri base URL of the merchant backend
+ * @param backend_url base URL of the merchant backend
  * @param instance which merchant instance is performing this call
  * @param start return @a delta records starting from position @a start
  * @param delta return @a delta records starting from position @a start
@@ -691,7 +772,7 @@ typedef void
  */
 struct TALER_MERCHANT_HistoryOperation *
 TALER_MERCHANT_history (struct GNUNET_CURL_Context *ctx,
-                        const char *backend_uri,
+                        const char *backend_url,
                         const char *instance,
                         unsigned int start,
                         unsigned int delta,
@@ -710,71 +791,6 @@ void
 TALER_MERCHANT_history_cancel (struct TALER_MERCHANT_HistoryOperation *ho);
 
 
-/* ********************** /tip-enable ************************* */
-
-
-/**
- * Handle for a /tip-enable operation.
- */
-struct TALER_MERCHANT_TipEnableOperation;
-
-
-/**
- * Callback for a /tip-enable request.  Returns the result of
- * the operation.
- *
- * @param cls closure
- * @param http_status HTTP status returned by the merchant backend
- * @param ec taler-specific error code
- */
-typedef void
-(*TALER_MERCHANT_TipEnableCallback) (void *cls,
-                                     unsigned int http_status,
-                                     enum TALER_ErrorCode ec);
-
-
-/**
- * Issue a /tip-enable request to the backend.  Informs the backend
- * that a reserve is now available for tipping.  Note that the
- * respective @a reserve_priv must also be bound to one or more
- * instances (together with the URI of the exchange) via the backend's
- * configuration file before it can be used.  Usually, the process
- * is that one first configures an exchange and a @a reserve_priv for
- * an instance, and then enables (or re-enables) the reserve by
- * performing wire transfers and informs the backend about it using
- * this API.
- *
- * @param ctx execution context
- * @param backend_uri base URL of the merchant backend
- * @param amount amount that was credited to the reserve
- * @param expiration when will the reserve expire
- * @param reserve_priv private key of the reserve
- * @param credit_uuid unique ID of the wire transfer
- * @param enable_cb callback which will work the response gotten from the 
backend
- * @param enable_cb_cls closure to pass to @a enable_cb
- * @return handle for this operation, NULL upon errors
- */
-struct TALER_MERCHANT_TipEnableOperation *
-TALER_MERCHANT_tip_enable (struct GNUNET_CURL_Context *ctx,
-                           const char *backend_uri,
-                           const struct TALER_Amount *amount,
-                           struct GNUNET_TIME_Absolute expiration,
-                           const struct TALER_ReservePrivateKeyP *reserve_priv,
-                           const struct GNUNET_HashCode *credit_uuid,
-                           TALER_MERCHANT_TipEnableCallback enable_cb,
-                           void *enable_cb_cls);
-
-
-
-/**
- * Cancel a pending /tip-enable request
- *
- * @param teo handle from the operation to cancel
- */
-void
-TALER_MERCHANT_tip_enable_cancel (struct TALER_MERCHANT_TipEnableOperation 
*teo);
-
-
 /* ********************** /tip-authorize ********************** */
 
 /**
@@ -792,7 +808,7 @@ struct TALER_MERCHANT_TipAuthorizeOperation;
  * @param ec taler-specific error code
  * @param tip_id which tip ID should be used to pickup the tip
  * @param tip_expiration when does the tip expire (needs to be picked up 
before this time)
- * @param exchange_uri at what exchange can the tip be picked up
+ * @param exchange_url at what exchange can the tip be picked up
  */
 typedef void
 (*TALER_MERCHANT_TipAuthorizeCallback) (void *cls,
@@ -800,7 +816,7 @@ typedef void
                                         enum TALER_ErrorCode ec,
                                         const struct GNUNET_HashCode *tip_id,
                                         struct GNUNET_TIME_Absolute 
tip_expiration,
-                                        const char *exchange_uri);
+                                        const char *exchange_url);
 
 
 /**
@@ -808,7 +824,9 @@ typedef void
  * that a tip should be created.
  *
  * @param ctx execution context
- * @param backend_uri base URL of the merchant backend
+ * @param backend_url base URL of the merchant backend
+ * @param pickup_url frontend URL for where the tip can be picked up
+ * @param next_url where the browser should proceed after picking up the tip
  * @param amount amount to be handed out as a tip
  * @param instance which backend instance should create the tip (identifies 
the reserve and exchange)
  * @param justification which justification should be stored (human-readable 
reason for the tip)
@@ -818,7 +836,9 @@ typedef void
  */
 struct TALER_MERCHANT_TipAuthorizeOperation *
 TALER_MERCHANT_tip_authorize (struct GNUNET_CURL_Context *ctx,
-                              const char *backend_uri,
+                              const char *backend_url,
+                              const char *pickup_url,
+                              const char *next_url,
                               const struct TALER_Amount *amount,
                               const char *instance,
                               const char *justification,
@@ -871,7 +891,7 @@ typedef void
  * that a customer wants to pick up a tip.
  *
  * @param ctx execution context
- * @param backend_uri base URL of the merchant backend
+ * @param backend_url base URL of the merchant backend
  * @param tip_id unique identifier for the tip
  * @param num_planches number of planchets provided in @a planchets
  * @param planchets array of planchets to be signed into existence for the tip
@@ -881,7 +901,7 @@ typedef void
  */
 struct TALER_MERCHANT_TipPickupOperation *
 TALER_MERCHANT_tip_pickup (struct GNUNET_CURL_Context *ctx,
-                           const char *backend_uri,
+                           const char *backend_url,
                            const struct GNUNET_HashCode *tip_id,
                            unsigned int num_planchets,
                            struct TALER_PlanchetDetail *planchets,
@@ -898,4 +918,138 @@ void
 TALER_MERCHANT_tip_pickup_cancel (struct TALER_MERCHANT_TipPickupOperation 
*tp);
 
 
+/* ********************** /check-payment ************************* */
+
+
+/**
+ * Handle for a /check-payment operation.
+ */
+struct TALER_MERCHANT_CheckPaymentOperation;
+
+
+/**
+ * Callback to process a GET /check-payment request
+ *
+ * @param cls closure
+ * @param http_status HTTP status code for this request
+ * @param obj raw response body
+ * @param paid #GNUNET_YES if the payment is settled, #GNUNET_NO if not
+ *        settled, $GNUNET_SYSERR on error
+ *        (note that refunded payments are returned as paid!)
+ * @param refunded #GNUNET_YES if there is at least on refund on this payment,
+ *        #GNUNET_NO if refunded, #GNUNET_SYSERR or error
+ * @param refunded_amount amount that was refunded, NULL if there
+ *        was no refund
+ * @param payment_redirect_url URL to redirect the browser to in order to
+ *        execute or re-play the payment (NULL if not applicable)
+ */
+typedef void
+(*TALER_MERCHANT_CheckPaymentCallback) (void *cls,
+                                       unsigned int http_status,
+                                       const json_t *obj,
+                                        int paid,
+                                        int refunded,
+                                        struct TALER_Amount *refund_amount,
+                                        const char *payment_redirect_url);
+
+
+/**
+ * Issue a /check-payment request to the backend.  Checks the status
+ * of a payment.
+ *
+ * @param ctx execution context
+ * @param backend_url base URL of the merchant backend
+ * @param instance instance used for the transaction
+ * @param order_id order id to identify the payment
+ * @parem resource_url resource URL to identify duplicate payments (can be 
NULL)
+ * @parem session_id sesion id for the payment (or NULL if the payment is not 
bound to a session) 
+ * @parem session_id sesion signature for the payment (or NULL if the payment
+ *        is not bound to a session or the session is not signed yet) 
+ * @param check_payment_cb callback which will work the response gotten from 
the backend
+ * @param check_payment_cb_cls closure to pass to @a check_payment_cb
+ * @return handle for this operation, NULL upon errors
+ */
+struct TALER_MERCHANT_CheckPaymentOperation *
+TALER_MERCHANT_check_payment (struct GNUNET_CURL_Context *ctx,
+                              const char *backend_url,
+                              const char *instance,
+                              const char *order_id,
+                              const char *resource_url,
+                              const char *session_id,
+                              const char *session_sig,
+                              TALER_MERCHANT_CheckPaymentCallback 
check_payment_cb,
+                              void *check_payment_cls);
+
+/**
+ * Cancel a GET /check-payment request.
+ *
+ * @param cpo handle to the request to be canceled
+ */
+void
+TALER_MERCHANT_check_payment_cancel (struct 
TALER_MERCHANT_CheckPaymentOperation *cpo);
+
+
+/* ********************** /tip-query ************************* */
+
+/**
+ * Handle for a /tip-query operation.
+ */
+struct TALER_MERCHANT_TipQueryOperation;
+
+
+/**
+ * Callback to process a GET /tip-query request
+ *
+ * @param cls closure
+ * @param http_status HTTP status code for this request
+ * @param ec Taler-specific error code
+ * @param raw raw response body
+ */
+typedef void
+(*TALER_MERCHANT_TipQueryCallback) (void *cls,
+                                    unsigned int http_status,
+                                    enum TALER_ErrorCode ec,
+                                    const json_t *raw,
+                                    struct GNUNET_TIME_Absolute 
reserve_expiration,
+                                    struct TALER_ReservePublicKeyP 
*reserve_pub,
+                                    struct TALER_Amount *amount_authorized,
+                                    struct TALER_Amount *amount_available,
+                                    struct TALER_Amount *amount_picked_up);
+
+
+/**
+ * Cancel a GET /tip-query request.
+ *
+ * @param cph handle to the request to be canceled
+ */
+void
+TALER_MERCHANT_tip_query_cancel (struct TALER_MERCHANT_TipQueryOperation *tqo);
+
+
+/**
+ * Issue a /tip-query request to the backend.  Informs the backend
+ * that a customer wants to pick up a tip.
+ *
+ * @param ctx execution context
+ * @param backend_url base URL of the merchant backend
+ * @param instance instance to query
+ * @return handle for this operation, NULL upon errors
+ */
+struct TALER_MERCHANT_TipQueryOperation *
+TALER_MERCHANT_tip_query (struct GNUNET_CURL_Context *ctx,
+                          const char *backend_url,
+                          const char *instance,
+                          TALER_MERCHANT_TipQueryCallback query_cb,
+                          void *query_cb_cls);
+
+
+/**
+ * Cancel a GET /tip-query request.
+ *
+ * @param tqo handle to the request to be canceled
+ */
+void
+TALER_MERCHANT_tip_query_cancel (struct TALER_MERCHANT_TipQueryOperation *tqh);
+
+
 #endif  /* _TALER_MERCHANT_SERVICE_H */
diff --git a/src/include/taler_merchantdb_plugin.h 
b/src/include/taler_merchantdb_plugin.h
index b36a04e..6f497b0 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -51,7 +51,6 @@ struct TALER_MERCHANTDB_Plugin;
  *
  * @param cls closure
  * @param merchant_pub merchant's public key
- * @param exchange_uri URI of the exchange
  * @param h_contract_terms proposal data's hashcode
  * @param h_wire hash of our wire details
  * @param timestamp time of the confirmation
@@ -61,7 +60,6 @@ struct TALER_MERCHANTDB_Plugin;
 typedef void
 (*TALER_MERCHANTDB_TransactionCallback)(void *cls,
                                        const struct TALER_MerchantPublicKeyP 
*merchant_pub,
-                                        const char *exchange_uri,
                                         const struct GNUNET_HashCode 
*h_contract_terms,
                                         const struct GNUNET_HashCode *h_wire,
                                         struct GNUNET_TIME_Absolute timestamp,
@@ -75,9 +73,11 @@ typedef void
  * @param cls closure
  * @param h_contract_terms proposal data's hashcode
  * @param coin_pub public key of the coin
+ * @param exchange_url URL of the exchange that issued the coin
  * @param amount_with_fee amount the exchange will deposit for this coin
  * @param deposit_fee fee the exchange will charge for this coin
  * @param refund_fee fee the exchange will charge for refunding this coin
+ * @param wire_fee wire fee the exchange charges
  * @param exchange_proof proof from exchange that coin was accepted,
  *        matches the `interface DepositSuccess` of the documentation.
  */
@@ -85,9 +85,11 @@ typedef void
 (*TALER_MERCHANTDB_CoinDepositCallback)(void *cls,
                                         const struct GNUNET_HashCode 
*h_contract_terms,
                                         const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
+                                       const char *exchange_url,
                                         const struct TALER_Amount 
*amount_with_fee,
                                         const struct TALER_Amount *deposit_fee,
                                         const struct TALER_Amount *refund_fee,
+                                       const struct TALER_Amount *wire_fee,
                                         const json_t *exchange_proof);
 
 
@@ -182,6 +184,23 @@ struct TALER_MERCHANTDB_Plugin
   int
   (*initialize) (void *cls);
 
+  /**
+   * Insert order into db.
+   *
+   * @param cls closure
+   * @param order_id alphanumeric string that uniquely identifies the proposal
+   * @param merchant_pub merchant's public key
+   * @param timestamp timestamp of this proposal data
+   * @param contract_terms proposal data to store
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*insert_order) (void *cls,
+                   const char *order_id,
+                   const struct TALER_MerchantPublicKeyP *merchant_pub,
+                   struct GNUNET_TIME_Absolute timestamp,
+                   const json_t *contract_terms);
+
 
   /**
    * Insert proposal data into db; the routine will internally hash and
@@ -214,18 +233,22 @@ struct TALER_MERCHANTDB_Plugin
    * @param cls closure
    * @param h_contract_terms hash of the contract that is now paid
    * @param merchant_pub merchant's public key
+   * @param last_session_id session id used for the payment, NULL
+   *        if payment was not session-bound
    * @return transaction status
    */
   enum GNUNET_DB_QueryStatus
   (*mark_proposal_paid) (void *cls,
                          const struct GNUNET_HashCode *h_contract_terms,
-                         const struct TALER_MerchantPublicKeyP *merchant_pub);
+                         const struct TALER_MerchantPublicKeyP *merchant_pub,
+                         const char *last_session_id);
 
   /**
    * Retrieve proposal data given its order ID.
    *
    * @param cls closure
    * @param[out] contract_terms where to store the result
+   * @param[out] last_session_id where to store the result
    * @param order_id order_id used to lookup.
    * @param merchant_pub instance's public key.
    * @return transaction status
@@ -233,9 +256,25 @@ struct TALER_MERCHANTDB_Plugin
   enum GNUNET_DB_QueryStatus
   (*find_contract_terms) (void *cls,
                          json_t **contract_terms,
+                         char **last_session_id,
                          const char *order_id,
                          const struct TALER_MerchantPublicKeyP *merchant_pub);
 
+  /**
+   * Retrieve order given its order id and the instance's merchant public key.
+   *
+   * @param cls closure
+   * @param[out] contract_terms where to store the retrieved contract terms
+   * @param order id order id used to perform the lookup
+   * @param merchant_pub merchant public key that identifies the instance
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*find_order) (void *cls,
+                 json_t **contract_terms,
+                 const char *order_id,
+                 const struct TALER_MerchantPublicKeyP *merchant_pub);
+
 
   /**
    * Retrieve proposal data given its hashcode
@@ -253,6 +292,21 @@ struct TALER_MERCHANTDB_Plugin
                                    const struct TALER_MerchantPublicKeyP 
*merchant_pub);
 
 
+  /**
+   * Retrieve paid contract terms data given its hashcode.
+   *
+   * @param cls closure
+   * @param[out] contract_terms where to store the result
+   * @param h_contract_terms hashcode used to lookup.
+   * @param merchant_pub instance's public key.
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*find_paid_contract_terms_from_hash) (void *cls,
+                                         json_t **contract_terms,
+                                         const struct GNUNET_HashCode 
*h_contract_terms,
+                                         const struct TALER_MerchantPublicKeyP 
*merchant_pub);
+
 
   /**
    * Return proposals whose timestamp are older than `date`.
@@ -324,38 +378,16 @@ struct TALER_MERCHANTDB_Plugin
 
 
   /**
-   * Insert transaction data into the database.
-   *
-   * @param cls closure
-   * @param h_contract_terms proposal data's hashcode
-   * @param merchant_pub merchant's public key
-   * @param exchange_uri URI of the exchange
-   * @param h_wire hash of our wire details
-   * @param timestamp time of the confirmation
-   * @param refund refund deadline
-   * @param total_amount total amount we receive for the contract after fees
-   * @return transaction status
-   */
-  enum GNUNET_DB_QueryStatus
-  (*store_transaction) (void *cls,
-                        const struct GNUNET_HashCode *h_contract_terms,
-                       const struct TALER_MerchantPublicKeyP *merchant_pub,
-                        const char *exchange_uri,
-                        const struct GNUNET_HashCode *h_wire,
-                        struct GNUNET_TIME_Absolute timestamp,
-                        struct GNUNET_TIME_Absolute refund,
-                        const struct TALER_Amount *total_amount);
-
-
-  /**
    * Insert payment confirmation from the exchange into the database.
    *
    * @param cls closure
    * @param h_contract_terms proposal data's hashcode
    * @param merchant_pub merchant's public key
    * @param coin_pub public key of the coin
+   * @param exchange_url URL of the exchange that issued @a coin_pub
    * @param amount_with_fee amount the exchange will deposit for this coin
    * @param deposit_fee fee the exchange will charge for this coin
+   * @param wire_fee wire fee the exchange charges
    * @param signkey_pub public key used by the exchange for @a exchange_proof
    * @param exchange_proof proof from exchange that coin was accepted
    * @return transaction status
@@ -365,9 +397,11 @@ struct TALER_MERCHANTDB_Plugin
                     const struct GNUNET_HashCode *h_contract_terms,
                     const struct TALER_MerchantPublicKeyP *merchant_pub,
                     const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                   const char *exchange_url,
                     const struct TALER_Amount *amount_with_fee,
                     const struct TALER_Amount *deposit_fee,
                     const struct TALER_Amount *refund_fee,
+                    const struct TALER_Amount *wire_fee,
                     const struct TALER_ExchangePublicKeyP *signkey_pub,
                     const json_t *exchange_proof);
 
@@ -394,7 +428,7 @@ struct TALER_MERCHANTDB_Plugin
    * Insert wire transfer confirmation from the exchange into the database.
    *
    * @param cls closure
-   * @param exchange_uri from which exchange did we get the @a exchange_proof
+   * @param exchange_url from which exchange did we get the @a exchange_proof
    * @param wtid identifier of the wire transfer
    * @param execution_time when was @a wtid executed
    * @param signkey_pub public key used by the exchange for @a exchange_proof
@@ -403,7 +437,7 @@ struct TALER_MERCHANTDB_Plugin
    */
   enum GNUNET_DB_QueryStatus
   (*store_transfer_to_proof) (void *cls,
-                              const char *exchange_uri,
+                              const char *exchange_url,
                               const struct TALER_WireTransferIdentifierRawP 
*wtid,
                               struct GNUNET_TIME_Absolute execution_time,
                               const struct TALER_ExchangePublicKeyP 
*signkey_pub,
@@ -436,39 +470,6 @@ struct TALER_MERCHANTDB_Plugin
                                 const struct TALER_MasterSignatureP 
*exchange_sig);
 
 
-  /**
-   * Find information about a transaction.
-   *
-   * @param cls our plugin handle
-   * @param date limit to transactions' age
-   * @param cb function to call with transaction data
-   * @param cb_cls closure for @a cb
-   * @return transaction status
-   */
-  enum GNUNET_DB_QueryStatus
-  (*find_transactions_by_date) (void *cls,
-                                struct GNUNET_TIME_Absolute date,
-                                TALER_MERCHANTDB_TransactionCallback cb,
-                                void *cb_cls);
-
-
-  /**
-   * Find information about a transaction.
-   *
-   * @param cls our plugin handle
-   * @param h_contract_terms proposal data's hashcode
-   * @param merchant_pub merchant's public key.
-   * @param cb function to call with transaction data
-   * @param cb_cls closure for @a cb
-   * @return transaction status
-   */
-  enum GNUNET_DB_QueryStatus
-  (*find_transaction) (void *cls,
-                       const struct GNUNET_HashCode *h_contract_terms,
-                      const struct TALER_MerchantPublicKeyP *merchant_pub,
-                       TALER_MERCHANTDB_TransactionCallback cb,
-                       void *cb_cls);
-
 
   /**
    * Lookup information about coin payments by proposal data's hashcode.
@@ -550,7 +551,7 @@ struct TALER_MERCHANTDB_Plugin
    * Lookup proof information about a wire transfer.
    *
    * @param cls closure
-   * @param exchange_uri from which exchange are we looking for proof
+   * @param exchange_url from which exchange are we looking for proof
    * @param wtid wire transfer identifier for the search
    * @param cb function to call with proof data
    * @param cb_cls closure for @a cb
@@ -558,7 +559,7 @@ struct TALER_MERCHANTDB_Plugin
    */
   enum GNUNET_DB_QueryStatus
   (*find_proof_by_wtid) (void *cls,
-                         const char *exchange_uri,
+                         const char *exchange_url,
                          const struct TALER_WireTransferIdentifierRawP *wtid,
                          TALER_MERCHANTDB_ProofCallback cb,
                          void *cb_cls);
@@ -663,7 +664,7 @@ struct TALER_MERCHANTDB_Plugin
    * @param justification why was the tip approved
    * @param amount how high is the tip (with fees)
    * @param reserve_priv which reserve is debited
-   * @param exchange_uri which exchange manages the tip
+   * @param exchange_url which exchange manages the tip
    * @param[out] expiration set to when the tip expires
    * @param[out] tip_id set to the unique ID for the tip
    * @return transaction status,
@@ -679,17 +680,33 @@ struct TALER_MERCHANTDB_Plugin
                    const char *justification,
                    const struct TALER_Amount *amount,
                    const struct TALER_ReservePrivateKeyP *reserve_priv,
-                  const char *exchange_uri,
+                  const char *exchange_url,
                    struct GNUNET_TIME_Absolute *expiration,
                    struct GNUNET_HashCode *tip_id);
 
+  /**
+   * Get the total amount of authorized tips for a tipping reserve.
+   *
+   * @param cls closure, typically a connection to the db
+   * @param reserve_priv which reserve to check
+   * @param[out] authorzed_amount amount we've authorized so far for tips
+   * @return transaction status, usually
+   *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
+   *      #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the reserve_priv
+   *      does not identify a known tipping reserve
+   */
+  enum GNUNET_DB_QueryStatus
+  (*get_authorized_tip_amount)(void *cls,
+                               const struct TALER_ReservePrivateKeyP 
*reserve_priv,
+                               struct TALER_Amount *authorized_amount);
+
 
   /**
    * Find out tip authorization details associated with @a tip_id
    *
    * @param cls closure, typically a connection to the d
    * @param tip_id the unique ID for the tip
-   * @param[out] exchange_uri set to the URI of the exchange (unless NULL)
+   * @param[out] exchange_url set to the URL of the exchange (unless NULL)
    * @param[out] amount set to the authorized amount (unless NULL)
    * @param[out] timestamp set to the timestamp of the tip authorization 
(unless NULL)
    * @return transaction status, usually
@@ -699,7 +716,7 @@ struct TALER_MERCHANTDB_Plugin
   enum GNUNET_DB_QueryStatus
   (*lookup_tip_by_id)(void *cls,
                       const struct GNUNET_HashCode *tip_id,
-                      char **exchange_uri,
+                      char **exchange_url,
                       struct TALER_Amount *amount,
                       struct GNUNET_TIME_Absolute *timestamp);
 
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 5f46122..d953081 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -14,16 +14,16 @@ libtalermerchant_la_LDFLAGS = \
   -no-undefined
 
 libtalermerchant_la_SOURCES = \
-  merchant_api_common.c merchant_api_common.h \
   merchant_api_proposal.c \
   merchant_api_pay.c \
   merchant_api_tip_authorize.c \
-  merchant_api_tip_enable.c \
   merchant_api_tip_pickup.c \
   merchant_api_track_transaction.c \
   merchant_api_track_transfer.c \
   merchant_api_history.c \
-  merchant_api_refund.c
+  merchant_api_refund.c \
+  merchant_api_check_payment.c \
+  merchant_api_tip_query.c
 
 libtalermerchant_la_LIBADD = \
   -ltalerexchange \
diff --git a/src/lib/merchant_api_check_payment.c 
b/src/lib/merchant_api_check_payment.c
new file mode 100644
index 0000000..98e844d
--- /dev/null
+++ b/src/lib/merchant_api_check_payment.c
@@ -0,0 +1,258 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2018 GNUnet e.V. and INRIA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Lesser General Public License as published by the Free 
Software
+  Foundation; either version 2.1, 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 Lesser General Public License for more 
details.
+
+  You should have received a copy of the GNU Lesser General Public License 
along with
+  TALER; see the file COPYING.LGPL.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file lib/merchant_api_check_payment.c
+ * @brief Implementation of the /check-payment GET request
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ * @author Florian Dold
+ */
+#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_curl_lib.h>
+#include "taler_merchant_service.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * @brief A check payment operation handle
+ */
+struct TALER_MERCHANT_CheckPaymentOperation
+{
+
+  /**
+   * The url for this request.
+   */
+  char *url;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * Function to call with the result.
+   */
+  TALER_MERCHANT_CheckPaymentCallback cb;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Reference to the execution context.
+   */
+  struct GNUNET_CURL_Context *ctx;
+};
+
+
+/**
+ * Function called when we're done processing the GET /check-payment request.
+ *
+ * @param cls the `struct TALER_MERCHANT_CheckPaymentOperation`
+ * @param response_code HTTP response code, 0 on error
+ * @param json response body, should be NULL
+ */
+static void
+handle_check_payment_finished (void *cls,
+                               long response_code,
+                               const json_t *json)
+{
+  struct TALER_MERCHANT_CheckPaymentOperation *cpo = cls;
+  struct TALER_Amount refund_amount = { 0 };
+  int refunded;
+  
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_boolean ("refunded",
+                              &refunded),
+    TALER_JSON_spec_amount ("refund_amount",
+                            &refund_amount),
+    GNUNET_JSON_spec_end()
+  };
+
+  cpo->job = NULL;
+
+  if (MHD_HTTP_OK != response_code)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Checking payment failed with HTTP status code %u\n",
+                (unsigned int) response_code);
+    GNUNET_break_op (0);
+    cpo->cb (cpo->cb_cls,
+             response_code,
+             json,
+             GNUNET_SYSERR,
+             GNUNET_SYSERR,
+             &refund_amount,
+             NULL);
+    TALER_MERCHANT_check_payment_cancel (cpo);
+    return;
+  }
+
+  if (! json_boolean_value (json_object_get (json, "paid")))
+  {
+    const char *payment_redirect_url = json_string_value (json_object_get 
(json, "payment_redirect_url"));
+    if (NULL == payment_redirect_url)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "no payment_redirect_url in unpaid check-payment 
response\n");
+      GNUNET_break_op (0);
+      cpo->cb (cpo->cb_cls,
+               0,
+               json,
+               GNUNET_SYSERR,
+               GNUNET_SYSERR,
+               &refund_amount,
+               NULL);
+    } else {
+      cpo->cb (cpo->cb_cls,
+               response_code,
+               json,
+               GNUNET_NO,
+               GNUNET_NO,
+               &refund_amount,
+               payment_redirect_url);
+    }
+    TALER_MERCHANT_check_payment_cancel (cpo);
+    return;
+  }
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "check payment failed to parse JSON\n");
+    GNUNET_break_op (0);
+    cpo->cb (cpo->cb_cls,
+             0,
+             json,
+             GNUNET_SYSERR,
+             GNUNET_SYSERR,
+             &refund_amount,
+             NULL);
+    TALER_MERCHANT_check_payment_cancel (cpo);
+    return;
+  }
+
+  cpo->cb (cpo->cb_cls,
+           MHD_HTTP_OK,
+           json,
+           GNUNET_YES,
+           refunded,
+           &refund_amount,
+           NULL);
+  GNUNET_JSON_parse_free (spec);
+  TALER_MERCHANT_check_payment_cancel (cpo);
+}
+
+
+/**
+ * Issue a /check-payment request to the backend.  Checks the status
+ * of a payment.
+ *
+ * @param ctx execution context
+ * @param backend_url base URL of the merchant backend
+ * @param instance instance used for the transaction
+ * @param order_id order id to identify the payment
+ * @parem resource_url resource URL to identify duplicate payments (can be 
NULL)
+ * @parem session_id sesion id for the payment (or NULL if the payment is not 
bound to a session) 
+ * @parem session_id sesion signature for the payment (or NULL if the payment
+ *        is not bound to a session or the session is not signed yet) 
+ * @param check_payment_cb callback which will work the response gotten from 
the backend
+ * @param check_payment_cb_cls closure to pass to @a check_payment_cb
+ * @return handle for this operation, NULL upon errors
+ */
+struct TALER_MERCHANT_CheckPaymentOperation *
+TALER_MERCHANT_check_payment (struct GNUNET_CURL_Context *ctx,
+                              const char *backend_url,
+                              const char *instance,
+                              const char *order_id,
+                              const char *resource_url,
+                              const char *session_id,
+                              const char *session_sig,
+                              TALER_MERCHANT_CheckPaymentCallback 
check_payment_cb,
+                              void *check_payment_cb_cls)
+{
+  struct TALER_MERCHANT_CheckPaymentOperation *cpo;
+  CURL *eh;
+
+  GNUNET_assert (NULL != backend_url);
+  GNUNET_assert (NULL != instance);
+  GNUNET_assert (NULL != order_id);
+
+  cpo = GNUNET_new (struct TALER_MERCHANT_CheckPaymentOperation);
+  cpo->ctx = ctx;
+  cpo->cb = check_payment_cb;
+  cpo->cb_cls = check_payment_cb_cls;
+  cpo->url = TALER_url_join (backend_url, "/check-payment",
+                             "instance", instance,
+                             "order_id", order_id,
+                             "resource_url", resource_url,
+                             "session_id", session_id,
+                             "session_sig", session_sig,
+                             NULL);
+  eh = curl_easy_init ();
+  if (CURLE_OK != curl_easy_setopt (eh,
+                                    CURLOPT_URL,
+                                    cpo->url))
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "checking payment from %s\n",
+              cpo->url);
+
+  if (NULL == (cpo->job = GNUNET_CURL_job_add (ctx,
+                                               eh,
+                                               GNUNET_YES,
+                                               &handle_check_payment_finished,
+                                               cpo)))
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  return cpo;
+}
+
+
+/**
+ * Cancel a GET /check-payment request.
+ *
+ * @param cph handle to the request to be canceled
+ */
+void
+TALER_MERCHANT_check_payment_cancel (struct 
TALER_MERCHANT_CheckPaymentOperation *cph)
+{
+  if (NULL != cph->job)
+  {
+    GNUNET_CURL_job_cancel (cph->job);
+    cph->job = NULL;
+  }
+  GNUNET_free (cph->url);
+  GNUNET_free (cph);
+}
+
+/* end of merchant_api_check_payment.c */
diff --git a/src/lib/merchant_api_common.c b/src/lib/merchant_api_common.c
deleted file mode 100644
index 05e2637..0000000
--- a/src/lib/merchant_api_common.c
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014-2017 GNUnet e.V. and INRIA
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU Lesser General Public License as published by the Free 
Software
-  Foundation; either version 2.1, 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 Lesser General Public License for more 
details.
-
-  You should have received a copy of the GNU Lesser General Public License 
along with
-  TALER; see the file COPYING.LGPL.  If not, see
-  <http://www.gnu.org/licenses/>
-*/
-/**
- * @file lib/merchant_api_common.c
- * @brief Shared functionality
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-
-
-/**
- * Obtain the URL to use for an API request.
- *
- * @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
- */
-char *
-MAH_path_to_url_ (const char *base_url,
-                 const char *path)
-{
-  char *url;
-
-  if ( ('/' == path[0]) &&
-       (0 < strlen (base_url)) &&
-       ('/' == base_url[strlen (base_url) - 1]) )
-    path++; /* avoid generating URL with "//" from concat */
-  GNUNET_asprintf (&url,
-                   "%s%s",
-                   base_url,
-                   path);
-  return url;
-}
-
-/* end of merchant_api_common.c */
diff --git a/src/lib/merchant_api_common.h b/src/lib/merchant_api_common.h
deleted file mode 100644
index 683f1db..0000000
--- a/src/lib/merchant_api_common.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014-2017 GNUnet e.V. and INRIA
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU Lesser General Public License as published by the Free 
Software
-  Foundation; either version 2.1, 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 Lesser General Public License for more 
details.
-
-  You should have received a copy of the GNU Lesser General Public License 
along with
-  TALER; see the file COPYING.LGPL.  If not, see
-  <http://www.gnu.org/licenses/>
-*/
-/**
- * @file lib/merchant_api_common.h
- * @brief Shared functions
- * @author Christian Grothoff
- */
-#ifndef MERCHANT_API_COMMON_H
-#define MERCHANT_API_COMMON_H
-
-/**
- * Obtain the URL to use for an API request.
- *
- * @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
- */
-char *
-MAH_path_to_url_ (const char *base_url,
-                 const char *path);
-
-#endif
diff --git a/src/lib/merchant_api_history.c b/src/lib/merchant_api_history.c
index efc20be..0a3e01a 100644
--- a/src/lib/merchant_api_history.c
+++ b/src/lib/merchant_api_history.c
@@ -27,7 +27,6 @@
 #include <gnunet/gnunet_curl_lib.h>
 #include "taler_merchant_service.h"
 #include <taler/taler_json_lib.h>
-#include "merchant_api_common.h"
 
 
 /**
@@ -110,7 +109,7 @@ history_raw_cb (void *cls,
     return;
   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-               "/history URI not found\n");
+               "/history URL not found\n");
     break;
   case MHD_HTTP_BAD_REQUEST:
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -137,7 +136,7 @@ history_raw_cb (void *cls,
  * Issue a /history request to the backend.
  *
  * @param ctx execution context
- * @param backend_uri base URL of the merchant backend
+ * @param backend_url base URL of the merchant backend
  * @param instance which merchant instance is performing this call
  * @param start return `delta` records starting from position `start`.
  * If given as zero, then no initial skip of `start` records is done.
@@ -149,7 +148,7 @@ history_raw_cb (void *cls,
  */
 struct TALER_MERCHANT_HistoryOperation *
 TALER_MERCHANT_history (struct GNUNET_CURL_Context *ctx,
-                        const char *backend_uri,
+                        const char *backend_url,
                         const char *instance,
                         unsigned int start,
                         unsigned int delta,
@@ -167,8 +166,7 @@ TALER_MERCHANT_history (struct GNUNET_CURL_Context *ctx,
   ho->cb = history_cb;
   ho->cb_cls = history_cb_cls;
   seconds = date.abs_value_us / 1000LL / 1000LL;
-  base = MAH_path_to_url_ (backend_uri,
-                          "/history");
+  base = TALER_url_join (backend_url, "/history", NULL);
   GNUNET_asprintf (&ho->url,
                    "%s?date=%llu&instance=%s&start=%d&delta=%d",
                    base,
diff --git a/src/lib/merchant_api_pay.c b/src/lib/merchant_api_pay.c
index a480061..849b22b 100644
--- a/src/lib/merchant_api_pay.c
+++ b/src/lib/merchant_api_pay.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 GNUnet e.V. and INRIA
+  Copyright (C) 2014, 2015, 2016, 2017 GNUnet e.V. and INRIA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Lesser General Public License as published by the Free 
Software
@@ -29,7 +29,6 @@
 #include <taler/taler_json_lib.h>
 #include <taler/taler_signatures.h>
 #include <taler/taler_exchange_service.h>
-#include "merchant_api_common.h"
 
 
 /**
@@ -54,34 +53,165 @@ struct TALER_MERCHANT_Pay
   struct GNUNET_CURL_Job *job;
 
   /**
-   * Function to call with the result.
+   * Function to call with the result in "pay" @e mode.
    */
-  TALER_MERCHANT_PayCallback cb;
+  TALER_MERCHANT_PayCallback pay_cb;
 
   /**
-   * Closure for @a cb.
+   * Closure for @a pay_cb.
    */
-  void *cb_cls;
+  void *pay_cb_cls;
 
   /**
+   * Function to call with the result in "abort-refund" @e mode.
+   */
+  TALER_MERCHANT_PayRefundCallback abort_cb;
+
+  /**
+   * Closure for @a abort_cb.
+   */
+  void *abort_cb_cls;
+
+  /**
+   * Operational mode, either "pay" or "abort-refund".
+   */
+  const char *mode;
+  
+  /**
    * Reference to the execution context.
    */
   struct GNUNET_CURL_Context *ctx;
 
   /**
+   * The coins we are paying with.
+   */
+  struct TALER_MERCHANT_PaidCoin *coins;
+
+  /**
    * Number of @e coins we are paying with.
    */
   unsigned int num_coins;
 
   /**
-   * The coins we are paying with.
+   * Hash of the contract, only available in "abort-refund" mode.
    */
-  struct TALER_MERCHANT_PaidCoin *coins;
-
+  struct GNUNET_HashCode h_contract_terms;
+  
 };
 
 
 /**
+ * Check that the response for a /pay refund is well-formed,
+ * and call the application callback with the result if it is
+ * OK. Otherwise returns #GNUNET_SYSERR.
+ *
+ * @param ph handle to operation that created the reply
+ * @param json the reply to parse
+ * @return #GNUNET_OK on success
+ */
+static int
+check_abort_refund (struct TALER_MERCHANT_Pay *ph,
+                   const json_t *json)
+{
+  json_t *refunds;
+  unsigned int num_refunds;
+  struct TALER_MerchantPublicKeyP merchant_pub;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_json ("refund_permissions", &refunds),
+    GNUNET_JSON_spec_fixed_auto ("merchant_pub", &merchant_pub),
+    GNUNET_JSON_spec_end()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  num_refunds = json_array_size (refunds);
+  {
+    struct TALER_MERCHANT_RefundEntry res[num_refunds];
+
+    for (unsigned int i=0;i<num_refunds;i++)
+    {
+      struct TALER_MerchantSignatureP *sig = &res[i].merchant_sig;
+      json_t *refund = json_array_get (refunds, i);
+      struct GNUNET_JSON_Specification spec_detail[] = {
+        GNUNET_JSON_spec_fixed_auto ("merchant_sig",
+                                    sig),
+        GNUNET_JSON_spec_fixed_auto ("coin_pub",
+                                    &res[i].coin_pub),
+       GNUNET_JSON_spec_uint64 ("rtransaction_id",
+                                &res[i].rtransaction_id),
+        GNUNET_JSON_spec_end()
+      };
+      struct TALER_RefundRequestPS rr;
+      int found;
+
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (refund,
+                             spec_detail,
+                             NULL, NULL))
+      {
+        GNUNET_break_op (0);
+       GNUNET_JSON_parse_free (spec);
+        return GNUNET_SYSERR;
+      }
+
+      rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
+      rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
+      rr.h_contract_terms = ph->h_contract_terms;
+      rr.coin_pub = res[i].coin_pub;
+      rr.merchant = merchant_pub;      
+      rr.rtransaction_id = GNUNET_htonll (res[i].rtransaction_id);
+      found = -1;
+      for (unsigned int j=0;j<ph->num_coins;j++)
+      {
+       if (0 == memcmp (&ph->coins[j].coin_pub,
+                        &res[i].coin_pub,
+                        sizeof (struct TALER_CoinSpendPublicKeyP)))
+       {
+         TALER_amount_hton (&rr.refund_amount,
+                            &ph->coins[j].amount_with_fee);
+         TALER_amount_hton (&rr.refund_fee,
+                            &ph->coins[j].refund_fee);
+         found = j;
+       }
+      }
+      if (-1 == found)
+      {
+       GNUNET_break_op (0);
+       return GNUNET_SYSERR;
+      }
+
+      if (GNUNET_OK !=
+         GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
+                                     &rr.purpose,
+                                     &sig->eddsa_sig,
+                                     &merchant_pub.eddsa_pub))
+      {
+       GNUNET_break_op (0);
+       return GNUNET_SYSERR;
+      }
+    }
+    ph->abort_cb (ph->abort_cb_cls,
+                 MHD_HTTP_OK,
+                 TALER_EC_NONE,
+                 &merchant_pub,
+                 &ph->h_contract_terms,
+                 num_refunds,
+                 res,
+                 json);
+    ph->abort_cb = NULL;
+  }
+  GNUNET_JSON_parse_free (spec);
+  return GNUNET_OK;
+}
+
+
+/**
  * We got a 403 response back from the exchange (or the merchant).
  * Now we need to check the provided cryptographic proof that the
  * coin was actually already spent!
@@ -196,204 +326,146 @@ handle_pay_finished (void *cls,
   struct TALER_MERCHANT_Pay *ph = cls;
 
   ph->job = NULL;
-  switch (response_code)
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+             "/pay completed with response code %u\n",
+             (unsigned int) response_code);
+  if (0 == strcasecmp (ph->mode,
+                      "pay"))
   {
-  case 0:
-    break;
-  case MHD_HTTP_OK:
-    break;
-  case MHD_HTTP_BAD_REQUEST:
-    /* This should never happen, either us or the merchant is buggy
-       (or API version conflict); just pass JSON reply to the application */
-    break;
-  case MHD_HTTP_FORBIDDEN:
-    if (GNUNET_OK != check_forbidden (ph,
-                                      json))
+    switch (response_code)
     {
-      GNUNET_break_op (0);
+    case 0:
+      break;
+    case MHD_HTTP_OK:
+      break;
+    case MHD_HTTP_BAD_REQUEST:
+      /* This should never happen, either us or the merchant is buggy
+        (or API version conflict); just pass JSON reply to the application */
+      break;
+    case MHD_HTTP_FORBIDDEN:
+      if (GNUNET_OK != check_forbidden (ph,
+                                       json))
+      {
+       GNUNET_break_op (0);
+       response_code = 0;
+      }
+      break;
+    case MHD_HTTP_UNAUTHORIZED:
+      /* Nothing really to verify, merchant 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;
-  case MHD_HTTP_UNAUTHORIZED:
-    /* Nothing really to verify, merchant 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;
+      break;
+    }    
+    ph->pay_cb (ph->pay_cb_cls,
+               response_code,
+               TALER_JSON_get_error_code (json),
+               json);
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "/pay completed with response code %u\n",
-              (unsigned int) response_code);
-  ph->cb (ph->cb_cls,
-          response_code,
-         TALER_JSON_get_error_code (json),
-          json);
-  TALER_MERCHANT_pay_cancel (ph);
-}
-
-
-/**
- * Pay a merchant.  API for wallets that have the coin's private keys.
- * _NOTE_: this function does NOT calculate each coin amount in order
- * to match the contract total price.  This calculation is to be made
- * by the logic using this library.
- *
- * @param ctx the execution loop context
- * @param merchant_uri base URI of the merchant's backend
- * @param instance which merchant instance will receive this payment
- * @param h_contract_terms hashcode of the proposal being paid
- * @param amount total value of the contract to be paid to the merchant
- * @param max_fee maximum fee covered by the merchant (according to the 
contract)
- * @param merchant_pub the public key of the merchant (used to identify the 
merchant for refund requests)
- * @param merchant_sig signature from the merchant over the original contract
- * @param timestamp timestamp when the contract was finalized, must match 
approximately the current time of the merchant
- * @param refund_deadline date until which the merchant can issue a refund to 
the customer via the merchant (can be zero if refunds are not allowed)
- * @param pay_deadline maximum time limit to pay for this contract
- * @param h_wire hash of the merchant’s account details
- * @param exchange_uri URI of the exchange that the coins belong to
- * @param order_id order id of the proposal being paid
- * @param num_coins number of coins used to pay
- * @param coins array of coins we use to pay
- * @param pay_cb the callback to call when a reply for this request is 
available
- * @param pay_cb_cls closure for @a pay_cb
- * @return a handle for this request
- */
-struct TALER_MERCHANT_Pay *
-TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx,
-                          const char *merchant_uri,
-                          const char *instance,
-                           const struct GNUNET_HashCode *h_contract_terms,
-                          const struct TALER_Amount *amount,
-                          const struct TALER_Amount *max_fee,
-                           const struct TALER_MerchantPublicKeyP *merchant_pub,
-                           const struct TALER_MerchantSignatureP *merchant_sig,
-                           struct GNUNET_TIME_Absolute timestamp,
-                           struct GNUNET_TIME_Absolute refund_deadline,
-                           struct GNUNET_TIME_Absolute pay_deadline,
-                           const struct GNUNET_HashCode *h_wire,
-                          const char *exchange_uri,
-                           const char *order_id,
-                           unsigned int num_coins,
-                           const struct TALER_MERCHANT_PayCoin *coins,
-                           TALER_MERCHANT_PayCallback pay_cb,
-                           void *pay_cb_cls)
-{
-  unsigned int i;
-  struct TALER_DepositRequestPS dr;
-  struct TALER_MERCHANT_PaidCoin pc[num_coins];
-
-  (void) GNUNET_TIME_round_abs (&timestamp);
-  (void) GNUNET_TIME_round_abs (&pay_deadline);
-  (void) GNUNET_TIME_round_abs (&refund_deadline);
-
-  if (GNUNET_YES !=
-      TALER_amount_cmp_currency (amount,
-                                 max_fee))
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-
-  dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
-  dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
-  dr.h_contract_terms = *h_contract_terms;
-  dr.h_wire = *h_wire;
-  dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
-  dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
-  dr.merchant = *merchant_pub;
-  for (i=0;i<num_coins;i++)
+  else
   {
-    const struct TALER_MERCHANT_PayCoin *coin = &coins[i];
-    struct TALER_MERCHANT_PaidCoin *p = &pc[i];
-    struct TALER_Amount fee;
-
-    /* prepare 'dr' for this coin to generate coin signature */
-    GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv,
-                                       &dr.coin_pub.eddsa_pub);
-    TALER_amount_hton (&dr.amount_with_fee,
-                      &coin->amount_with_fee);
-    if (GNUNET_SYSERR ==
-       TALER_amount_subtract (&fee,
-                              &coin->amount_with_fee,
-                              &coin->amount_without_fee))
+    GNUNET_assert (0 == strcasecmp (ph->mode,
+                                   "abort-refund"));
+    
+    switch (response_code)
     {
-      /* Integer underflow, fee larger than total amount?
-        This should not happen (client violated API!) */
+    case 0:
+      break;
+    case MHD_HTTP_OK:
+      if (GNUNET_OK ==
+         check_abort_refund (ph,
+                             json))
+      {
+       TALER_MERCHANT_pay_cancel (ph);
+       return;
+      }
+      response_code = 0;
+      break;
+    case MHD_HTTP_BAD_REQUEST:
+      /* This should never happen, either us or the merchant is buggy
+        (or API version conflict); just pass JSON reply to the application */
+      break;
+    case MHD_HTTP_FORBIDDEN:
+      break;
+    case MHD_HTTP_UNAUTHORIZED:
+      /* Nothing really to verify, merchant 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);
-      return NULL;
-    }
-    TALER_amount_hton (&dr.deposit_fee,
-                      &fee);
-    {
-      TALER_LOG_DEBUG ("... amount_with_fee was %s\n",
-                       TALER_amount2s (&coin->amount_with_fee));
-      TALER_LOG_DEBUG ("... fee was %s\n",
-                       TALER_amount2s (&fee));
-    }
-
-    GNUNET_CRYPTO_eddsa_sign (&coin->coin_priv.eddsa_priv,
-                             &dr.purpose,
-                             &p->coin_sig.eddsa_signature);
-    p->denom_pub = coin->denom_pub;
-    p->denom_sig = coin->denom_sig;
-    p->denom_value = coin->denom_value;
-    p->coin_pub = dr.coin_pub;
-    p->amount_with_fee = coin->amount_with_fee;
-    p->amount_without_fee = coin->amount_without_fee;
+      response_code = 0;
+      break;
+    }    
+    ph->abort_cb (ph->abort_cb_cls,
+                 response_code,
+                 TALER_JSON_get_error_code (json),
+                 NULL,
+                 NULL,
+                 0,
+                 NULL,
+                 json);
   }
-  return TALER_MERCHANT_pay_frontend (ctx,
-                                     merchant_uri,
-                                      merchant_pub,
-                                      order_id,
-                                     exchange_uri,
-                                     num_coins,
-                                     pc,
-                                     pay_cb,
-                                     pay_cb_cls);
+    
+  TALER_MERCHANT_pay_cancel (ph);
 }
 
 
 /**
- * Pay a merchant.  API for frontends talking to backends. Here,
- * the frontend does not have the coin's private keys, but just
- * the public keys and signatures.  Note the subtle difference
- * in the type of @a coins compared to #TALER_MERCHANT_pay().
+ * Issue /pay request. Generic version for the various
+ * variants of the API.
  *
  * @param ctx the execution loop context
- * @param merchant_uri base URI of the merchant's backend
+ * @param merchant_url base URL of the merchant's backend
  * @param merchant_pub public key of the merchant
- * @param exchange_uri URI of the exchange that the coins belong to
  * @param num_coins number of coins used to pay
  * @param coins array of coins we use to pay
+ * @param mode mode string to use ("pay" or "abort-refund").
  * @param pay_cb the callback to call when a reply for this request is 
available
  * @param pay_cb_cls closure for @a pay_cb
+ * @param abort_cb callback to call for the abort-refund variant
+ * @param abort_cb_cls closure for @a abort_cb
  * @return a handle for this request
  */
-struct TALER_MERCHANT_Pay *
-TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx,
-                            const char *merchant_uri,
-                             const struct TALER_MerchantPublicKeyP 
*merchant_pub,
-                             const char *order_id,
-                            const char *exchange_uri,
-                             unsigned int num_coins,
-                             const struct TALER_MERCHANT_PaidCoin *coins,
-                             TALER_MERCHANT_PayCallback pay_cb,
-                             void *pay_cb_cls)
+static struct TALER_MERCHANT_Pay *
+request_pay_generic (struct GNUNET_CURL_Context *ctx,
+                    const char *merchant_url,
+                    const struct TALER_MerchantPublicKeyP *merchant_pub,
+                    const char *order_id,
+                    unsigned int num_coins,
+                    const struct TALER_MERCHANT_PaidCoin *coins,
+                    const char *mode,
+                    TALER_MERCHANT_PayCallback pay_cb,
+                    void *pay_cb_cls,
+                    TALER_MERCHANT_PayRefundCallback abort_cb,
+                    void *abort_cb_cls)
 {
   struct TALER_MERCHANT_Pay *ph;
   json_t *pay_obj;
@@ -449,11 +521,12 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context 
*ctx,
     }
 
     /* create JSON for this coin */
-    j_coin = json_pack ("{s:o, s:o," /* f/coin_pub */
-                       " s:o, s:o," /* denom_pub / ub_sig */
-                       " s:o}",     /* coin_sig */
-                       "f", TALER_JSON_from_amount (&pc->amount_with_fee),
+    j_coin = json_pack ("{s:o, s:o," /* contribution/coin_pub */
+                       " s:s, s:o," /* exchange_url / denom_pub */
+                       " s:o, s:o}", /* ub_sig / coin_sig */
+                       "contribution", TALER_JSON_from_amount 
(&pc->amount_with_fee),
                        "coin_pub", GNUNET_JSON_from_data_auto (&pc->coin_pub),
+                       "exchange_url", pc->exchange_url,
                        "denom_pub", GNUNET_JSON_from_rsa_public_key 
(pc->denom_pub.rsa_public_key),
                        "ub_sig", GNUNET_JSON_from_rsa_signature 
(pc->denom_sig.rsa_signature),
                        "coin_sig", GNUNET_JSON_from_data_auto (&pc->coin_sig)
@@ -469,12 +542,12 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context 
*ctx,
   }
 
   pay_obj = json_pack ("{"
-                       " s:s," /* exchange */
+                      " s:s," /* mode */
                        " s:o," /* coins */
                        " s:s," /* order_id */
                        " s:o," /* merchant_pub */
                        "}",
-                      "exchange", exchange_uri,
+                      "mode", mode,
                       "coins", j_coins,
                        "order_id", order_id,
                        "merchant_pub", GNUNET_JSON_from_data_auto 
(merchant_pub));
@@ -485,10 +558,12 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context 
*ctx,
   }
   ph = GNUNET_new (struct TALER_MERCHANT_Pay);
   ph->ctx = ctx;
-  ph->cb = pay_cb;
-  ph->cb_cls = pay_cb_cls;
-  ph->url = MAH_path_to_url_ (merchant_uri,
-                             "/pay");
+  ph->mode = mode;
+  ph->abort_cb = abort_cb;
+  ph->abort_cb_cls = abort_cb_cls;
+  ph->pay_cb = pay_cb;
+  ph->pay_cb_cls = pay_cb_cls;
+  ph->url = TALER_url_join (merchant_url, "/public/pay", NULL);
   ph->num_coins = num_coins;
   ph->coins = GNUNET_new_array (num_coins,
                                 struct TALER_MERCHANT_PaidCoin);
@@ -523,6 +598,307 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context 
*ctx,
 
 
 /**
+ * Pay a merchant.  API for wallets that have the coin's private keys.
+ * _NOTE_: this function does NOT calculate each coin amount in order
+ * to match the contract total price.  This calculation is to be made
+ * by the logic using this library.
+ *
+ * @param ctx the execution loop context
+ * @param merchant_url base URL of the merchant's backend
+ * @param instance which merchant instance will receive this payment
+ * @param h_contract_terms hashcode of the proposal being paid
+ * @param amount total value of the contract to be paid to the merchant
+ * @param max_fee maximum fee covered by the merchant (according to the 
contract)
+ * @param merchant_pub the public key of the merchant (used to identify the 
merchant for refund requests)
+ * @param merchant_sig signature from the merchant over the original contract
+ * @param timestamp timestamp when the contract was finalized, must match 
approximately the current time of the merchant
+ * @param refund_deadline date until which the merchant can issue a refund to 
the customer via the merchant (can be zero if refunds are not allowed)
+ * @param pay_deadline maximum time limit to pay for this contract
+ * @param h_wire hash of the merchant’s account details
+ * @param order_id order id of the proposal being paid
+ * @param num_coins number of coins used to pay
+ * @param coins array of coins we use to pay
+ * @param pay_cb the callback to call when a reply for this request is 
available
+ * @param pay_cb_cls closure for @a pay_cb
+ * @return a handle for this request
+ */
+static struct TALER_MERCHANT_Pay *
+prepare_pay_generic (struct GNUNET_CURL_Context *ctx,
+                    const char *merchant_url,
+                    const char *instance,
+                    const struct GNUNET_HashCode *h_contract_terms,
+                    const struct TALER_Amount *amount,
+                    const struct TALER_Amount *max_fee,
+                    const struct TALER_MerchantPublicKeyP *merchant_pub,
+                    const struct TALER_MerchantSignatureP *merchant_sig,
+                    struct GNUNET_TIME_Absolute timestamp,
+                    struct GNUNET_TIME_Absolute refund_deadline,
+                    struct GNUNET_TIME_Absolute pay_deadline,
+                    const struct GNUNET_HashCode *h_wire,
+                    const char *order_id,
+                    unsigned int num_coins,
+                    const struct TALER_MERCHANT_PayCoin *coins,
+                    const char *mode,
+                    TALER_MERCHANT_PayCallback pay_cb,
+                    void *pay_cb_cls,
+                    TALER_MERCHANT_PayRefundCallback abort_cb,
+                    void *abort_cb_cls)
+{
+  struct TALER_DepositRequestPS dr;
+  struct TALER_MERCHANT_PaidCoin pc[num_coins];
+
+  (void) GNUNET_TIME_round_abs (&timestamp);
+  (void) GNUNET_TIME_round_abs (&pay_deadline);
+  (void) GNUNET_TIME_round_abs (&refund_deadline);
+
+  if (GNUNET_YES !=
+      TALER_amount_cmp_currency (amount,
+                                 max_fee))
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  
+  dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
+  dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
+  dr.h_contract_terms = *h_contract_terms;
+  dr.h_wire = *h_wire;
+  dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
+  dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
+  dr.merchant = *merchant_pub;
+  for (unsigned int i=0;i<num_coins;i++)
+  {
+    const struct TALER_MERCHANT_PayCoin *coin = &coins[i];
+    struct TALER_MERCHANT_PaidCoin *p = &pc[i];
+    struct TALER_Amount fee;
+
+    /* prepare 'dr' for this coin to generate coin signature */
+    GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv,
+                                       &dr.coin_pub.eddsa_pub);
+    TALER_amount_hton (&dr.amount_with_fee,
+                      &coin->amount_with_fee);
+    if (GNUNET_SYSERR ==
+       TALER_amount_subtract (&fee,
+                              &coin->amount_with_fee,
+                              &coin->amount_without_fee))
+    {
+      /* Integer underflow, fee larger than total amount?
+        This should not happen (client violated API!) */
+      GNUNET_break (0);
+      return NULL;
+    }
+    TALER_amount_hton (&dr.deposit_fee,
+                      &fee);
+    {
+      TALER_LOG_DEBUG ("... amount_with_fee was %s\n",
+                       TALER_amount2s (&coin->amount_with_fee));
+      TALER_LOG_DEBUG ("... fee was %s\n",
+                       TALER_amount2s (&fee));
+    }
+
+    GNUNET_CRYPTO_eddsa_sign (&coin->coin_priv.eddsa_priv,
+                             &dr.purpose,
+                             &p->coin_sig.eddsa_signature);
+    p->denom_pub = coin->denom_pub;
+    p->denom_sig = coin->denom_sig;
+    p->denom_value = coin->denom_value;
+    p->coin_pub = dr.coin_pub;
+    p->amount_with_fee = coin->amount_with_fee;
+    p->amount_without_fee = coin->amount_without_fee;
+    p->refund_fee = coin->refund_fee;
+    p->exchange_url = coin->exchange_url;
+  }
+  return request_pay_generic (ctx,
+                             merchant_url,
+                             merchant_pub,
+                             order_id,
+                             num_coins,
+                             pc,
+                             mode,
+                             pay_cb,
+                             pay_cb_cls,
+                             abort_cb,
+                             abort_cb_cls);
+}
+
+
+/**
+ * Pay a merchant.  API for wallets that have the coin's private keys.
+ * _NOTE_: this function does NOT calculate each coin amount in order
+ * to match the contract total price.  This calculation is to be made
+ * by the logic using this library.
+ *
+ * @param ctx the execution loop context
+ * @param merchant_url base URL of the merchant's backend
+ * @param instance which merchant instance will receive this payment
+ * @param h_contract_terms hashcode of the proposal being paid
+ * @param amount total value of the contract to be paid to the merchant
+ * @param max_fee maximum fee covered by the merchant (according to the 
contract)
+ * @param merchant_pub the public key of the merchant (used to identify the 
merchant for refund requests)
+ * @param merchant_sig signature from the merchant over the original contract
+ * @param timestamp timestamp when the contract was finalized, must match 
approximately the current time of the merchant
+ * @param refund_deadline date until which the merchant can issue a refund to 
the customer via the merchant (can be zero if refunds are not allowed)
+ * @param pay_deadline maximum time limit to pay for this contract
+ * @param h_wire hash of the merchant’s account details
+ * @param order_id order id of the proposal being paid
+ * @param num_coins number of coins used to pay
+ * @param coins array of coins we use to pay
+ * @param pay_cb the callback to call when a reply for this request is 
available
+ * @param pay_cb_cls closure for @a pay_cb
+ * @return a handle for this request
+ */
+struct TALER_MERCHANT_Pay *
+TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx,
+                          const char *merchant_url,
+                          const char *instance,
+                           const struct GNUNET_HashCode *h_contract_terms,
+                          const struct TALER_Amount *amount,
+                          const struct TALER_Amount *max_fee,
+                           const struct TALER_MerchantPublicKeyP *merchant_pub,
+                           const struct TALER_MerchantSignatureP *merchant_sig,
+                           struct GNUNET_TIME_Absolute timestamp,
+                           struct GNUNET_TIME_Absolute refund_deadline,
+                           struct GNUNET_TIME_Absolute pay_deadline,
+                           const struct GNUNET_HashCode *h_wire,
+                           const char *order_id,
+                           unsigned int num_coins,
+                           const struct TALER_MERCHANT_PayCoin *coins,
+                           TALER_MERCHANT_PayCallback pay_cb,
+                           void *pay_cb_cls)
+{
+  return prepare_pay_generic (ctx,
+                             merchant_url,
+                             instance,
+                             h_contract_terms,
+                             amount,
+                             max_fee,
+                             merchant_pub,
+                             merchant_sig,
+                             timestamp,
+                             refund_deadline,
+                             pay_deadline,
+                             h_wire,
+                             order_id,
+                             num_coins,
+                             coins,
+                             "pay",
+                             pay_cb,
+                             pay_cb_cls,
+                             NULL,
+                             NULL);
+}
+
+
+/**
+ * Run a payment abort operation, asking for refunds for coins
+ * that were previously spend on a /pay that failed to go through.
+ *
+ * @param ctx execution context
+ * @param merchant_url base URL of the merchant
+ * @param instance which merchant instance will receive this payment
+ * @param h_wire hash of the merchant’s account details
+ * @param h_contract hash of the contact of the merchant with the customer
+ * @param transaction_id transaction id for the transaction between merchant 
and customer
+ * @param amount total value of the contract to be paid to the merchant
+ * @param max_fee maximum fee covered by the merchant (according to the 
contract)
+ * @param merchant_pub the public key of the merchant (used to identify the 
merchant for refund requests)
+ * @param merchant_sig signature from the merchant over the original contract
+ * @param timestamp timestamp when the contract was finalized, must match 
approximately the current time of the merchant
+ * @param refund_deadline date until which the merchant can issue a refund to 
the customer via the merchant (can be zero if refunds are not allowed)
+ * @param pay_deadline maximum time limit to pay for this contract
+ * @param num_coins number of coins used to pay
+ * @param coins array of coins we use to pay
+ * @param coin_sig the signature made with purpose 
#TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s 
private key.
+ * @param payref_cb the callback to call when a reply for this request is 
available
+ * @param payref_cb_cls closure for @a pay_cb
+ * @return a handle for this request
+ */
+struct TALER_MERCHANT_Pay *
+TALER_MERCHANT_pay_abort (struct GNUNET_CURL_Context *ctx,
+                         const char *merchant_url,
+                         const char *instance,
+                         const struct GNUNET_HashCode *h_contract,
+                         const struct TALER_Amount *amount,
+                         const struct TALER_Amount *max_fee,
+                         const struct TALER_MerchantPublicKeyP *merchant_pub,
+                         const struct TALER_MerchantSignatureP *merchant_sig,
+                         struct GNUNET_TIME_Absolute timestamp,
+                         struct GNUNET_TIME_Absolute refund_deadline,
+                         struct GNUNET_TIME_Absolute pay_deadline,
+                         const struct GNUNET_HashCode *h_wire,
+                         const char *order_id,
+                         unsigned int num_coins,
+                         const struct TALER_MERCHANT_PayCoin *coins,
+                         TALER_MERCHANT_PayRefundCallback payref_cb,
+                         void *payref_cb_cls)
+{
+  struct TALER_MERCHANT_Pay *ph;
+
+  ph = prepare_pay_generic (ctx,
+                           merchant_url,
+                           instance,
+                           h_contract,
+                           amount,
+                           max_fee,
+                           merchant_pub,
+                           merchant_sig,
+                           timestamp,
+                           refund_deadline,
+                           pay_deadline,
+                           h_wire,
+                           order_id,
+                           num_coins,
+                           coins,
+                           "abort-refund",
+                           NULL,
+                           NULL,
+                           payref_cb,
+                           payref_cb_cls);
+  ph->h_contract_terms = *h_contract;
+  return ph;
+}
+
+
+/**
+ * PAY a merchant.  API for frontends talking to backends. Here,
+ * the frontend does not have the coin's private keys, but just
+ * the public keys and signatures.  Note the subtle difference
+ * in the type of @a coins compared to #TALER_MERCHANT_pay().
+ *
+ * @param ctx the execution loop context
+ * @param merchant_url base URL of the merchant's backend
+ * @param merchant_pub public key of the merchant
+ * @param num_coins number of coins used to pay
+ * @param coins array of coins we use to pay
+ * @param pay_cb the callback to call when a reply for this request is 
available
+ * @param pay_cb_cls closure for @a pay_cb
+ * @return a handle for this request
+ */
+struct TALER_MERCHANT_Pay *
+TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx,
+                            const char *merchant_url,
+                             const struct TALER_MerchantPublicKeyP 
*merchant_pub,
+                             const char *order_id,
+                             unsigned int num_coins,
+                             const struct TALER_MERCHANT_PaidCoin *coins,
+                             TALER_MERCHANT_PayCallback pay_cb,
+                             void *pay_cb_cls)
+{
+  return request_pay_generic (ctx,
+                             merchant_url,
+                             merchant_pub,
+                             order_id,
+                             num_coins,
+                             coins,
+                             "pay",
+                             pay_cb,
+                             pay_cb_cls,
+                             NULL,
+                             NULL);
+}
+
+
+/**
  * Cancel a pay permission request.  This function cannot be used
  * on a request handle if a response is already served for it.
  *
diff --git a/src/lib/merchant_api_pay.c b/src/lib/merchant_api_pay_abort.c
similarity index 87%
copy from src/lib/merchant_api_pay.c
copy to src/lib/merchant_api_pay_abort.c
index a480061..eb7d219 100644
--- a/src/lib/merchant_api_pay.c
+++ b/src/lib/merchant_api_pay_abort.c
@@ -254,7 +254,7 @@ handle_pay_finished (void *cls,
  * by the logic using this library.
  *
  * @param ctx the execution loop context
- * @param merchant_uri base URI of the merchant's backend
+ * @param merchant_url base URL of the merchant's backend
  * @param instance which merchant instance will receive this payment
  * @param h_contract_terms hashcode of the proposal being paid
  * @param amount total value of the contract to be paid to the merchant
@@ -265,7 +265,6 @@ handle_pay_finished (void *cls,
  * @param refund_deadline date until which the merchant can issue a refund to 
the customer via the merchant (can be zero if refunds are not allowed)
  * @param pay_deadline maximum time limit to pay for this contract
  * @param h_wire hash of the merchant’s account details
- * @param exchange_uri URI of the exchange that the coins belong to
  * @param order_id order id of the proposal being paid
  * @param num_coins number of coins used to pay
  * @param coins array of coins we use to pay
@@ -275,7 +274,7 @@ handle_pay_finished (void *cls,
  */
 struct TALER_MERCHANT_Pay *
 TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx,
-                          const char *merchant_uri,
+                          const char *merchant_url,
                           const char *instance,
                            const struct GNUNET_HashCode *h_contract_terms,
                           const struct TALER_Amount *amount,
@@ -286,16 +285,20 @@ TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context 
*ctx,
                            struct GNUNET_TIME_Absolute refund_deadline,
                            struct GNUNET_TIME_Absolute pay_deadline,
                            const struct GNUNET_HashCode *h_wire,
-                          const char *exchange_uri,
                            const char *order_id,
                            unsigned int num_coins,
                            const struct TALER_MERCHANT_PayCoin *coins,
                            TALER_MERCHANT_PayCallback pay_cb,
                            void *pay_cb_cls)
 {
-  unsigned int i;
   struct TALER_DepositRequestPS dr;
   struct TALER_MERCHANT_PaidCoin pc[num_coins];
+  struct TALER_MERCHANT_Pay *ph;
+  json_t *pay_obj;
+  json_t *j_coins;
+  CURL *eh;
+  struct TALER_Amount total_fee;
+  struct TALER_Amount total_amount;
 
   (void) GNUNET_TIME_round_abs (&timestamp);
   (void) GNUNET_TIME_round_abs (&pay_deadline);
@@ -316,7 +319,7 @@ TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx,
   dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
   dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
   dr.merchant = *merchant_pub;
-  for (i=0;i<num_coins;i++)
+  for (unsigned int i=0;i<num_coins;i++)
   {
     const struct TALER_MERCHANT_PayCoin *coin = &coins[i];
     struct TALER_MERCHANT_PaidCoin *p = &pc[i];
@@ -355,58 +358,9 @@ TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx,
     p->coin_pub = dr.coin_pub;
     p->amount_with_fee = coin->amount_with_fee;
     p->amount_without_fee = coin->amount_without_fee;
+    p->exchange_url = coin->exchange_url;
   }
-  return TALER_MERCHANT_pay_frontend (ctx,
-                                     merchant_uri,
-                                      merchant_pub,
-                                      order_id,
-                                     exchange_uri,
-                                     num_coins,
-                                     pc,
-                                     pay_cb,
-                                     pay_cb_cls);
-}
 
-
-/**
- * Pay a merchant.  API for frontends talking to backends. Here,
- * the frontend does not have the coin's private keys, but just
- * the public keys and signatures.  Note the subtle difference
- * in the type of @a coins compared to #TALER_MERCHANT_pay().
- *
- * @param ctx the execution loop context
- * @param merchant_uri base URI of the merchant's backend
- * @param merchant_pub public key of the merchant
- * @param exchange_uri URI of the exchange that the coins belong to
- * @param num_coins number of coins used to pay
- * @param coins array of coins we use to pay
- * @param pay_cb the callback to call when a reply for this request is 
available
- * @param pay_cb_cls closure for @a pay_cb
- * @return a handle for this request
- */
-struct TALER_MERCHANT_Pay *
-TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx,
-                            const char *merchant_uri,
-                             const struct TALER_MerchantPublicKeyP 
*merchant_pub,
-                             const char *order_id,
-                            const char *exchange_uri,
-                             unsigned int num_coins,
-                             const struct TALER_MERCHANT_PaidCoin *coins,
-                             TALER_MERCHANT_PayCallback pay_cb,
-                             void *pay_cb_cls)
-{
-  struct TALER_MERCHANT_Pay *ph;
-  json_t *pay_obj;
-  json_t *j_coins;
-  CURL *eh;
-  struct TALER_Amount total_fee;
-  struct TALER_Amount total_amount;
-
-  if (0 == num_coins)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
   j_coins = json_array ();
   for (unsigned int i=0;i<num_coins;i++)
   {
@@ -449,11 +403,12 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context 
*ctx,
     }
 
     /* create JSON for this coin */
-    j_coin = json_pack ("{s:o, s:o," /* f/coin_pub */
-                       " s:o, s:o," /* denom_pub / ub_sig */
-                       " s:o}",     /* coin_sig */
-                       "f", TALER_JSON_from_amount (&pc->amount_with_fee),
+    j_coin = json_pack ("{s:o, s:o," /* contribution/coin_pub */
+                       " s:s, s:o," /* exchange_url / denom_pub */
+                       " s:o, s:o}", /* ub_sig / coin_sig */
+                       "contribution", TALER_JSON_from_amount 
(&pc->amount_with_fee),
                        "coin_pub", GNUNET_JSON_from_data_auto (&pc->coin_pub),
+                       "exchange_url", pc->exchange_url,
                        "denom_pub", GNUNET_JSON_from_rsa_public_key 
(pc->denom_pub.rsa_public_key),
                        "ub_sig", GNUNET_JSON_from_rsa_signature 
(pc->denom_sig.rsa_signature),
                        "coin_sig", GNUNET_JSON_from_data_auto (&pc->coin_sig)
@@ -469,12 +424,10 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context 
*ctx,
   }
 
   pay_obj = json_pack ("{"
-                       " s:s," /* exchange */
                        " s:o," /* coins */
                        " s:s," /* order_id */
                        " s:o," /* merchant_pub */
                        "}",
-                      "exchange", exchange_uri,
                       "coins", j_coins,
                        "order_id", order_id,
                        "merchant_pub", GNUNET_JSON_from_data_auto 
(merchant_pub));
@@ -487,8 +440,7 @@ TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context 
*ctx,
   ph->ctx = ctx;
   ph->cb = pay_cb;
   ph->cb_cls = pay_cb_cls;
-  ph->url = MAH_path_to_url_ (merchant_uri,
-                             "/pay");
+  ph->url = TALER_url_join (merchant_url, "/pay", NULL);
   ph->num_coins = num_coins;
   ph->coins = GNUNET_new_array (num_coins,
                                 struct TALER_MERCHANT_PaidCoin);
diff --git a/src/lib/merchant_api_proposal.c b/src/lib/merchant_api_proposal.c
index fa7e6b6..8f4f67f 100644
--- a/src/lib/merchant_api_proposal.c
+++ b/src/lib/merchant_api_proposal.c
@@ -29,7 +29,6 @@
 #include "taler_merchant_service.h"
 #include <taler/taler_json_lib.h>
 #include <taler/taler_signatures.h>
-#include "merchant_api_common.h"
 
 
 /**
@@ -75,7 +74,7 @@ struct TALER_MERCHANT_ProposalOperation
 struct TALER_MERCHANT_ProposalLookupOperation
 {
   /**
-   * Full URI, includes "/proposal".
+   * Full URL, includes "/proposal".
    */
   char *url;
 
@@ -99,6 +98,16 @@ struct TALER_MERCHANT_ProposalLookupOperation
    */
   struct GNUNET_CURL_Context *ctx;
 
+  /**
+   * Should we send the lookup operation with a nonce?
+   */
+  int has_nonce;
+
+  /**
+   * Nonce, only initialized if has_nonce is GNUNET_YES.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey nonce;
+
 };
 
 
@@ -116,29 +125,20 @@ handle_proposal_finished (void *cls,
                           const json_t *json)
 {
   struct TALER_MERCHANT_ProposalOperation *po = cls;
-  json_t *contract_terms;
-  const struct TALER_MerchantSignatureP *sigp;
-  const struct GNUNET_HashCode *hashp;
-  struct TALER_MerchantSignatureP sig;
-  struct GNUNET_HashCode hash;
+  const char *order_id;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_string ("order_id",
+                             &order_id),
+    GNUNET_JSON_spec_end()
+  };
 
   po->job = NULL;
-  contract_terms = NULL;
-  sigp = NULL;
-  hashp = NULL;
   switch (response_code)
   {
   case 0:
     break;
   case MHD_HTTP_OK:
     {
-      struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_json ("data", &contract_terms),
-        GNUNET_JSON_spec_fixed_auto ("sig", &sig),
-        GNUNET_JSON_spec_fixed_auto ("hash", &hash),
-        GNUNET_JSON_spec_end()
-      };
-
       if (GNUNET_OK !=
           GNUNET_JSON_parse (json,
                              spec,
@@ -148,8 +148,6 @@ handle_proposal_finished (void *cls,
         response_code = 0;
         break;
       }
-      hashp = &hash;
-      sigp = &sig;
     }
     break;
   case MHD_HTTP_BAD_REQUEST:
@@ -183,11 +181,8 @@ handle_proposal_finished (void *cls,
           response_code,
          TALER_JSON_get_error_code (json),
           json,
-          contract_terms,
-          sigp,
-          hashp);
-  if (NULL != contract_terms)
-    json_decref (contract_terms);
+          order_id);
+  GNUNET_JSON_parse_free (spec);
   TALER_MERCHANT_proposal_cancel (po);
 }
 
@@ -196,7 +191,7 @@ handle_proposal_finished (void *cls,
  * POST an order to the backend and receives the related proposal.
  *
  * @param ctx execution context
- * @param backend_uri URI of the backend
+ * @param backend_url URL of the backend
  * @param order basic information about this purchase, to be extended by the
  * backend
  * @param proposal_cb the callback to call when a reply for this request is
@@ -206,7 +201,7 @@ handle_proposal_finished (void *cls,
  */
 struct TALER_MERCHANT_ProposalOperation *
 TALER_MERCHANT_order_put (struct GNUNET_CURL_Context *ctx,
-                          const char *backend_uri,
+                          const char *backend_url,
                           const json_t *order,
                           TALER_MERCHANT_ProposalCallback proposal_cb,
                           void *proposal_cb_cls)
@@ -219,8 +214,7 @@ TALER_MERCHANT_order_put (struct GNUNET_CURL_Context *ctx,
   po->ctx = ctx;
   po->cb = proposal_cb;
   po->cb_cls = proposal_cb_cls;
-  po->url = MAH_path_to_url_ (backend_uri,
-                             "/proposal");
+  po->url = TALER_url_join (backend_url, "/order", NULL);
   req = json_pack ("{s:O}",
                    "order", (json_t *) order);
   eh = curl_easy_init ();
@@ -267,6 +261,70 @@ handle_proposal_lookup_finished (void *cls,
                                  const json_t *json)
 {
   struct TALER_MERCHANT_ProposalLookupOperation *plo = cls;
+  json_t *contract_terms;
+  struct TALER_MerchantSignatureP sig;
+  struct GNUNET_HashCode hash;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_json ("contract_terms",
+                           &contract_terms),
+    GNUNET_JSON_spec_fixed_auto ("sig",
+                                 &sig),
+    GNUNET_JSON_spec_end()
+  };
+
+  if (MHD_HTTP_OK != response_code)
+  {
+    char *s;
+
+    s = json_dumps (json,
+                    JSON_COMPACT);
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Proposal lookup failed with HTTP status code %u on input 
`%s'\n",
+                (unsigned int) response_code,
+                s);
+    if (NULL != s)
+      free (s);
+    GNUNET_break_op (0);
+    plo->cb (plo->cb_cls,
+             response_code,
+             json,
+             NULL,
+             NULL,
+             NULL);
+    return;
+  }
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "proposal lookup failed to parse JSON\n");
+    GNUNET_break_op (0);
+    plo->cb (plo->cb_cls,
+             0,
+             json,
+             NULL,
+             NULL,
+             NULL);
+    return;
+  }
+
+  if (GNUNET_OK !=
+      TALER_JSON_hash (contract_terms,
+                       &hash))
+  {
+    GNUNET_break (0);
+    GNUNET_JSON_parse_free (spec);
+    plo->cb (plo->cb_cls,
+             0,
+             json,
+             NULL,
+             NULL,
+             NULL);
+    return;
+  }
 
   plo->job = NULL;
   /**
@@ -275,7 +333,11 @@ handle_proposal_lookup_finished (void *cls,
    */
   plo->cb (plo->cb_cls,
            response_code,
-           json);
+           json,
+           contract_terms,
+           &sig,
+           &hash);
+  GNUNET_JSON_parse_free (spec);
   TALER_MERCHANT_proposal_lookup_cancel (plo);
 }
 
@@ -285,17 +347,19 @@ handle_proposal_lookup_finished (void *cls,
  * retrieve a proposal data by providing its transaction id.
  *
  * @param ctx execution context
- * @param backend_uri base URL of the merchant backend
- * @param transaction_id transaction id used to perform the lookup
+ * @param backend_url base URL of the merchant backend
+ * @param order_id order id used to perform the lookup
+ * @param nonce nonce used to perform the lookup
  * @param plo_cb callback which will work the response gotten from the backend
  * @param plo_cb_cls closure to pass to @a history_cb
  * @return handle for this operation, NULL upon errors
  */
 struct TALER_MERCHANT_ProposalLookupOperation *
 TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context *ctx,
-                                const char *backend_uri,
+                                const char *backend_url,
                                 const char *order_id,
                                 const char *instance,
+                                const struct GNUNET_CRYPTO_EddsaPublicKey 
*nonce,
                                 TALER_MERCHANT_ProposalLookupOperationCallback 
plo_cb,
                                 void *plo_cb_cls)
 {
@@ -307,13 +371,30 @@ TALER_MERCHANT_proposal_lookup (struct 
GNUNET_CURL_Context *ctx,
   plo->ctx = ctx;
   plo->cb = plo_cb;
   plo->cb_cls = plo_cb_cls;
-  base = MAH_path_to_url_ (backend_uri,
-                          "/proposal");
-  GNUNET_asprintf (&plo->url,
-                   "%s?order_id=%s&instance=%s",
-                   base,
-                   order_id,
-                   instance);
+  base = TALER_url_join (backend_url, "/public/proposal", NULL);
+  if (NULL != nonce)
+  {
+    char *nonce_str;
+    plo->has_nonce = GNUNET_YES;
+    plo->nonce = *nonce;
+    nonce_str = GNUNET_STRINGS_data_to_string_alloc (nonce, sizeof (struct 
GNUNET_CRYPTO_EddsaPublicKey));
+    GNUNET_assert (NULL != nonce_str);
+    GNUNET_asprintf (&plo->url,
+                     "%s?order_id=%s&instance=%s&nonce=%s",
+                     base,
+                     order_id,
+                     instance,
+                     nonce_str);
+    GNUNET_free (nonce_str);
+  }
+  else
+  {
+    GNUNET_asprintf (&plo->url,
+                     "%s?order_id=%s&instance=%s",
+                     base,
+                     order_id,
+                     instance);
+  }
   GNUNET_free (base);
   eh = curl_easy_init ();
   if (CURLE_OK != curl_easy_setopt (eh,
@@ -324,6 +405,10 @@ TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context 
*ctx,
     return NULL;
   }
 
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "looking up proposal from %s\n",
+              plo->url);
+
   if (NULL == (plo->job = GNUNET_CURL_job_add (ctx,
                                                eh,
                                                GNUNET_YES,
diff --git a/src/lib/merchant_api_refund.c b/src/lib/merchant_api_refund.c
index 8cea8b7..01fa6bd 100644
--- a/src/lib/merchant_api_refund.c
+++ b/src/lib/merchant_api_refund.c
@@ -30,7 +30,6 @@
 #include "taler_merchant_service.h"
 #include <taler/taler_json_lib.h>
 #include <taler/taler_signatures.h>
-#include "merchant_api_common.h"
 
 
 struct TALER_MERCHANT_RefundLookupOperation
@@ -170,7 +169,7 @@ TALER_MERCHANT_refund_increase_cancel (struct 
TALER_MERCHANT_RefundIncreaseOpera
  * Increase the refund associated to a order
  *
  * @param ctx the CURL context used to connect to the backend
- * @param backend_uri backend's base URL, including final "/"
+ * @param backend_url backend's base URL, including final "/"
  * @param order_id id of the order whose refund is to be increased
  * @param refund amount to which increase the refund
  * @param reason human-readable reason justifying the refund
@@ -180,7 +179,7 @@ TALER_MERCHANT_refund_increase_cancel (struct 
TALER_MERCHANT_RefundIncreaseOpera
  */
 struct TALER_MERCHANT_RefundIncreaseOperation *
 TALER_MERCHANT_refund_increase (struct GNUNET_CURL_Context *ctx,
-                                const char *backend_uri,
+                                const char *backend_url,
                                 const char *order_id,
                                 const struct TALER_Amount *refund,
                                 const char *reason,
@@ -196,8 +195,7 @@ TALER_MERCHANT_refund_increase (struct GNUNET_CURL_Context 
*ctx,
   rio->ctx = ctx;
   rio->cb = cb;
   rio->cb_cls = cb_cls;
-  rio->url = MAH_path_to_url_ (backend_uri,
-                              "/refund");
+  rio->url = TALER_url_join (backend_url, "/refund", NULL);
   req = json_pack ("{s:o, s:s, s:s, s:s}",
                    "refund", TALER_JSON_from_amount (refund),
                    "order_id", order_id,
@@ -297,11 +295,12 @@ handle_refund_lookup_finished (void *cls,
   }
 }
 
+
 /**
  * Does a GET /refund.
  *
  * @param ctx execution context
- * @param backend_uri base URL of the merchant backend
+ * @param backend_url base URL of the merchant backend
  * @param order_id order id used to perform the lookup
  * @param cb callback which will work the response gotten from the backend
  * @param cb_cls closure to pass to the callback
@@ -309,7 +308,7 @@ handle_refund_lookup_finished (void *cls,
  */
 struct TALER_MERCHANT_RefundLookupOperation *
 TALER_MERCHANT_refund_lookup (struct GNUNET_CURL_Context *ctx,
-                              const char *backend_uri,
+                              const char *backend_url,
                               const char *order_id,
                               const char *instance,
                               TALER_MERCHANT_RefundLookupCallback cb,
@@ -324,8 +323,8 @@ TALER_MERCHANT_refund_lookup (struct GNUNET_CURL_Context 
*ctx,
   rlo->cb_cls = cb_cls;
 
   GNUNET_asprintf (&rlo->url,
-                   "%s/refund?instance=%s&order_id=%s",
-                   backend_uri,
+                   "%s/public/refund?instance=%s&order_id=%s",
+                   backend_url,
                    instance,
                    order_id);
   eh = curl_easy_init ();
diff --git a/src/lib/merchant_api_tip_authorize.c 
b/src/lib/merchant_api_tip_authorize.c
index 3c7899d..f27d06f 100644
--- a/src/lib/merchant_api_tip_authorize.c
+++ b/src/lib/merchant_api_tip_authorize.c
@@ -29,7 +29,6 @@
 #include "taler_merchant_service.h"
 #include <taler/taler_json_lib.h>
 #include <taler/taler_signatures.h>
-#include "merchant_api_common.h"
 
 
 /**
@@ -85,11 +84,11 @@ check_ok (struct TALER_MERCHANT_TipAuthorizeOperation *tao,
 {
   struct GNUNET_HashCode tip_id;
   struct GNUNET_TIME_Absolute tip_expiration;
-  const char *exchange_uri;
+  const char *exchange_url;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_absolute_time ("expiration", &tip_expiration),
     GNUNET_JSON_spec_fixed_auto ("tip_id", &tip_id),
-    GNUNET_JSON_spec_string ("exchange_url", &exchange_uri),
+    GNUNET_JSON_spec_string ("exchange_url", &exchange_url),
     GNUNET_JSON_spec_end()
   };
 
@@ -106,7 +105,7 @@ check_ok (struct TALER_MERCHANT_TipAuthorizeOperation *tao,
            TALER_JSON_get_error_code (json),
            &tip_id,
            tip_expiration,
-           exchange_uri);
+           exchange_url);
   tao->cb = NULL; /* do not call twice */
   GNUNET_JSON_parse_free (spec);
   return GNUNET_OK;
@@ -174,7 +173,9 @@ handle_tip_authorize_finished (void *cls,
  * that a tip should be created.
  *
  * @param ctx execution context
- * @param backend_uri base URL of the merchant backend
+ * @param backend_url base URL of the merchant backend
+ * @param pickup_url frontend URL for where the tip can be picked up
+ * @param next_url where the browser should proceed after picking up the tip
  * @param amount amount to be handed out as a tip
  * @param instance which backend instance should create the tip (identifies 
the reserve and exchange)
  * @param justification which justification should be stored (human-readable 
reason for the tip)
@@ -184,7 +185,9 @@ handle_tip_authorize_finished (void *cls,
  */
 struct TALER_MERCHANT_TipAuthorizeOperation *
 TALER_MERCHANT_tip_authorize (struct GNUNET_CURL_Context *ctx,
-                              const char *backend_uri,
+                              const char *backend_url,
+                              const char *pickup_url,
+                              const char *next_url,
                               const struct TALER_Amount *amount,
                               const char *instance,
                               const char *justification,
@@ -199,18 +202,19 @@ TALER_MERCHANT_tip_authorize (struct GNUNET_CURL_Context 
*ctx,
   tao->ctx = ctx;
   tao->cb = authorize_cb;
   tao->cb_cls = authorize_cb_cls;
-  tao->url = MAH_path_to_url_ (backend_uri,
-                               "/tip-authorize");
+  tao->url = TALER_url_join (backend_url, "/tip-authorize", NULL);
   te_obj = json_pack ("{"
                       " s:o," /* amount */
                       " s:s," /* instance */
                       " s:s," /* justification */
                       " s:s," /* pickup_url */
+                      " s:s," /* next_url */
                       "}",
                       "amount", TALER_JSON_from_amount (amount),
                       "instance", instance,
                       "justification", justification,
-                      "pickup_url", "https://example.com";);
+                      "pickup_url", pickup_url,
+                      "next_url", next_url);
   if (NULL == te_obj)
   {
     GNUNET_break (0);
@@ -230,7 +234,7 @@ TALER_MERCHANT_tip_authorize (struct GNUNET_CURL_Context 
*ctx,
   }
   json_decref (te_obj);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Requesting URI '%s'\n",
+              "Requesting URL '%s'\n",
               tao->url);
   eh = curl_easy_init ();
   GNUNET_assert (CURLE_OK ==
diff --git a/src/lib/merchant_api_tip_enable.c 
b/src/lib/merchant_api_tip_enable.c
deleted file mode 100644
index eb7dadf..0000000
--- a/src/lib/merchant_api_tip_enable.c
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014-2017 Taler Systems SA
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU Lesser General Public License as published by the Free 
Software
-  Foundation; either version 2.1, 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 Lesser General Public License for more 
details.
-
-  You should have received a copy of the GNU Lesser General Public License 
along with
-  TALER; see the file COPYING.LGPL.  If not, see
-  <http://www.gnu.org/licenses/>
-*/
-/**
- * @file lib/merchant_api_tip_enable.c
- * @brief Implementation of the /tip-enable request of the merchant's HTTP API
- * @author Marcello Stanisci
- * @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_curl_lib.h>
-#include "taler_merchant_service.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include "merchant_api_common.h"
-
-
-/**
- * @brief A handle for tracking transactions.
- */
-struct TALER_MERCHANT_TipEnableOperation
-{
-
-  /**
-   * 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;
-
-  /**
-   * Function to call with the result.
-   */
-  TALER_MERCHANT_TipEnableCallback cb;
-
-  /**
-   * Closure for @a cb.
-   */
-  void *cb_cls;
-
-  /**
-   * Reference to the execution context.
-   */
-  struct GNUNET_CURL_Context *ctx;
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP /track/transaction request.
- *
- * @param cls the `struct TALER_MERCHANT_TipEnableOperation`
- * @param response_code HTTP response code, 0 on error
- * @param json response body, NULL if not in JSON
- */
-static void
-handle_tip_enable_finished (void *cls,
-                            long response_code,
-                            const json_t *json)
-{
-  struct TALER_MERCHANT_TipEnableOperation *teo = cls;
-
-  teo->job = NULL;
-  switch (response_code)
-  {
-  case MHD_HTTP_OK:
-    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;
-  }
-  teo->cb (teo->cb_cls,
-           response_code,
-          TALER_JSON_get_error_code (json));
-  TALER_MERCHANT_tip_enable_cancel (teo);
-}
-
-
-/**
- * Issue a /tip-enable request to the backend.  Informs the backend
- * that a reserve is now available for tipping.  Note that the
- * respective @a reserve_priv must also be bound to one or more
- * instances (together with the URI of the exchange) via the backend's
- * configuration file before it can be used.  Usually, the process
- * is that one first configures an exchange and a @a reserve_priv for
- * an instance, and then enables (or re-enables) the reserve by
- * performing wire transfers and informs the backend about it using
- * this API.
- *
- * @param ctx execution context
- * @param backend_uri base URL of the merchant backend
- * @param amount amount that was credited to the reserve
- * @param expiration when will the reserve expire
- * @param reserve_priv private key of the reserve
- * @param credit_uuid unique ID of the wire transfer
- * @param enable_cb callback which will work the response gotten from the 
backend
- * @param enable_cb_cls closure to pass to @a enable_cb
- * @return handle for this operation, NULL upon errors
- */
-struct TALER_MERCHANT_TipEnableOperation *
-TALER_MERCHANT_tip_enable (struct GNUNET_CURL_Context *ctx,
-                           const char *backend_uri,
-                           const struct TALER_Amount *amount,
-                           struct GNUNET_TIME_Absolute expiration,
-                           const struct TALER_ReservePrivateKeyP *reserve_priv,
-                           const struct GNUNET_HashCode *credit_uuid,
-                           TALER_MERCHANT_TipEnableCallback enable_cb,
-                           void *enable_cb_cls)
-{
-  struct TALER_MERCHANT_TipEnableOperation *teo;
-  CURL *eh;
-  json_t *te_obj;
-
-  (void) GNUNET_TIME_round_abs (&expiration);
-  teo = GNUNET_new (struct TALER_MERCHANT_TipEnableOperation);
-  teo->ctx = ctx;
-  teo->cb = enable_cb;
-  teo->cb_cls = enable_cb_cls;
-  teo->url = MAH_path_to_url_ (backend_uri,
-                               "/tip-enable");
-  te_obj = json_pack ("{"
-                      " s:o," /* amount */
-                      " s:o," /* expiration */
-                      " s:o," /* credit_uuid */
-                      " s:o," /* reserve_priv */
-                      "}",
-                      "credit", TALER_JSON_from_amount (amount),
-                      "expiration", GNUNET_JSON_from_time_abs (expiration),
-                      "credit_uuid", GNUNET_JSON_from_data_auto (credit_uuid),
-                      "reserve_priv", GNUNET_JSON_from_data_auto 
(reserve_priv));
-  if (NULL == te_obj)
-  {
-    GNUNET_break (0);
-    GNUNET_free (teo->url);
-    GNUNET_free (teo);
-    return NULL;
-  }
-  if (NULL == (teo->json_enc =
-               json_dumps (te_obj,
-                           JSON_COMPACT)))
-  {
-    GNUNET_break (0);
-    json_decref (te_obj);
-    GNUNET_free (teo->url);
-    GNUNET_free (teo);
-    return NULL;
-  }
-  json_decref (te_obj);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Requesting URI '%s'\n",
-              teo->url);
-  eh = curl_easy_init ();
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_URL,
-                                   teo->url));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDS,
-                                   teo->json_enc));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDSIZE,
-                                   strlen (teo->json_enc)));
-  teo->job = GNUNET_CURL_job_add (ctx,
-                                  eh,
-                                  GNUNET_YES,
-                                  &handle_tip_enable_finished,
-                                  teo);
-  return teo;
-}
-
-
-/**
- * Cancel a /track/transaction request.  This function cannot be used
- * on a request handle if a response is already served for it.
- *
- * @param teo handle to the tracking operation being cancelled
- */
-void
-TALER_MERCHANT_tip_enable_cancel (struct TALER_MERCHANT_TipEnableOperation 
*teo)
-{
-  if (NULL != teo->job)
-  {
-    GNUNET_CURL_job_cancel (teo->job);
-    teo->job = NULL;
-  }
-  GNUNET_free_non_null (teo->json_enc);
-  GNUNET_free (teo->url);
-  GNUNET_free (teo);
-}
-
-/* end of merchant_api_tip_enable.c */
diff --git a/src/lib/merchant_api_tip_pickup.c 
b/src/lib/merchant_api_tip_pickup.c
index e967588..3bf38e4 100644
--- a/src/lib/merchant_api_tip_pickup.c
+++ b/src/lib/merchant_api_tip_pickup.c
@@ -29,7 +29,6 @@
 #include "taler_merchant_service.h"
 #include <taler/taler_json_lib.h>
 #include <taler/taler_signatures.h>
-#include "merchant_api_common.h"
 
 
 /**
@@ -176,9 +175,12 @@ handle_tip_pickup_finished (void *cls,
     /* Server had an internal issue; we should retry, but this API
        leaves this to the application */
     break;
-  case MHD_HTTP_SERVICE_UNAVAILABLE:
+  case MHD_HTTP_CONFLICT:
     /* legal, can happen if we pickup a tip twice... */
     break;
+  case MHD_HTTP_NOT_FOUND:
+    /* legal, can happen if tip ID is unknown */
+    break;
   default:
     /* unexpected response code */
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -189,6 +191,7 @@ handle_tip_pickup_finished (void *cls,
     break;
   }
   if (NULL != tpo->cb)
+  {
     tpo->cb (tpo->cb_cls,
              response_code,
              TALER_JSON_get_error_code (json),
@@ -196,6 +199,8 @@ handle_tip_pickup_finished (void *cls,
              0,
              NULL,
              json);
+    tpo->cb = NULL;
+  }
   TALER_MERCHANT_tip_pickup_cancel (tpo);
 }
 
@@ -205,7 +210,7 @@ handle_tip_pickup_finished (void *cls,
  * that a customer wants to pick up a tip.
  *
  * @param ctx execution context
- * @param backend_uri base URL of the merchant backend
+ * @param backend_url base URL of the merchant backend
  * @param tip_id unique identifier for the tip
  * @param num_planches number of planchets provided in @a planchets
  * @param planchets array of planchets to be signed into existence for the tip
@@ -215,7 +220,7 @@ handle_tip_pickup_finished (void *cls,
  */
 struct TALER_MERCHANT_TipPickupOperation *
 TALER_MERCHANT_tip_pickup (struct GNUNET_CURL_Context *ctx,
-                           const char *backend_uri,
+                           const char *backend_url,
                            const struct GNUNET_HashCode *tip_id,
                            unsigned int num_planchets,
                            struct TALER_PlanchetDetail *planchets,
@@ -276,8 +281,7 @@ TALER_MERCHANT_tip_pickup (struct GNUNET_CURL_Context *ctx,
   tpo->ctx = ctx;
   tpo->cb = pickup_cb;
   tpo->cb_cls = pickup_cb_cls;
-  tpo->url = MAH_path_to_url_ (backend_uri,
-                               "/tip-pickup");
+  tpo->url = TALER_url_join (backend_url, "/public/tip-pickup", NULL);
   if (NULL == (tpo->json_enc =
                json_dumps (tp_obj,
                            JSON_COMPACT)))
@@ -290,7 +294,7 @@ TALER_MERCHANT_tip_pickup (struct GNUNET_CURL_Context *ctx,
   }
   json_decref (tp_obj);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Requesting URI '%s'\n",
+              "Requesting URL '%s'\n",
               tpo->url);
   eh = curl_easy_init ();
   GNUNET_assert (CURLE_OK ==
diff --git a/src/lib/merchant_api_tip_query.c b/src/lib/merchant_api_tip_query.c
new file mode 100644
index 0000000..b0b2c19
--- /dev/null
+++ b/src/lib/merchant_api_tip_query.c
@@ -0,0 +1,244 @@
+/*
+  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 Lesser General Public License as published by the Free 
Software
+  Foundation; either version 2.1, 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 Lesser General Public License for more 
details.
+
+  You should have received a copy of the GNU Lesser General Public License 
along with
+  TALER; see the file COPYING.LGPL.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file lib/merchant_api_tip_query.c
+ * @brief Implementation of the /tip-query request of the merchant's HTTP API
+ * @author Florian Dold
+ */
+#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_curl_lib.h>
+#include "taler_merchant_service.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * @brief A handle for tracking /tip-pickup operations
+ */
+struct TALER_MERCHANT_TipQueryOperation
+{
+  /**
+   * 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;
+
+  /**
+   * Function to call with the result.
+   */
+  TALER_MERCHANT_TipQueryCallback cb;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Reference to the execution context.
+   */
+  struct GNUNET_CURL_Context *ctx;
+
+  /**
+   * Expected number of planchets.
+   */
+  unsigned int num_planchets;
+};
+
+
+/**
+ * We got a 200 response back from the exchange (or the merchant).
+ * Now we need to parse the response and if it is well-formed,
+ * call the callback (and set it to NULL afterwards).
+ *
+ * @param tqo handle of the original operation
+ * @param json cryptographic proof returned by the exchange/merchant
+ * @return #GNUNET_OK if response is valid
+ */
+static int
+check_ok (struct TALER_MERCHANT_TipQueryOperation *tqo,
+          const json_t *json)
+{
+  struct GNUNET_TIME_Absolute reserve_expiration;
+  struct TALER_Amount amount_authorized;
+  struct TALER_Amount amount_available;
+  struct TALER_Amount amount_picked_up;
+  struct TALER_ReservePublicKeyP reserve_pub;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("reserve_pub", &reserve_pub),
+    GNUNET_JSON_spec_absolute_time ("reserve_expiration", &reserve_expiration),
+    TALER_JSON_spec_amount ("amount_authorized", &amount_authorized),
+    TALER_JSON_spec_amount ("amount_available", &amount_available),
+    TALER_JSON_spec_amount ("amount_picked_up", &amount_picked_up),
+    GNUNET_JSON_spec_end()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  tqo->cb (tqo->cb_cls,
+           MHD_HTTP_OK,
+           TALER_JSON_get_error_code (json),
+           json,
+           reserve_expiration,
+           &reserve_pub,
+           &amount_authorized,
+           &amount_available,
+           &amount_picked_up);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /track/transaction request.
+ *
+ * @param cls the `struct TALER_MERCHANT_TipQueryOperation`
+ * @param response_code HTTP response code, 0 on error
+ * @param json response body, NULL if not in JSON
+ */
+static void
+handle_tip_query_finished (void *cls,
+                            long response_code,
+                            const json_t *json)
+{
+  struct TALER_MERCHANT_TipQueryOperation *tqo = cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Got /tip-query response with status code %u\n",
+              (unsigned int) response_code);
+
+  tqo->job = NULL;
+  switch (response_code)
+  {
+  case MHD_HTTP_OK:
+    if (GNUNET_OK != check_ok (tqo,
+                               json))
+    {
+      GNUNET_break_op (0);
+      response_code = 0;
+    }
+    break;
+  case MHD_HTTP_INTERNAL_SERVER_ERROR:
+    /* Server had an internal issue; we should retry, but this API
+       leaves this to the application */
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* legal, can happen if instance or tip reserve is unknown */
+    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;
+  }
+  if (MHD_HTTP_OK != response_code)
+    tqo->cb (tqo->cb_cls,
+             response_code,
+             TALER_JSON_get_error_code (json),
+             json,
+             GNUNET_TIME_UNIT_ZERO_ABS,
+             NULL,
+             NULL,
+             NULL,
+             NULL);
+  TALER_MERCHANT_tip_query_cancel (tqo);
+}
+
+
+/**
+ * Issue a /tip-query request to the backend.  Informs the backend
+ * that a customer wants to pick up a tip.
+ *
+ * @param ctx execution context
+ * @param backend_url base URL of the merchant backend
+ * @param instance instance to query
+ * @return handle for this operation, NULL upon errors
+ */
+struct TALER_MERCHANT_TipQueryOperation *
+TALER_MERCHANT_tip_query (struct GNUNET_CURL_Context *ctx,
+                          const char *backend_url,
+                          const char *instance,
+                          TALER_MERCHANT_TipQueryCallback query_cb,
+                          void *query_cb_cls)
+{
+  struct TALER_MERCHANT_TipQueryOperation *tqo;
+  CURL *eh;
+
+  tqo = GNUNET_new (struct TALER_MERCHANT_TipQueryOperation);
+  tqo->ctx = ctx;
+  tqo->cb = query_cb;
+  tqo->cb_cls = query_cb_cls;
+  tqo->url = TALER_url_join (backend_url, "/tip-query",
+                             "instance", instance,
+                             NULL);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Requesting URL '%s'\n",
+              tqo->url);
+  eh = curl_easy_init ();
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_URL,
+                                   tqo->url));
+  tqo->job = GNUNET_CURL_job_add (ctx,
+                                  eh,
+                                  GNUNET_YES,
+                                  &handle_tip_query_finished,
+                                  tqo);
+  return tqo;
+}
+
+
+/**
+ * Cancel a /tip-query request.  This function cannot be used
+ * on a request handle if a response is already served for it.
+ *
+ * @param tqo handle to the operation being cancelled
+ */
+void
+TALER_MERCHANT_tip_query_cancel (struct TALER_MERCHANT_TipQueryOperation *tqo)
+{
+  if (NULL != tqo->job)
+  {
+    GNUNET_CURL_job_cancel (tqo->job);
+    tqo->job = NULL;
+  }
+  GNUNET_free (tqo->url);
+  GNUNET_free (tqo);
+}
+
+/* end of merchant_api_tip_query.c */
diff --git a/src/lib/merchant_api_track_transaction.c 
b/src/lib/merchant_api_track_transaction.c
index 5357d37..e38660c 100644
--- a/src/lib/merchant_api_track_transaction.c
+++ b/src/lib/merchant_api_track_transaction.c
@@ -30,7 +30,6 @@
 #include "taler_merchant_service.h"
 #include <taler/taler_json_lib.h>
 #include <taler/taler_signatures.h>
-#include "merchant_api_common.h"
 
 
 /**
@@ -129,7 +128,7 @@ handle_track_transaction_finished (void *cls,
  * Request backend to return transactions associated with a given wtid.
  *
  * @param ctx execution context
- * @param backend_uri base URI of the backend
+ * @param backend_url base URL of the backend
  * @param instance which merchant instance is going to be tracked
  * @param order_id order id pointing to the transaction being tracked
  * @param track_transaction_cb the callback to call when a reply for this 
request is available
@@ -138,7 +137,7 @@ handle_track_transaction_finished (void *cls,
  */
 struct TALER_MERCHANT_TrackTransactionHandle *
 TALER_MERCHANT_track_transaction (struct GNUNET_CURL_Context *ctx,
-                                  const char *backend_uri,
+                                  const char *backend_url,
                                   const char *instance,
                                   const char *order_id,
                                   TALER_MERCHANT_TrackTransactionCallback 
track_transaction_cb,
@@ -152,8 +151,7 @@ TALER_MERCHANT_track_transaction (struct 
GNUNET_CURL_Context *ctx,
   tdo->ctx = ctx;
   tdo->cb = track_transaction_cb;
   tdo->cb_cls = track_transaction_cb_cls;
-  base = MAH_path_to_url_ (backend_uri,
-                          "/track/transaction");
+  base = TALER_url_join (backend_url, "/track/transaction", NULL);
   GNUNET_asprintf (&tdo->url,
                    "%s?order_id=%s&instance=%s",
                    base,
@@ -161,7 +159,7 @@ TALER_MERCHANT_track_transaction (struct 
GNUNET_CURL_Context *ctx,
                    instance);
   GNUNET_free (base);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Requesting URI '%s'\n",
+              "Requesting URL '%s'\n",
               tdo->url);
   eh = curl_easy_init ();
   GNUNET_assert (CURLE_OK ==
diff --git a/src/lib/merchant_api_track_transfer.c 
b/src/lib/merchant_api_track_transfer.c
index 660d4e1..7c78532 100644
--- a/src/lib/merchant_api_track_transfer.c
+++ b/src/lib/merchant_api_track_transfer.c
@@ -30,7 +30,6 @@
 #include "taler_merchant_service.h"
 #include <taler/taler_json_lib.h>
 #include <taler/taler_signatures.h>
-#include "merchant_api_common.h"
 
 
 /**
@@ -214,29 +213,28 @@ handle_track_transfer_finished (void *cls,
  * Request backend to return transfers associated with a given wtid.
  *
  * @param ctx execution context
- * @param backend_uri base URI of the backend
+ * @param backend_url base URL of the backend
  * @param instance which merchant instance is going to be tracked
  * @param wire_method wire method used for the wire transfer
  * @param wtid base32 string indicating a wtid
- * @param exchange_uri base URL of the exchange in charge of returning the 
wanted information
+ * @param exchange_url base URL of the exchange in charge of returning the 
wanted information
  * @param track_transfer_cb the callback to call when a reply for this request 
is available
  * @param track_transfer_cb_cls closure for @a contract_cb
  * @return a handle for this request
  */
 struct TALER_MERCHANT_TrackTransferHandle *
 TALER_MERCHANT_track_transfer (struct GNUNET_CURL_Context *ctx,
-                               const char *backend_uri,
+                               const char *backend_url,
                                const char *instance,
                               const char *wire_method,
                                const struct TALER_WireTransferIdentifierRawP 
*wtid,
-                               const char *exchange_uri,
+                               const char *exchange_url,
                                TALER_MERCHANT_TrackTransferCallback 
track_transfer_cb,
                                void *track_transfer_cb_cls)
 {
   struct TALER_MERCHANT_TrackTransferHandle *tdo;
   CURL *eh;
   char *wtid_str;
-  char *base;
 
   wtid_str = GNUNET_STRINGS_data_to_string_alloc (wtid,
                                                   sizeof (struct 
TALER_WireTransferIdentifierRawP));
@@ -244,17 +242,12 @@ TALER_MERCHANT_track_transfer (struct GNUNET_CURL_Context 
*ctx,
   tdo->ctx = ctx;
   tdo->cb = track_transfer_cb; // very last to be called
   tdo->cb_cls = track_transfer_cb_cls;
-  /* TODO: do we need to escape 'exchange_uri' here? */
-  base = MAH_path_to_url_ (backend_uri,
-                          "/track/transfer");
-  GNUNET_asprintf (&tdo->url,
-                   "%s?wtid=%s&exchange=%s&instance=%s&wire_method=%s",
-                   base,
-                   wtid_str,
-                   exchange_uri,
-                  instance,
-                  wire_method);
-  GNUNET_free (base);
+  tdo->url = TALER_url_join (backend_url, "/track/transfer",
+                             "wtid", wtid_str,
+                             "exchange", exchange_url,
+                             "instance", instance,
+                             "wire_method", wire_method,
+                             NULL);
   GNUNET_free (wtid_str);
   eh = curl_easy_init ();
   GNUNET_assert (CURLE_OK ==
diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c
index 9bbcb00..c8ae9f8 100644
--- a/src/lib/test_merchant_api.c
+++ b/src/lib/test_merchant_api.c
@@ -18,9 +18,6 @@
  * @brief testcase to test merchant's HTTP API interface
  * @author Christian Grothoff
  * @author Marcello Stanisci
- *
- * TODO:
- * - add test logic for tips to main test interpreter
  */
 #include "platform.h"
 #include <taler/taler_exchange_service.h>
@@ -177,6 +174,21 @@ enum OpCode
   OC_PAY,
 
   /**
+   * Resume pay operation with additional coins.
+   */
+  OC_PAY_AGAIN,
+
+  /**
+   * Abort payment with coins, requesting refund.
+   */
+  OC_PAY_ABORT,
+
+  /**
+   * Abort payment with coins, executing refund.
+   */
+  OC_PAY_ABORT_REFUND,
+
+  /**
    * Run the aggregator to execute deposits.
    */
   OC_RUN_AGGREGATOR,
@@ -222,11 +234,6 @@ enum OpCode
   OC_REFUND_LOOKUP,
 
   /**
-   * Start a reserve for tipping.
-   */
-  OC_TIP_ENABLE,
-
-  /**
    * Authorize a tip.
    */
   OC_TIP_AUTHORIZE,
@@ -234,7 +241,17 @@ enum OpCode
   /**
    * Pickup a tip.
    */
-  OC_TIP_PICKUP
+  OC_TIP_PICKUP,
+
+  /**
+   * Check pay status.
+   */
+  OC_CHECK_PAYMENT,
+
+  /**
+   * Query tip stats.
+   */
+  OC_TIP_QUERY,
 
 };
 
@@ -488,6 +505,11 @@ struct Command
       struct TALER_MERCHANT_ProposalOperation *po;
 
       /**
+       * Handle to the active GET /proposal operation, or NULL.
+       */
+      struct TALER_MERCHANT_ProposalLookupOperation *plo;
+
+      /**
        * Full contract in JSON, set by the /contract operation.
        * FIXME: verify in the code that this bit is actually proposal
        * data and not the whole proposal.
@@ -504,11 +526,16 @@ struct Command
        */
       struct GNUNET_HashCode hash;
 
+      /**
+       * The nonce set by the customer looking up the contract
+       * the first time.
+       */
+      struct GNUNET_CRYPTO_EddsaPublicKey nonce;
+
     } proposal;
 
     /**
      * Information for a #OC_PAY command.
-     * FIXME: support tests where we pay with multiple coins at once.
      */
     struct
     {
@@ -541,7 +568,13 @@ struct Command
       const char *amount_without_fee;
 
       /**
-       * Deposit handle while operation is running.
+       * Refund fee to use for each coin (only relevant if we
+       * exercise /pay's abort functionality).
+       */
+      const char *refund_fee;
+
+      /**
+       * Pay handle while operation is running.
        */
       struct TALER_MERCHANT_Pay *ph;
 
@@ -560,6 +593,92 @@ struct Command
     struct {
 
       /**
+       * Reference to the (incomplete) pay operation that is to be
+       * resumed.
+       */
+      char *pay_ref;
+
+      /**
+       * ";"-separated list of references to additional withdrawn
+       * coins to be used in the payment.  Each reference has the
+       * syntax "LABEL[/NUMBER]" where NUMBER refers to a particular
+       * coin (in case multiple coins were created in a step).
+       */
+      char *coin_ref;
+
+      /**
+       * Pay handle while operation is running.
+       */
+      struct TALER_MERCHANT_Pay *ph;
+
+    } pay_again;
+
+    struct {
+
+      /**
+       * Reference to the pay operation that is to be aborted.
+       */
+      char *pay_ref;
+
+      /**
+       * Pay handle while operation is running.
+       */
+      struct TALER_MERCHANT_Pay *ph;
+
+      /**
+       * Set in #pay_refund_cb to number of refunds obtained.
+       */
+      unsigned int num_refunds;
+
+      /**
+       * Array of @e num_refund refunds obtained.
+       */
+      struct TALER_MERCHANT_RefundEntry *res;
+
+      /**
+       * Set to the hash of the original contract.
+       */
+      struct GNUNET_HashCode h_contract;
+
+      /**
+       * Set to the merchant's public key.
+       */
+      struct TALER_MerchantPublicKeyP merchant_pub;
+
+    } pay_abort;
+
+    struct {
+
+      /**
+       * Reference to the pay_abort command to be refunded.
+       */
+      const char *abort_ref;
+
+      /**
+       * Number of the coin of @e abort_ref to be refunded.
+       */
+      unsigned int num_coin;
+
+      /**
+       * Refund amount to use.
+       */
+      const char *refund_amount;
+
+      /**
+       * Refund fee to expect.
+       */
+      const char *refund_fee;
+
+      /**
+       * Handle to the refund operation.
+       */
+      struct TALER_EXCHANGE_RefundHandle *rh;
+
+    } pay_abort_refund;
+
+    struct {
+
+      /**
        * Process for the aggregator.
        */
       struct GNUNET_OS_Process *aggregator_proc;
@@ -749,45 +868,6 @@ struct Command
     struct {
 
       /**
-       * Reference to the operation that provisioned the reserve.
-       * Used to determine the reserve private key and the instance.
-       */
-      const char *admin_add_incoming_ref;
-
-      /**
-       * Reference to another enable operation, usually NULL. Can
-       * be set to a non-NULL value to call enable again with the
-       * same @e credit_uuid that was previously used.
-       */
-      const char *uuid_ref;
-
-      /**
-       * How much should be put into the tipping reserve? If
-       * NULL, the amount is taken from the @e admin_add_incoming_ref.
-       */
-      const char *amount;
-
-      /**
-       * Handle to the ongoing operation.
-       */
-      struct TALER_MERCHANT_TipEnableOperation *teo;
-
-      /**
-       * UUID used for the enable operation, set by the interpreter to
-       * a random value UNLESS @e uuid_ref is non-NULL.
-       */
-      struct GNUNET_HashCode credit_uuid;
-
-      /**
-       * EC expected for the operation.
-       */
-      enum TALER_ErrorCode expected_ec;
-
-    } tip_enable;
-
-    struct {
-
-      /**
        * Specify the instance (to succeed, this must match a prior
        * enable action and the respective wire transfer's instance).
        */
@@ -836,6 +916,12 @@ struct Command
       const char *authorize_ref;
 
       /**
+       * Set to non-NULL to a label of another pick up operation
+       * that we should replay.
+       */
+      const char *replay_ref;
+
+      /**
        * Number of coins we pick up.
        */
       unsigned int num_coins;
@@ -880,6 +966,57 @@ struct Command
 
     } tip_pickup;
 
+    struct {
+      /**
+       * Expected available amount (in string format).
+       * NULL to skip check.
+       */
+      char *expected_amount_available;
+
+      /**
+       * Expected picked up amount (in string format).
+       * NULL to skip check.
+       */
+      char *expected_amount_picked_up;
+
+      /**
+       * Expected authorized amount (in string format).
+       * NULL to skip check.
+       */
+      char *expected_amount_authorized;
+
+      /**
+       * Handle for the ongoing operation.
+       */
+      struct TALER_MERCHANT_TipQueryOperation *tqo;
+
+      /**
+       * Merchant instance to use for tipping.
+       */
+      char *instance;
+
+    } tip_query;
+
+    struct {
+
+      /**
+       * Reference for the contract we want to check.
+       */
+      const char *contract_ref;
+
+      /**
+       * Whether to expect the payment to be settled or not.
+       */
+      int expect_paid;
+
+      /**
+       * Operation handle for the /check-payment request,
+       * NULL if operation is not running.
+       */
+      struct TALER_MERCHANT_CheckPaymentOperation *cpo;
+
+    } check_payment;
+
   } details;
 
 };
@@ -1059,6 +1196,54 @@ add_incoming_cb (void *cls,
   next_command (is);
 }
 
+/**
+ * Parse given JSON object to absolute time.
+ *
+ * @param root the json object representing data
+ * @param[out] ret where to write the data
+ * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
+ */
+static int
+parse_abs_time (json_t *root,
+                struct GNUNET_TIME_Absolute *ret)
+{
+  const char *val;
+  unsigned long long int tval;
+
+  val = json_string_value (root);
+  if (NULL == val)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if ( (0 == strcasecmp (val,
+                         "/forever/")) ||
+       (0 == strcasecmp (val,
+                         "/end of time/")) ||
+       (0 == strcasecmp (val,
+                         "/never/")) )
+  {
+    *ret = GNUNET_TIME_UNIT_FOREVER_ABS;
+    return GNUNET_OK;
+  }
+  if (1 != sscanf (val,
+                   "/Date(%llu)/",
+                   &tval))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  /* Time is in seconds in JSON, but in microseconds in GNUNET_TIME_Absolute */
+  ret->abs_value_us = tval * 1000LL * 1000LL;
+  if ( (ret->abs_value_us) / 1000LL / 1000LL != tval)
+  {
+    /* Integer overflow */
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
 
 /**
  * Callback for a /history request. It's up to this function how
@@ -1079,6 +1264,8 @@ history_cb (void *cls,
   struct InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
   unsigned int nresult;
+  struct GNUNET_TIME_Absolute last_timestamp;
+  struct GNUNET_TIME_Absolute entry_timestamp;
 
   cmd->details.history.ho = NULL;
   if (MHD_HTTP_OK != http_status)
@@ -1096,6 +1283,32 @@ history_cb (void *cls,
     fail (is);
     return;
   }
+
+  last_timestamp = GNUNET_TIME_absolute_get ();
+  last_timestamp = GNUNET_TIME_absolute_add (last_timestamp,
+                                             GNUNET_TIME_UNIT_DAYS);
+  json_t *entry;
+  json_t *timestamp;
+  size_t index;
+  json_array_foreach (json, index, entry)
+  {
+    timestamp = json_object_get (entry, "timestamp");
+    if (GNUNET_OK != parse_abs_time (timestamp, &entry_timestamp))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Bad timestamp given\n");
+        fail (is);
+        return;
+      }
+    entry_timestamp = GNUNET_TIME_absolute_max (last_timestamp, 
entry_timestamp);
+    if (last_timestamp.abs_value_us != entry_timestamp.abs_value_us)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "History entries are NOT sorted from younger to older\n");
+      fail (is);
+      return;
+    }
+  }
+
   next_command (is);
 }
 
@@ -1199,8 +1412,9 @@ reserve_status_cb (void *cls,
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
+                ec,
                 cmd->label);
     GNUNET_break (0);
     json_dumpf (json, stderr, 0);
@@ -1323,8 +1537,9 @@ reserve_withdraw_cb (void *cls,
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
+                ec,
                 cmd->label);
     fail (is);
     return;
@@ -1359,6 +1574,40 @@ reserve_withdraw_cb (void *cls,
 
 
 /**
+ * Callback for GET /proposal issued at backend.
+ * Used to initialize the proposal after it was created.
+ *
+ * @param cls closure
+ * @param http_status HTTP status code we got
+ * @param json full response we got
+ */
+static void
+proposal_lookup_initial_cb (void *cls,
+                            unsigned int http_status,
+                            const json_t *json,
+                            const json_t *contract_terms,
+                            const struct TALER_MerchantSignatureP *sig,
+                            const struct GNUNET_HashCode *hash)
+{
+  struct InterpreterState *is = cls;
+  struct Command *cmd = &is->commands[is->ip];
+
+  if (cmd->expected_response_code != http_status)
+  {
+    fail (is);
+    return;
+  }
+
+  cmd->details.proposal.hash = *hash;
+  cmd->details.proposal.merchant_sig = *sig;
+  cmd->details.proposal.contract_terms = json_deep_copy (contract_terms);
+
+  cmd->details.proposal.plo = NULL;
+  next_command (is);
+}
+
+
+/**
  * Callback that works POST /proposal's output.
  *
  * @param cls closure
@@ -1377,9 +1626,7 @@ proposal_cb (void *cls,
              unsigned int http_status,
             enum TALER_ErrorCode ec,
              const json_t *obj,
-             const json_t *contract_terms,
-             const struct TALER_MerchantSignatureP *sig,
-             const struct GNUNET_HashCode *hash)
+             const char *order_id)
 {
   struct InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
@@ -1388,22 +1635,33 @@ proposal_cb (void *cls,
   switch (http_status)
   {
   case MHD_HTTP_OK:
-    cmd->details.proposal.contract_terms = json_incref ((json_t *) 
contract_terms);
-    cmd->details.proposal.merchant_sig = *sig;
-    cmd->details.proposal.hash = *hash;
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Hashed proposal is `%s'\n",
-                GNUNET_h2s (hash));
     break;
-  default:
+  default: {
+    char *s = json_dumps (obj, JSON_COMPACT);
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected status code from /proposal: %u. Step %u\n",
+                "Unexpected status code from /proposal: %u (%d). Step %u, 
response: %s\n",
                 http_status,
-                is->ip);
+                ec,
+                is->ip,
+                s);
+    GNUNET_free_non_null (s);
     fail (is);
+    }
     return;
   }
-  next_command (is);
+
+  if (NULL == (cmd->details.proposal.plo
+               = TALER_MERCHANT_proposal_lookup (ctx,
+                                                 MERCHANT_URL,
+                                                 order_id,
+                                                 instance,
+                                                 &cmd->details.proposal.nonce,
+                                                 proposal_lookup_initial_cb,
+                                                 is)))
+  {
+    GNUNET_break (0);
+    fail (is);
+  }
 }
 
 
@@ -1608,7 +1866,6 @@ pay_cb (void *cls,
 {
   struct InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
-  struct PaymentResponsePS mr;
   struct GNUNET_CRYPTO_EddsaSignature sig;
   const char *error_name;
   unsigned int error_line;
@@ -1617,8 +1874,9 @@ pay_cb (void *cls,
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
+                ec,
                 cmd->label);
     fail (is);
     return;
@@ -1626,9 +1884,11 @@ pay_cb (void *cls,
   if (MHD_HTTP_OK == http_status)
   {
     /* Check signature */
+    struct PaymentResponsePS mr;
     struct GNUNET_JSON_Specification spec[] = {
       GNUNET_JSON_spec_fixed_auto ("sig", &sig),
-      GNUNET_JSON_spec_fixed_auto ("h_contract_terms", 
&cmd->details.pay.h_contract_terms),
+      GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
+                                  &cmd->details.pay.h_contract_terms),
       GNUNET_JSON_spec_end ()
     };
     GNUNET_assert (GNUNET_OK ==
@@ -1656,40 +1916,112 @@ pay_cb (void *cls,
 
 
 /**
- * Task triggered whenever we receive a SIGCHLD (child
- * process died).
+ * Function called with the result of a /pay again operation.
  *
- * @param cls closure, NULL if we need to self-restart
+ * @param cls closure with the interpreter state
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
deposit;
+ *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
+ * @param ec taler-specific error object
+ * @param obj the received JSON reply, should be kept as proof (and, in case 
of errors,
+ *            be forwarded to the customer)
  */
 static void
-maint_child_death (void *cls)
+pay_again_cb (void *cls,
+             unsigned int http_status,
+             enum TALER_ErrorCode ec,
+             const json_t *obj)
 {
   struct InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
-  const struct GNUNET_DISK_FileHandle *pr;
-  char c[16];
+  struct GNUNET_CRYPTO_EddsaSignature sig;
+  const char *error_name;
+  unsigned int error_line;
+  const struct Command *pref;
 
-  switch (cmd->oc) {
-  case OC_RUN_AGGREGATOR:
-    cmd->details.run_aggregator.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)));
-    GNUNET_OS_process_wait (cmd->details.run_aggregator.aggregator_proc);
-    GNUNET_OS_process_destroy (cmd->details.run_aggregator.aggregator_proc);
-    cmd->details.run_aggregator.aggregator_proc = NULL;
-    break;
-  case OC_RUN_WIREWATCH:
-    cmd->details.run_wirewatch.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)));
-    GNUNET_OS_process_wait (cmd->details.run_wirewatch.wirewatch_proc);
-    GNUNET_OS_process_destroy (cmd->details.run_wirewatch.wirewatch_proc);
-    cmd->details.run_wirewatch.wirewatch_proc = NULL;
-    break;
-  default:
+  cmd->details.pay_again.ph = NULL;
+  if (cmd->expected_response_code != http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u (%d) to command %s\n",
+                http_status,
+                ec,
+                cmd->label);
+    fail (is);
+    return;
+  }
+  GNUNET_assert (NULL != (pref = find_command
+                         (is,
+                          cmd->details.pay_again.pay_ref)));
+  if (MHD_HTTP_OK == http_status)
+  {
+    struct PaymentResponsePS mr;
+    /* Check signature */
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_fixed_auto ("sig",
+                                  &sig),
+      GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
+                                  &mr.h_contract_terms),
+      GNUNET_JSON_spec_end ()
+    };
+
+    GNUNET_assert (GNUNET_OK ==
+        GNUNET_JSON_parse (obj,
+                           spec,
+                           &error_name,
+                           &error_line));
+    mr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK);
+    mr.purpose.size = htonl (sizeof (mr));
+    if (GNUNET_OK !=
+        GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_PAYMENT_OK,
+                                    &mr.purpose,
+                                    &sig,
+                                    &pref->details.pay.merchant_pub.eddsa_pub))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Merchant signature given in response to /pay invalid\n");
+      fail (is);
+      return;
+    }
+  }
+  next_command (is);
+}
+
+
+/**
+ * Task triggered whenever we receive a SIGCHLD (child
+ * process died).
+ *
+ * @param cls closure, NULL if we need to self-restart
+ */
+static void
+maint_child_death (void *cls)
+{
+  struct InterpreterState *is = cls;
+  struct Command *cmd = &is->commands[is->ip];
+  const struct GNUNET_DISK_FileHandle *pr;
+  char c[16];
+
+  switch (cmd->oc) {
+  case OC_RUN_AGGREGATOR:
+    cmd->details.run_aggregator.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)));
+    GNUNET_OS_process_wait (cmd->details.run_aggregator.aggregator_proc);
+    GNUNET_OS_process_destroy (cmd->details.run_aggregator.aggregator_proc);
+    cmd->details.run_aggregator.aggregator_proc = NULL;
+    break;
+  case OC_RUN_WIREWATCH:
+    cmd->details.run_wirewatch.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)));
+    GNUNET_OS_process_wait (cmd->details.run_wirewatch.wirewatch_proc);
+    GNUNET_OS_process_destroy (cmd->details.run_wirewatch.wirewatch_proc);
+    cmd->details.run_wirewatch.wirewatch_proc = NULL;
+    break;
+  default:
     GNUNET_break (0);
     fail (is);
     return;
@@ -1731,8 +2063,9 @@ track_transfer_cb (void *cls,
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
+                ec,
                 cmd->label);
     fail (is);
     return;
@@ -1760,7 +2093,10 @@ track_transfer_cb (void *cls,
 static void
 proposal_lookup_cb (void *cls,
                     unsigned int http_status,
-                    const json_t *json)
+                    const json_t *json,
+                    const json_t *contract_terms,
+                    const struct TALER_MerchantSignatureP *sig,
+                    const struct GNUNET_HashCode *hash)
 {
   struct InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
@@ -1773,73 +2109,148 @@ proposal_lookup_cb (void *cls,
 
 
 /**
- * Function called with detailed wire transfer data.
+ * Callback for GET /proposal issued at backend. Just check
+ * whether response code is as expected.
  *
  * @param cls closure
- * @param http_status HTTP status code we got, 0 on exchange protocol violation
- * @param ec taler-specific error code
- * @param json original json reply
+ * @param http_status HTTP status code we got
+ * @param json full response we got
  */
 static void
-track_transaction_cb (void *cls,
-                      unsigned int http_status,
-                      enum TALER_ErrorCode ec,
-                      const json_t *json)
+check_payment_cb (void *cls,
+                  unsigned int http_status,
+                  const json_t *obj,
+                  int paid,
+                  int refunded,
+                  struct TALER_Amount *refund_amount,
+                  const char *payment_redirect_url)
 {
   struct InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
 
-  cmd->details.track_transaction.tth = NULL;
-  if (cmd->expected_response_code != http_status)
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "check payment: expected paid: %s: %d\n",
+              cmd->label,
+              cmd->details.check_payment.expect_paid);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "check payment: paid: %d\n",
+              paid);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "check payment: url: %s\n",
+              payment_redirect_url);
+
+  cmd->details.check_payment.cpo = NULL;
+  if (paid != cmd->details.check_payment.expect_paid)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
-                http_status,
-                cmd->label);
+    GNUNET_break (0);
     fail (is);
     return;
   }
-  if (MHD_HTTP_OK != http_status)
+
+  if (cmd->expected_response_code != http_status)
     fail (is);
   next_command (is);
 }
 
 
 /**
- * Callback for a /tip-enable request.  Returns the result of
- * the operation.
+ * Callback to process a GET /tip-query request
  *
  * @param cls closure
- * @param http_status HTTP status returned by the merchant backend
+ * @param http_status HTTP status code for this request
+ * @param ec Taler-specific error code
+ * @param raw raw response body
+ */
+static void
+tip_query_cb (void *cls,
+              unsigned int http_status,
+              enum TALER_ErrorCode ec,
+              const json_t *raw,
+              struct GNUNET_TIME_Absolute reserve_expiration,
+              struct TALER_ReservePublicKeyP *reserve_pub,
+              struct TALER_Amount *amount_authorized,
+              struct TALER_Amount *amount_available,
+              struct TALER_Amount *amount_picked_up)
+{
+  struct InterpreterState *is = cls;
+  struct Command *cmd = &is->commands[is->ip];
+  struct TALER_Amount a;
+
+  cmd->details.tip_query.tqo = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Tip query callback at command %u/%s(%u)\n",
+              is->ip,
+              cmd->label,
+              cmd->oc);
+
+  GNUNET_assert (NULL != reserve_pub);
+  GNUNET_assert (NULL != amount_authorized);
+  GNUNET_assert (NULL != amount_available);
+  GNUNET_assert (NULL != amount_picked_up);
+
+  if (cmd->details.tip_query.expected_amount_available)
+  {
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_string_to_amount 
(cmd->details.tip_query.expected_amount_available, &a));
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO, "expected available %s, actual %s\n",
+                TALER_amount_to_string (&a),
+                TALER_amount_to_string (amount_available));
+    GNUNET_assert (0 == TALER_amount_cmp (amount_available, &a));
+  }
+
+  if (cmd->details.tip_query.expected_amount_authorized)
+  {
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_string_to_amount 
(cmd->details.tip_query.expected_amount_authorized, &a));
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO, "expected authorized %s, actual %s\n",
+                TALER_amount_to_string (&a),
+                TALER_amount_to_string (amount_authorized));
+    GNUNET_assert (0 == TALER_amount_cmp (amount_authorized, &a));
+  }
+
+  if (cmd->details.tip_query.expected_amount_picked_up)
+  {
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_string_to_amount 
(cmd->details.tip_query.expected_amount_picked_up, &a));
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO, "expected picked_up %s, actual %s\n",
+                TALER_amount_to_string (&a),
+                TALER_amount_to_string (amount_picked_up));
+    GNUNET_assert (0 == TALER_amount_cmp (amount_picked_up, &a));
+  }
+
+  if (cmd->expected_response_code != http_status)
+    fail (is);
+  next_command (is);
+}
+
+
+/**
+ * Function called with detailed wire transfer data.
+ *
+ * @param cls closure
+ * @param http_status HTTP status code we got, 0 on exchange protocol violation
  * @param ec taler-specific error code
+ * @param json original json reply
  */
 static void
-tip_enable_cb (void *cls,
-               unsigned int http_status,
-               enum TALER_ErrorCode ec)
+track_transaction_cb (void *cls,
+                      unsigned int http_status,
+                      enum TALER_ErrorCode ec,
+                      const json_t *json)
 {
   struct InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
 
-  cmd->details.tip_enable.teo = NULL;
+  cmd->details.track_transaction.tth = NULL;
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
-                cmd->label);
-    fail (is);
-    return;
-  }
-  if (cmd->details.tip_enable.expected_ec != ec)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected error code %u to command %s\n",
                 ec,
                 cmd->label);
     fail (is);
     return;
   }
+  if (MHD_HTTP_OK != http_status)
+    fail (is);
   next_command (is);
 }
 
@@ -1853,7 +2264,7 @@ tip_enable_cb (void *cls,
  * @param ec taler-specific error code
  * @param tip_id which tip ID should be used to pickup the tip
  * @param tip_expiration when does the tip expire (needs to be picked up 
before this time)
- * @param exchange_uri at what exchange can the tip be picked up
+ * @param exchange_url at what exchange can the tip be picked up
  */
 static void
 tip_authorize_cb (void *cls,
@@ -1861,7 +2272,7 @@ tip_authorize_cb (void *cls,
                   enum TALER_ErrorCode ec,
                   const struct GNUNET_HashCode *tip_id,
                   struct GNUNET_TIME_Absolute tip_expiration,
-                  const char *exchange_uri)
+                  const char *exchange_url)
 {
   struct InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
@@ -1870,8 +2281,9 @@ tip_authorize_cb (void *cls,
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
+                ec,
                 cmd->label);
     fail (is);
     return;
@@ -1879,8 +2291,9 @@ tip_authorize_cb (void *cls,
   if (cmd->details.tip_authorize.expected_ec != ec)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected error code %u to command %s\n",
+                "Unexpected error code %d (%u) to command %s\n",
                 ec,
+                http_status,
                 cmd->label);
     fail (is);
     return;
@@ -1888,12 +2301,12 @@ tip_authorize_cb (void *cls,
   if ( (MHD_HTTP_OK == http_status) &&
        (TALER_EC_NONE == ec) )
   {
-    if (0 != strcmp (exchange_uri,
+    if (0 != strcmp (exchange_url,
                      EXCHANGE_URL))
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Unexpected exchange URL %s to command %s\n",
-                  exchange_uri,
+                  exchange_url,
                   cmd->label);
       fail (is);
       return;
@@ -1929,7 +2342,8 @@ pickup_withdraw_cb (void *cls,
 
   wh->wsh = NULL;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Withdraw operation completed with %u/%u\n",
+              "Withdraw operation %u completed with %u (%d)\n",
+              wh->off,
               http_status,
               ec);
   GNUNET_assert (wh->off < cmd->details.tip_pickup.num_coins);
@@ -1937,7 +2351,7 @@ pickup_withdraw_cb (void *cls,
        (TALER_EC_NONE != ec) )
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u/%u to command %s when 
withdrawing\n",
+                "Unexpected response code %u (%d) to command %s when 
withdrawing\n",
                 http_status,
                 ec,
                 cmd->label);
@@ -1947,6 +2361,7 @@ pickup_withdraw_cb (void *cls,
   if (NULL == cmd->details.tip_pickup.sigs)
     cmd->details.tip_pickup.sigs = GNUNET_new_array 
(cmd->details.tip_pickup.num_coins,
                                                      struct 
TALER_DenominationSignature);
+  GNUNET_assert (NULL == cmd->details.tip_pickup.sigs[wh->off].rsa_signature);
   cmd->details.tip_pickup.sigs[wh->off].rsa_signature
     = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature);
   for (unsigned int i=0;i<cmd->details.tip_pickup.num_coins;i++)
@@ -1986,7 +2401,7 @@ pickup_cb (void *cls,
   if (http_status != cmd->expected_response_code)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u/%u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
                 ec,
                 cmd->label);
@@ -2017,6 +2432,7 @@ pickup_cb (void *cls,
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Obtained %u signatures for withdrawal from picking up a tip\n",
               num_reserve_sigs);
+  GNUNET_assert (NULL == cmd->details.tip_pickup.withdraws);
   cmd->details.tip_pickup.withdraws
     = GNUNET_new_array (num_reserve_sigs,
                         struct WithdrawHandle);
@@ -2026,6 +2442,9 @@ pickup_cb (void *cls,
 
     wh->off = i;
     wh->is = is;
+    GNUNET_assert ( (NULL == wh->wsh) &&
+                    ( (NULL == cmd->details.tip_pickup.sigs) ||
+                      (NULL == 
cmd->details.tip_pickup.sigs[wh->off].rsa_signature) ) );
     wh->wsh = TALER_EXCHANGE_reserve_withdraw2 (exchange,
                                                 cmd->details.tip_pickup.dks[i],
                                                 &reserve_sigs[i],
@@ -2170,12 +2589,20 @@ cleanup_state (struct InterpreterState *is)
       if (NULL != cmd->details.proposal.po)
       {
         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    "Command %u (%s) did not complete\n",
+                    "Command %u (%s) did not complete (proposal put)\n",
                     i,
                     cmd->label);
         TALER_MERCHANT_proposal_cancel (cmd->details.proposal.po);
         cmd->details.proposal.po = NULL;
       }
+      if (NULL != cmd->details.proposal.plo)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Command %u (%s) did not complete (proposal lookup)\n",
+                    i,
+                    cmd->label);
+        TALER_MERCHANT_proposal_lookup_cancel (cmd->details.proposal.plo);
+      }
       if (NULL != cmd->details.proposal.contract_terms)
       {
         json_decref (cmd->details.proposal.contract_terms);
@@ -2193,6 +2620,42 @@ cleanup_state (struct InterpreterState *is)
         cmd->details.pay.ph = NULL;
       }
       break;
+    case OC_PAY_AGAIN:
+      if (NULL != cmd->details.pay_again.ph)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Command %u (%s) did not complete\n",
+                    i,
+                    cmd->label);
+        TALER_MERCHANT_pay_cancel (cmd->details.pay_again.ph);
+        cmd->details.pay_again.ph = NULL;
+      }
+      break;
+    case OC_PAY_ABORT:
+      if (NULL != cmd->details.pay_abort.ph)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Command %u (%s) did not complete\n",
+                    i,
+                    cmd->label);
+        TALER_MERCHANT_pay_cancel (cmd->details.pay_abort.ph);
+        cmd->details.pay_abort.ph = NULL;
+      }
+      GNUNET_array_grow (cmd->details.pay_abort.res,
+                        cmd->details.pay_abort.num_refunds,
+                        0);
+      break;
+    case OC_PAY_ABORT_REFUND:
+      if (NULL != cmd->details.pay_abort_refund.rh)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Command %u (%s) did not complete\n",
+                    i,
+                    cmd->label);
+       TALER_EXCHANGE_refund_cancel (cmd->details.pay_abort_refund.rh);
+       cmd->details.pay_abort_refund.rh = NULL;
+      }
+      break;
     case OC_RUN_AGGREGATOR:
       if (NULL != cmd->details.run_aggregator.aggregator_proc)
       {
@@ -2266,13 +2729,6 @@ cleanup_state (struct InterpreterState *is)
         cmd->details.refund_lookup.rlo = NULL;
       }
       break;
-    case OC_TIP_ENABLE:
-      if (NULL != cmd->details.tip_enable.teo)
-      {
-        TALER_MERCHANT_tip_enable_cancel (cmd->details.tip_enable.teo);
-        cmd->details.tip_enable.teo = NULL;
-      }
-      break;
     case OC_TIP_AUTHORIZE:
       if (NULL != cmd->details.tip_authorize.tao)
       {
@@ -2315,12 +2771,36 @@ cleanup_state (struct InterpreterState *is)
       {
         for (unsigned int j=0;j<cmd->details.tip_pickup.num_coins;j++)
         {
-          if (NULL != cmd->details.reserve_withdraw.sig.rsa_signature)
+          if (NULL != cmd->details.tip_pickup.sigs[j].rsa_signature)
           {
             GNUNET_CRYPTO_rsa_signature_free 
(cmd->details.tip_pickup.sigs[j].rsa_signature);
             cmd->details.tip_pickup.sigs[j].rsa_signature = NULL;
           }
         }
+        GNUNET_free (cmd->details.tip_pickup.sigs);
+        cmd->details.tip_pickup.sigs = NULL;
+      }
+      break;
+    case OC_CHECK_PAYMENT:
+      if (NULL != cmd->details.check_payment.cpo)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Command %u (%s) did not complete\n",
+                    i,
+                    cmd->label);
+        TALER_MERCHANT_check_payment_cancel (cmd->details.check_payment.cpo);
+        cmd->details.check_payment.cpo = NULL;
+      }
+      break;
+    case OC_TIP_QUERY:
+      if (NULL != cmd->details.tip_query.tqo)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Command %u (%s) did not complete\n",
+                    i,
+                    cmd->label);
+        TALER_MERCHANT_tip_query_cancel (cmd->details.tip_query.tqo);
+        cmd->details.tip_query.tqo = NULL;
       }
       break;
     default:
@@ -2336,6 +2816,188 @@ cleanup_state (struct InterpreterState *is)
 
 
 /**
+ * Parse the @a coins specification and grow the @a pc
+ * array with the coins found, updating @a npc.
+ *
+ * @param[in,out] pc pointer to array of coins found
+ * @param[in,out] npc length of array at @a pc
+ * @param[in] coins string specifying coins to add to @a pc,
+ *            clobbered in the process
+ * @param is interpreter state
+ * @param pref reference to the #OC_PAY command
+ * @return #GNUNET_OK on success
+ */
+static int
+build_coins (struct TALER_MERCHANT_PayCoin **pc,
+            unsigned int *npc,
+            char *coins,
+            struct InterpreterState *is,
+            const struct Command *pref)
+{
+  char *token;
+
+  for (token = strtok (coins, ";");
+       NULL != token;
+       token = strtok (NULL, ";"))
+  {
+    const struct Command *coin_ref;
+    char *ctok;
+    unsigned int ci;
+    struct TALER_MERCHANT_PayCoin *icoin;
+
+    /* Token syntax is "LABEL[/NUMBER]" */
+    ctok = strchr (token, '/');
+    ci = 0;
+    if (NULL != ctok)
+    {
+      *ctok = '\0';
+      ctok++;
+      if (1 != sscanf (ctok,
+                      "%u",
+                      &ci))
+      {
+       GNUNET_break (0);
+       return GNUNET_SYSERR;
+      }
+    }
+    GNUNET_assert (coin_ref = find_command (is,
+                                           token));
+    GNUNET_array_grow (*pc,
+                      *npc,
+                      (*npc) + 1);
+    icoin = &(*pc)[(*npc)-1];
+    switch (coin_ref->oc)
+    {
+    case OC_WITHDRAW_SIGN:
+      icoin->coin_priv = coin_ref->details.reserve_withdraw.ps.coin_priv;
+      icoin->denom_pub = coin_ref->details.reserve_withdraw.pk->key;
+      icoin->denom_sig = coin_ref->details.reserve_withdraw.sig;
+      icoin->denom_value = coin_ref->details.reserve_withdraw.pk->value;
+      icoin->exchange_url = EXCHANGE_URL;
+      break;
+    case OC_TIP_PICKUP:
+      icoin->coin_priv = coin_ref->details.tip_pickup.psa[ci].coin_priv;
+      icoin->denom_pub = coin_ref->details.tip_pickup.dks[ci]->key;
+      icoin->denom_sig = coin_ref->details.tip_pickup.sigs[ci];
+      icoin->denom_value = coin_ref->details.tip_pickup.dks[ci]->value;
+      icoin->exchange_url = EXCHANGE_URL;
+      break;
+    default:
+      GNUNET_assert (0);
+    }
+
+    GNUNET_assert (GNUNET_OK ==
+                  TALER_string_to_amount (pref->details.pay.amount_with_fee,
+                                          &icoin->amount_with_fee));
+    GNUNET_assert (GNUNET_OK ==
+                  TALER_string_to_amount (pref->details.pay.amount_without_fee,
+                                          &icoin->amount_without_fee));
+    GNUNET_assert (GNUNET_OK ==
+                  TALER_string_to_amount (pref->details.pay.refund_fee,
+                                          &icoin->refund_fee));
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Callbacks of this type are used to serve the result of submitting a
+ * /pay request to a merchant.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, 200 or 300-level response codes
+ *                    can indicate success, depending on whether the 
interaction
+ *                    was with a merchant frontend or backend;
+ *                    0 if the merchant's reply is bogus (fails to follow the 
protocol)
+ * @param ec taler-specific error code
+ * @param merchant_pub public key of the merchant
+ * @param h_contract hash of the contract
+ * @param num_refunds size of the @a merchant_sigs array, 0 on errors
+ * @param res merchant signatures refunding coins, NULL on errors
+ * @param obj the received JSON reply, with error details if the request failed
+ */
+static void
+pay_refund_cb (void *cls,
+              unsigned int http_status,
+              enum TALER_ErrorCode ec,
+              const struct TALER_MerchantPublicKeyP *merchant_pub,
+              const struct GNUNET_HashCode *h_contract,
+              unsigned int num_refunds,
+              const struct TALER_MERCHANT_RefundEntry *res,
+              const json_t *obj)
+{
+  struct InterpreterState *is = cls;
+  struct Command *cmd = &is->commands[is->ip];
+
+  cmd->details.pay_abort.ph = NULL;
+  if (cmd->expected_response_code != http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u (%d) to command %s\n",
+                http_status,
+                ec,
+                cmd->label);
+    fail (is);
+    return;
+  }
+  if ( (MHD_HTTP_OK == http_status) &&
+       (TALER_EC_NONE == ec) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Received %u refunds\n",
+                num_refunds);
+    cmd->details.pay_abort.num_refunds = num_refunds;
+    cmd->details.pay_abort.res
+      = GNUNET_new_array (num_refunds,
+                         struct TALER_MERCHANT_RefundEntry);
+    memcpy (cmd->details.pay_abort.res,
+           res,
+           num_refunds * sizeof (struct TALER_MERCHANT_RefundEntry));
+    cmd->details.pay_abort.h_contract = *h_contract;
+    cmd->details.pay_abort.merchant_pub = *merchant_pub;
+  }
+  next_command (is);
+}
+
+
+/**
+ * Callbacks of this type are used to serve the result of submitting a
+ * refund request to an exchange.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
deposit;
+ *                    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 sign_key exchange key used to sign @a obj, or NULL
+ * @param obj the received JSON reply, should be kept as proof (and, in 
particular,
+ *            be forwarded to the customer)
+ */
+static void
+abort_refund_cb (void *cls,
+                unsigned int http_status,
+                enum TALER_ErrorCode ec,
+                const struct TALER_ExchangePublicKeyP *sign_key,
+                const json_t *obj)
+{
+  struct InterpreterState *is = cls;
+  struct Command *cmd = &is->commands[is->ip];
+
+  cmd->details.pay_abort_refund.rh = NULL;
+  if (cmd->expected_response_code != http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u (%d) to command %s\n",
+                http_status,
+                ec,
+                cmd->label);
+    fail (is);
+    return;
+  }
+  next_command (is);
+}
+
+
+/**
  * Run the main interpreter loop that performs exchange operations.
  *
  * @param cls contains the `struct InterpreterState`
@@ -2359,7 +3021,7 @@ interpreter_run (void *cls)
     fail (is);
     return;
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Interpreter runs command %u/%s(%u)\n",
               is->ip,
               cmd->label,
@@ -2397,11 +3059,13 @@ interpreter_run (void *cls)
 
       order_id = json_string_value (json_object_get 
(ref->details.proposal.contract_terms,
                                                      "order_id"));
+
       if (NULL == (cmd->details.proposal_lookup.plo
                    = TALER_MERCHANT_proposal_lookup (ctx,
                                                      MERCHANT_URL,
                                                      order_id,
                                                      instance,
+                                                     
&ref->details.proposal.nonce,
                                                      proposal_lookup_cb,
                                                      is)))
       {
@@ -2410,6 +3074,59 @@ interpreter_run (void *cls)
       }
     }
     break;
+  case OC_CHECK_PAYMENT:
+    {
+      const char *order_id;
+
+      GNUNET_assert (NULL != cmd->details.check_payment.contract_ref);
+      GNUNET_assert (NULL != (ref =
+                              find_command (is,
+                                            
cmd->details.check_payment.contract_ref)));
+
+      order_id = json_string_value (json_object_get 
(ref->details.proposal.contract_terms,
+                                                     "order_id"));
+
+      GNUNET_assert (NULL != order_id);
+
+      if (NULL == (cmd->details.check_payment.cpo
+                   = TALER_MERCHANT_check_payment (ctx,
+                                                   MERCHANT_URL,
+                                                   instance,
+                                                   order_id,
+                                                   NULL,
+                                                   NULL,
+                                                   NULL,
+                                                   check_payment_cb,
+                                                   is)))
+      {
+        GNUNET_break (0);
+        fail (is);
+      }
+    }
+    break;
+  case OC_TIP_QUERY:
+    {
+      if (instance_idx !=  0)
+      {
+        // We check /tip-query only for the first instance,
+        // since for tipping we use a dedicated instance.
+        // On repeated runs, the expected authorized amounts wouldn't
+        // match up (they would all accumulate!)
+        next_command (is);
+        break;
+      }
+      if (NULL == (cmd->details.tip_query.tqo
+                   = TALER_MERCHANT_tip_query (ctx,
+                                               MERCHANT_URL,
+                                               cmd->details.tip_query.instance,
+                                               tip_query_cb,
+                                               is)))
+      {
+        GNUNET_break (0);
+        fail (is);
+      }
+    }
+    break;
   case OC_ADMIN_ADD_INCOMING:
     {
       char *subject;
@@ -2570,6 +3287,10 @@ interpreter_run (void *cls)
       GNUNET_assert (NULL != (order = json_loads (cmd->details.proposal.order,
                                                   JSON_REJECT_DUPLICATES,
                                                   &error)));
+      GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+                                  &cmd->details.proposal.nonce,
+                                  sizeof (struct 
GNUNET_CRYPTO_EddsaPublicKey));
+
       if (NULL != instance)
       {
         json_t *merchant;
@@ -2609,7 +3330,6 @@ interpreter_run (void *cls)
       struct TALER_MerchantSignatureP merchant_sig;
       struct TALER_Amount total_amount;
       struct TALER_Amount max_fee;
-      char *token;
       const char *error_name;
       unsigned int error_line;
 
@@ -2622,14 +3342,22 @@ interpreter_run (void *cls)
       {
         /* Get information that needs to be replied in the deposit permission 
*/
         struct GNUNET_JSON_Specification spec[] = {
-          GNUNET_JSON_spec_string ("order_id", &order_id),
-          GNUNET_JSON_spec_absolute_time ("refund_deadline", &refund_deadline),
-          GNUNET_JSON_spec_absolute_time ("pay_deadline", &pay_deadline),
-          GNUNET_JSON_spec_absolute_time ("timestamp", &timestamp),
-          GNUNET_JSON_spec_fixed_auto ("merchant_pub", &merchant_pub),
-          GNUNET_JSON_spec_fixed_auto ("H_wire", &h_wire),
-          TALER_JSON_spec_amount ("amount", &total_amount),
-          TALER_JSON_spec_amount ("max_fee", &max_fee),
+          GNUNET_JSON_spec_string ("order_id",
+                                  &order_id),
+          GNUNET_JSON_spec_absolute_time ("refund_deadline",
+                                         &refund_deadline),
+          GNUNET_JSON_spec_absolute_time ("pay_deadline",
+                                         &pay_deadline),
+          GNUNET_JSON_spec_absolute_time ("timestamp",
+                                         &timestamp),
+          GNUNET_JSON_spec_fixed_auto ("merchant_pub",
+                                      &merchant_pub),
+          GNUNET_JSON_spec_fixed_auto ("H_wire",
+                                      &h_wire),
+          TALER_JSON_spec_amount ("amount",
+                                 &total_amount),
+          TALER_JSON_spec_amount ("max_fee",
+                                 &max_fee),
           GNUNET_JSON_spec_end()
         };
 
@@ -2655,68 +3383,260 @@ interpreter_run (void *cls)
       }
       /* strtok loop here */
       coins = GNUNET_strdup (cmd->details.pay.coin_ref);
-
       pc = NULL;
       npc = 0;
-      for (token = strtok (coins, ";");
-           NULL != token;
-           token = strtok (NULL, ";"))
+      if (GNUNET_OK !=
+         build_coins (&pc,
+                      &npc,
+                      coins,
+                      is,
+                      cmd))
       {
-        const struct Command *coin_ref;
-        char *ctok;
-        unsigned int ci;
-        struct TALER_MERCHANT_PayCoin *icoin;
-
-        /* Token syntax is "LABEL[/NUMBER]" */
-        ctok = strchr (token, '/');
-       ci = 0;
-        if (NULL != ctok)
-        {
-          *ctok = '\0';
-          ctok++;
-         if (1 != sscanf (ctok,
-                          "%u",
-                          &ci))
-         {
-           GNUNET_break (0);
-           fail (is);
-           return;
-         }
-       }
-        GNUNET_assert (coin_ref = find_command (is,
-                                                token));
-        GNUNET_array_grow (pc,
-                           npc,
-                           npc + 1);
-        icoin = &pc[npc-1];
-        switch (coin_ref->oc)
+       fail (is);
+       GNUNET_array_grow (pc,
+                          npc,
+                          0);
+       GNUNET_free (coins);
+       return;
+      }
+      GNUNET_free (coins);
+
+      cmd->details.pay.ph = TALER_MERCHANT_pay_wallet
+        (ctx,
+         MERCHANT_URL,
+         instance,
+         &ref->details.proposal.hash,
+         &total_amount,
+         &max_fee,
+         &merchant_pub,
+         &merchant_sig,
+         timestamp,
+         refund_deadline,
+         pay_deadline,
+         &h_wire,
+         order_id,
+         npc /* num_coins */,
+         pc /* coins */,
+         &pay_cb,
+         is);
+      GNUNET_array_grow (pc,
+                         npc,
+                         0);
+    }
+    if (NULL == cmd->details.pay.ph)
+    {
+      GNUNET_break (0);
+      fail (is);
+    }
+    break;
+
+  case OC_PAY_AGAIN:
+    {
+      struct TALER_MERCHANT_PayCoin *pc;
+      const struct Command *pref;
+      unsigned int npc;
+      char *coins;
+      const char *order_id;
+      struct GNUNET_TIME_Absolute refund_deadline;
+      struct GNUNET_TIME_Absolute pay_deadline;
+      struct GNUNET_TIME_Absolute timestamp;
+      struct GNUNET_HashCode h_wire;
+      struct TALER_MerchantPublicKeyP merchant_pub;
+      struct TALER_MerchantSignatureP merchant_sig;
+      struct TALER_Amount total_amount;
+      struct TALER_Amount max_fee;
+      const char *error_name;
+      unsigned int error_line;
+
+      /* Get original /pay command */
+      GNUNET_assert (NULL != (pref = find_command
+                              (is,
+                               cmd->details.pay_again.pay_ref)));
+      /* get proposal */
+      GNUNET_assert (NULL != (ref = find_command
+                              (is,
+                               pref->details.pay.contract_ref)));
+      merchant_sig = ref->details.proposal.merchant_sig;
+      GNUNET_assert (NULL != ref->details.proposal.contract_terms);
+      {
+        /* Get information that needs to be replied in the deposit permission 
*/
+        struct GNUNET_JSON_Specification spec[] = {
+          GNUNET_JSON_spec_string ("order_id",
+                                  &order_id),
+          GNUNET_JSON_spec_absolute_time ("refund_deadline",
+                                         &refund_deadline),
+          GNUNET_JSON_spec_absolute_time ("pay_deadline",
+                                         &pay_deadline),
+          GNUNET_JSON_spec_absolute_time ("timestamp",
+                                         &timestamp),
+          GNUNET_JSON_spec_fixed_auto ("merchant_pub",
+                                      &merchant_pub),
+          GNUNET_JSON_spec_fixed_auto ("H_wire",
+                                      &h_wire),
+          TALER_JSON_spec_amount ("amount",
+                                 &total_amount),
+          TALER_JSON_spec_amount ("max_fee",
+                                 &max_fee),
+          GNUNET_JSON_spec_end()
+        };
+
+        if (GNUNET_OK !=
+            GNUNET_JSON_parse (ref->details.proposal.contract_terms,
+                               spec,
+                               &error_name,
+                               &error_line))
         {
-        case OC_WITHDRAW_SIGN:
-          icoin->coin_priv = coin_ref->details.reserve_withdraw.ps.coin_priv;
-          icoin->denom_pub = coin_ref->details.reserve_withdraw.pk->key;
-          icoin->denom_sig = coin_ref->details.reserve_withdraw.sig;
-          icoin->denom_value = coin_ref->details.reserve_withdraw.pk->value;
-          break;
-        case OC_TIP_PICKUP:
-          icoin->coin_priv = coin_ref->details.tip_pickup.psa[ci].coin_priv;
-          icoin->denom_pub = coin_ref->details.tip_pickup.dks[ci]->key;
-          icoin->denom_sig = coin_ref->details.tip_pickup.sigs[ci];
-          icoin->denom_value = coin_ref->details.tip_pickup.dks[ci]->value;
-          break;
-        default:
-          GNUNET_assert (0);
+          GNUNET_break_op (0);
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      "Parser failed on %s:%u\n",
+                      error_name,
+                      error_line);
+          /**
+           * Let's use fail() here, as the proposal might be broken
+           * because of backend's fault.
+           */
+          fail (is);
+          return;
         }
-
-        GNUNET_assert (GNUNET_OK ==
-                       TALER_string_to_amount 
(cmd->details.pay.amount_without_fee,
-                                               &icoin->amount_without_fee));
-        GNUNET_assert (GNUNET_OK ==
-                       TALER_string_to_amount 
(cmd->details.pay.amount_with_fee,
-                                               &icoin->amount_with_fee));
+      }
+      pc = NULL;
+      npc = 0;
+      /* Loop over coins from pay again */
+      coins = GNUNET_strdup (cmd->details.pay_again.coin_ref);
+      if (GNUNET_OK !=
+         build_coins (&pc,
+                      &npc,
+                      coins,
+                      is,
+                      pref))
+      {
+       fail (is);
+       GNUNET_array_grow (pc,
+                          npc,
+                          0);
+       GNUNET_free (coins);
+       return;
       }
       GNUNET_free (coins);
+      /* then repeat payment attempt */
+      cmd->details.pay_again.ph = TALER_MERCHANT_pay_wallet
+        (ctx,
+         MERCHANT_URL,
+         instance,
+         &ref->details.proposal.hash,
+         &total_amount,
+         &max_fee,
+         &merchant_pub,
+         &merchant_sig,
+         timestamp,
+         refund_deadline,
+         pay_deadline,
+         &h_wire,
+         order_id,
+         npc /* num_coins */,
+         pc /* coins */,
+         &pay_again_cb,
+         is);
+      GNUNET_array_grow (pc,
+                         npc,
+                         0);
+    }
+    if (NULL == cmd->details.pay_again.ph)
+    {
+      GNUNET_break (0);
+      fail (is);
+    }
+    break;
+  case OC_PAY_ABORT:
+    {
+      struct TALER_MERCHANT_PayCoin *pc;
+      const struct Command *pref;
+      unsigned int npc;
+      char *coins;
+      const char *order_id;
+      struct GNUNET_TIME_Absolute refund_deadline;
+      struct GNUNET_TIME_Absolute pay_deadline;
+      struct GNUNET_TIME_Absolute timestamp;
+      struct GNUNET_HashCode h_wire;
+      struct TALER_MerchantPublicKeyP merchant_pub;
+      struct TALER_MerchantSignatureP merchant_sig;
+      struct TALER_Amount total_amount;
+      struct TALER_Amount max_fee;
+      const char *error_name;
+      unsigned int error_line;
 
-      cmd->details.pay.ph = TALER_MERCHANT_pay_wallet
+      /* Get original /pay command */
+      GNUNET_assert (NULL != (pref = find_command
+                              (is,
+                               cmd->details.pay_abort.pay_ref)));
+      /* get proposal */
+      GNUNET_assert (NULL != (ref = find_command
+                              (is,
+                               pref->details.pay.contract_ref)));
+      merchant_sig = ref->details.proposal.merchant_sig;
+      GNUNET_assert (NULL != ref->details.proposal.contract_terms);
+      {
+        /* Get information that needs to be replied in the deposit permission 
*/
+        struct GNUNET_JSON_Specification spec[] = {
+          GNUNET_JSON_spec_string ("order_id",
+                                  &order_id),
+          GNUNET_JSON_spec_absolute_time ("refund_deadline",
+                                         &refund_deadline),
+          GNUNET_JSON_spec_absolute_time ("pay_deadline",
+                                         &pay_deadline),
+          GNUNET_JSON_spec_absolute_time ("timestamp",
+                                         &timestamp),
+          GNUNET_JSON_spec_fixed_auto ("merchant_pub",
+                                      &merchant_pub),
+          GNUNET_JSON_spec_fixed_auto ("H_wire",
+                                      &h_wire),
+          TALER_JSON_spec_amount ("amount",
+                                 &total_amount),
+          TALER_JSON_spec_amount ("max_fee",
+                                 &max_fee),
+          GNUNET_JSON_spec_end()
+        };
+
+        if (GNUNET_OK !=
+            GNUNET_JSON_parse (ref->details.proposal.contract_terms,
+                               spec,
+                               &error_name,
+                               &error_line))
+        {
+          GNUNET_break_op (0);
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      "Parser failed on %s:%u\n",
+                      error_name,
+                      error_line);
+          /**
+           * Let's use fail() here, as the proposal might be broken
+           * because of backend's fault.
+           */
+          fail (is);
+          return;
+        }
+      }
+      /* strtok loop over original coins here */
+      pc = NULL;
+      npc = 0;
+      coins = GNUNET_strdup (pref->details.pay.coin_ref);
+      if (GNUNET_OK !=
+         build_coins (&pc,
+                      &npc,
+                      coins,
+                      is,
+                      pref))
+      {
+       fail (is);
+       GNUNET_array_grow (pc,
+                          npc,
+                          0);
+       GNUNET_free (coins);
+       return;
+      }
+      GNUNET_free (coins);
+      /* then trigger abort-refund operation */
+      cmd->details.pay_abort.ph = TALER_MERCHANT_pay_abort
         (ctx,
          MERCHANT_URL,
          instance,
@@ -2729,22 +3649,61 @@ interpreter_run (void *cls)
          refund_deadline,
          pay_deadline,
          &h_wire,
-         EXCHANGE_URL,
          order_id,
          npc /* num_coins */,
          pc /* coins */,
-         &pay_cb,
+         &pay_refund_cb,
          is);
       GNUNET_array_grow (pc,
                          npc,
                          0);
     }
-    if (NULL == cmd->details.pay.ph)
+    if (NULL == cmd->details.pay_abort.ph)
     {
       GNUNET_break (0);
       fail (is);
     }
     break;
+  case OC_PAY_ABORT_REFUND:
+    {
+      struct TALER_Amount refund_fee;
+      const struct TALER_MERCHANT_RefundEntry *re;
+
+      /* Get original /pay command */
+      GNUNET_assert (NULL != (ref = find_command
+                              (is,
+                               cmd->details.pay_abort_refund.abort_ref)));
+      GNUNET_assert (OC_PAY_ABORT == ref->oc);
+      GNUNET_assert (ref->details.pay_abort.num_refunds >
+                    cmd->details.pay_abort_refund.num_coin);
+      re = &ref->details.pay_abort.res[cmd->details.pay_abort_refund.num_coin];
+
+      GNUNET_assert (GNUNET_OK ==
+                    TALER_string_to_amount
+                    (cmd->details.pay_abort_refund.refund_amount,
+                     &amount));
+      GNUNET_assert (GNUNET_OK ==
+                    TALER_string_to_amount
+                    (cmd->details.pay_abort_refund.refund_fee,
+                     &refund_fee));
+      cmd->details.pay_abort_refund.rh
+       = TALER_EXCHANGE_refund2 (exchange,
+                                 &amount,
+                                 &refund_fee,
+                                 &ref->details.pay_abort.h_contract,
+                                 &re->coin_pub,
+                                 re->rtransaction_id,
+                                 &ref->details.pay_abort.merchant_pub,
+                                 &re->merchant_sig,
+                                 &abort_refund_cb,
+                                 is);
+      if (NULL == cmd->details.pay_abort_refund.rh)
+      {
+       GNUNET_break (0);
+       fail (is);
+      }
+    }
+    break;
   case OC_RUN_AGGREGATOR:
     {
       const struct GNUNET_DISK_FileHandle *pr;
@@ -2798,9 +3757,10 @@ interpreter_run (void *cls)
     }
   case OC_CHECK_BANK_TRANSFER:
     {
-      GNUNET_assert (GNUNET_OK == TALER_string_to_amount
-        (cmd->details.check_bank_transfer.amount,
-         &amount));
+      GNUNET_assert (GNUNET_OK ==
+                    TALER_string_to_amount
+                    (cmd->details.check_bank_transfer.amount,
+                     &amount));
       if (GNUNET_OK != TALER_FAKEBANK_check
            (fakebank,
             &amount,
@@ -2837,10 +3797,11 @@ interpreter_run (void *cls)
         (is,
          cmd->details.track_transfer.check_bank_ref)));
       subject = ref->details.check_bank_transfer.subject;
-      GNUNET_assert (GNUNET_OK == GNUNET_STRINGS_string_to_data (subject,
-         strlen (subject),
-         &wtid,
-         sizeof (wtid)));
+      GNUNET_assert (GNUNET_OK ==
+                    GNUNET_STRINGS_string_to_data (subject,
+                                                   strlen (subject),
+                                                   &wtid,
+                                                   sizeof (wtid)));
       if (NULL == (cmd->details.track_transfer.tdo
           = TALER_MERCHANT_track_transfer (ctx,
                                            MERCHANT_URL,
@@ -2946,83 +3907,6 @@ interpreter_run (void *cls)
       }
       break;
     }
-  case OC_TIP_ENABLE:
-    {
-      const struct Command *uuid_ref;
-      struct TALER_ReservePrivateKeyP reserve_priv;
-      struct GNUNET_TIME_Absolute expiration;
-
-      if (NULL != cmd->details.tip_enable.admin_add_incoming_ref)
-      {
-        ref = find_command (is,
-                            cmd->details.tip_enable.admin_add_incoming_ref);
-        GNUNET_assert (NULL != ref);
-        GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
-      }
-      else
-      {
-        ref = NULL;
-      }
-
-      /* Initialize 'credit_uuid' */
-      if (NULL != cmd->details.tip_enable.uuid_ref)
-      {
-        uuid_ref = find_command (is,
-                                 cmd->details.tip_enable.uuid_ref);
-        GNUNET_assert (NULL != uuid_ref);
-        GNUNET_assert (OC_TIP_ENABLE == uuid_ref->oc);
-        cmd->details.tip_enable.credit_uuid
-          = uuid_ref->details.tip_enable.credit_uuid;
-      }
-      else
-      {
-        uuid_ref = NULL;
-        GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
-                                    &cmd->details.tip_enable.credit_uuid,
-                                    sizeof 
(cmd->details.tip_enable.credit_uuid));
-      }
-
-      /* Initialize 'amount' */
-      if ( (NULL != ref) &&
-           (NULL == cmd->details.tip_enable.amount) )
-      {
-        GNUNET_assert (GNUNET_OK ==
-                       TALER_string_to_amount 
(ref->details.admin_add_incoming.amount,
-                                               &amount));
-      }
-      else
-      {
-        GNUNET_assert (NULL != cmd->details.tip_enable.amount);
-        GNUNET_assert (GNUNET_OK ==
-                       TALER_string_to_amount (cmd->details.tip_enable.amount,
-                                               &amount));
-      }
-      if (NULL == ref)
-        GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
-                                    &reserve_priv,
-                                    sizeof (reserve_priv));
-      /* Simply picked long enough for the test (we do not test expiration
-         behavior for now), should be short enough so that the reserve
-        expires before the test is run again, so that we avoid old
-        state messing up fresh runs. */
-      expiration = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
-
-      if (NULL == (cmd->details.tip_enable.teo
-                   = TALER_MERCHANT_tip_enable
-                   (ctx,
-                    MERCHANT_URL,
-                    &amount,
-                    expiration,
-                    (ref != NULL) ? 
&ref->details.admin_add_incoming.reserve_priv : &reserve_priv,
-                    &cmd->details.tip_enable.credit_uuid,
-                    &tip_enable_cb,
-                    is)))
-      {
-        GNUNET_break (0);
-        fail (is);
-      }
-      break;
-    }
   case OC_TIP_AUTHORIZE:
     {
       GNUNET_assert (NULL != cmd->details.tip_authorize.amount);
@@ -3033,6 +3917,8 @@ interpreter_run (void *cls)
                    = TALER_MERCHANT_tip_authorize
                    (ctx,
                     MERCHANT_URL,
+                    "http://merchant.com/pickup";,
+                    "http://merchant.com/continue";,
                     &amount,
                     cmd->details.tip_authorize.instance,
                     cmd->details.tip_authorize.justification,
@@ -3047,38 +3933,64 @@ interpreter_run (void *cls)
   case OC_TIP_PICKUP:
     {
       unsigned int num_planchets;
+      const struct Command *rr;
 
-      for (num_planchets=0;
-           NULL != cmd->details.tip_pickup.amounts[num_planchets];
-           num_planchets++);
-      cmd->details.tip_pickup.num_coins = num_planchets;
+      if (NULL == cmd->details.tip_pickup.replay_ref)
       {
-        struct TALER_PlanchetDetail planchets[num_planchets];
-
+        rr = NULL;
+        for (num_planchets=0;
+             NULL != cmd->details.tip_pickup.amounts[num_planchets];
+             num_planchets++);
         ref = find_command (is,
                             cmd->details.tip_pickup.authorize_ref);
         GNUNET_assert (NULL != ref);
         GNUNET_assert (OC_TIP_AUTHORIZE == ref->oc);
+      }
+      else
+      {
+        rr = find_command (is,
+                           cmd->details.tip_pickup.replay_ref);
+        GNUNET_assert (NULL != rr);
+        GNUNET_assert (OC_TIP_PICKUP == rr->oc);
+        num_planchets = rr->details.tip_pickup.num_coins;
+        ref = find_command (is,
+                            rr->details.tip_pickup.authorize_ref);
+        GNUNET_assert (NULL != ref);
+        GNUNET_assert (OC_TIP_AUTHORIZE == ref->oc);
+      }
+      cmd->details.tip_pickup.num_coins = num_planchets;
+      {
+        struct TALER_PlanchetDetail planchets[num_planchets];
+
         cmd->details.tip_pickup.psa = GNUNET_new_array (num_planchets,
                                                         struct 
TALER_PlanchetSecretsP);
         cmd->details.tip_pickup.dks = GNUNET_new_array (num_planchets,
                                                         const struct 
TALER_EXCHANGE_DenomPublicKey *);
         for (unsigned int i=0;i<num_planchets;i++)
         {
-          GNUNET_assert (GNUNET_OK ==
-                         TALER_string_to_amount 
(cmd->details.tip_pickup.amounts[i],
-                                                 &amount));
-
-          cmd->details.tip_pickup.dks[i]
-            = find_pk (is->keys,
-                       &amount);
-          if (NULL == cmd->details.tip_pickup.dks[i])
+          if (NULL == rr)
           {
-            GNUNET_break (0);
-            fail (is);
-            return;
+            GNUNET_assert (GNUNET_OK ==
+                           TALER_string_to_amount 
(cmd->details.tip_pickup.amounts[i],
+                                                   &amount));
+            cmd->details.tip_pickup.dks[i]
+              = find_pk (is->keys,
+                         &amount);
+            if (NULL == cmd->details.tip_pickup.dks[i])
+            {
+              GNUNET_break (0);
+              fail (is);
+              return;
+            }
+            TALER_planchet_setup_random (&cmd->details.tip_pickup.psa[i]);
+          }
+          else
+          {
+            cmd->details.tip_pickup.dks[i]
+              = rr->details.tip_pickup.dks[i];
+            cmd->details.tip_pickup.psa[i]
+              = rr->details.tip_pickup.psa[i];
           }
-          TALER_planchet_setup_random (&cmd->details.tip_pickup.psa[i]);
           if (GNUNET_OK !=
               TALER_planchet_prepare (&cmd->details.tip_pickup.dks[i]->key,
                                       &cmd->details.tip_pickup.psa[i],
@@ -3090,14 +4002,13 @@ interpreter_run (void *cls)
           }
         }
         if (NULL == (cmd->details.tip_pickup.tpo
-                     = TALER_MERCHANT_tip_pickup
-                     (ctx,
-                      MERCHANT_URL,
-                      &ref->details.tip_authorize.tip_id,
-                      num_planchets,
-                      planchets,
-                      &pickup_cb,
-                      is)))
+                     = TALER_MERCHANT_tip_pickup (ctx,
+                                                  MERCHANT_URL,
+                                                  
&ref->details.tip_authorize.tip_id,
+                                                  num_planchets,
+                                                  planchets,
+                                                  &pickup_cb,
+                                                  is)))
         {
           GNUNET_break (0);
           fail (is);
@@ -3303,7 +4214,6 @@ run (void *cls)
       .details.reserve_status.reserve_reference
         = "create-reserve-1",
       .details.reserve_status.expected_balance = "EUR:0" },
-
     /* Create proposal */
     { .oc = OC_PROPOSAL,
       .label = "create-proposal-1",
@@ -3325,21 +4235,44 @@ run (void *cls)
           [ {\"description\":\"ice cream\",\
              \"value\":\"{EUR:5}\"} ] }"},
 
+    /* check payment before we pay */
+    { .oc = OC_CHECK_PAYMENT,
+      .label = "check-payment-1",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.check_payment.contract_ref = "create-proposal-1",
+      .details.check_payment.expect_paid = GNUNET_NO },
+
     /* execute simple payment */
     { .oc = OC_PAY,
       .label = "deposit-simple",
       .expected_response_code = MHD_HTTP_OK,
       .details.pay.contract_ref = "create-proposal-1",
       .details.pay.coin_ref = "withdraw-coin-1",
+      .details.pay.refund_fee = "EUR:0.01",
       .details.pay.amount_with_fee = "EUR:5",
       .details.pay.amount_without_fee = "EUR:4.99" },
 
+    /* check payment after we've paid */
+    { .oc = OC_CHECK_PAYMENT,
+      .label = "check-payment-2",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.check_payment.contract_ref = "create-proposal-1",
+      .details.check_payment.expect_paid = GNUNET_YES },
+
+    /* Test for #5262: abort after full payment */
+    { .oc = OC_PAY_ABORT,
+      .label = "pay-abort-2",
+      .expected_response_code = MHD_HTTP_FORBIDDEN,
+      .details.pay_abort.pay_ref = "deposit-simple",
+    },
+
     /* Try to replay payment reusing coin */
     { .oc = OC_PAY,
       .label = "replay-simple",
       .expected_response_code = MHD_HTTP_OK,
       .details.pay.contract_ref = "create-proposal-1",
       .details.pay.coin_ref = "withdraw-coin-1",
+      .details.pay.refund_fee = "EUR:0.01",
       .details.pay.amount_with_fee = "EUR:5",
       .details.pay.amount_without_fee = "EUR:4.99" },
 
@@ -3372,6 +4305,7 @@ run (void *cls)
       .expected_response_code = MHD_HTTP_FORBIDDEN,
       .details.pay.contract_ref = "create-proposal-2",
       .details.pay.coin_ref = "withdraw-coin-1",
+      .details.pay.refund_fee = "EUR:0.01",
       .details.pay.amount_with_fee = "EUR:5",
       .details.pay.amount_without_fee = "EUR:4.99" },
 
@@ -3499,6 +4433,7 @@ run (void *cls)
       .expected_response_code = MHD_HTTP_OK,
       .details.pay.contract_ref = "create-proposal-2",
       .details.pay.coin_ref = "withdraw-coin-2",
+      .details.pay.refund_fee = "EUR:0.01",
       .details.pay.amount_with_fee = "EUR:5",
       .details.pay.amount_without_fee = "EUR:4.99" },
 
@@ -3608,17 +4543,6 @@ run (void *cls)
       .details.check_bank_transfer.account_debit = 62,
       .details.check_bank_transfer.account_credit = EXCHANGE_ACCOUNT_NO
     },
-    { .oc = OC_TIP_ENABLE,
-      .label = "enable-tip-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.tip_enable.admin_add_incoming_ref = "create-reserve-tip-1",
-      .details.tip_enable.amount = "EUR:5.01" },
-    /* Test incrementing active reserve balance */
-    { .oc = OC_TIP_ENABLE,
-      .label = "enable-tip-2",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.tip_enable.admin_add_incoming_ref = "create-reserve-tip-1",
-      .details.tip_enable.amount = "EUR:5.01" },
     /* Authorize two tips */
     { .oc = OC_TIP_AUTHORIZE,
       .label = "authorize-tip-1",
@@ -3632,11 +4556,54 @@ run (void *cls)
       .details.tip_authorize.instance = "tip",
       .details.tip_authorize.justification = "tip 2",
       .details.tip_authorize.amount = "EUR:5.01" },
+    /* Check tip status */
+    { .oc = OC_TIP_QUERY,
+      .label = "query-tip-1",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.tip_query.instance = "tip" },
+    { .oc = OC_TIP_QUERY,
+      .label = "query-tip-2",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.tip_query.instance = "tip",
+      .details.tip_query.expected_amount_authorized = "EUR:10.02",
+      .details.tip_query.expected_amount_picked_up = "EUR:0.0",
+      .details.tip_query.expected_amount_available = "EUR:20.04" },
+    /* Withdraw tip */
+    { .oc = OC_TIP_PICKUP,
+      .label = "pickup-tip-1",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.tip_pickup.authorize_ref = "authorize-tip-1",
+      .details.tip_pickup.amounts = pickup_amounts_1 },
+    /* Check tip status again */
+    { .oc = OC_TIP_QUERY,
+      .label = "query-tip-3",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.tip_query.instance = "tip",
+      .details.tip_query.expected_amount_picked_up = "EUR:5.01",
+      .details.tip_query.expected_amount_available = "EUR:15.03" },
+    { .oc = OC_TIP_PICKUP,
+      .label = "pickup-tip-2",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.tip_pickup.authorize_ref = "authorize-tip-2",
+      .details.tip_pickup.amounts = pickup_amounts_1 },
+    { .oc = OC_TIP_QUERY,
+      .label = "query-tip-4",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.tip_query.instance = "tip",
+      .details.tip_query.expected_amount_picked_up = "EUR:10.02",
+      .details.tip_query.expected_amount_available = "EUR:10.02",
+      .details.tip_query.expected_amount_authorized = "EUR:10.02" },
+    { .oc = OC_TIP_PICKUP,
+      .label = "pickup-tip-2b",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.tip_pickup.replay_ref = "pickup-tip-2",
+      .details.tip_pickup.authorize_ref = "authorize-tip-2",
+      .details.tip_pickup.amounts = pickup_amounts_1 },
     /* Test authorization failure modes */
     { .oc = OC_TIP_AUTHORIZE,
       .label = "authorize-tip-3-insufficient-funds",
       .expected_response_code = MHD_HTTP_PRECONDITION_FAILED,
-      .details.tip_authorize.instance = "tip",
+      .details.tip_authorize.instance = "dtip",
       .details.tip_authorize.justification = "tip 3",
       .details.tip_authorize.amount = "EUR:5.01",
       .details.tip_authorize.expected_ec = 
TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS },
@@ -3654,27 +4621,9 @@ run (void *cls)
       .details.tip_authorize.justification = "tip 5",
       .details.tip_authorize.amount = "EUR:5.01",
       .details.tip_authorize.expected_ec = 
TALER_EC_TIP_AUTHORIZE_INSTANCE_DOES_NOT_TIP },
-    { .oc = OC_TIP_AUTHORIZE,
-      .label = "authorize-tip-6-not-enabled-instance",
-      .expected_response_code = MHD_HTTP_NOT_FOUND,
-      .details.tip_authorize.instance = "dtip",
-      .details.tip_authorize.justification = "tip 6",
-      .details.tip_authorize.amount = "EUR:5.01",
-      .details.tip_authorize.expected_ec = 
TALER_EC_TIP_AUTHORIZE_RESERVE_NOT_ENABLED },
-    /* Withdraw tip */
-    { .oc = OC_TIP_PICKUP,
-      .label = "pickup-tip-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.tip_pickup.authorize_ref = "authorize-tip-1",
-      .details.tip_pickup.amounts = pickup_amounts_1 },
-    { .oc = OC_TIP_PICKUP,
-      .label = "pickup-tip-2",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.tip_pickup.authorize_ref = "authorize-tip-2",
-      .details.tip_pickup.amounts = pickup_amounts_1 },
     { .oc = OC_TIP_PICKUP,
       .label = "pickup-tip-3-too-much",
-      .expected_response_code = MHD_HTTP_SERVICE_UNAVAILABLE,
+      .expected_response_code = MHD_HTTP_CONFLICT,
       .details.tip_pickup.expected_ec = TALER_EC_TIP_PICKUP_NO_FUNDS,
       .details.tip_pickup.authorize_ref = "authorize-tip-1",
       .details.tip_pickup.amounts = pickup_amounts_1 },
@@ -3703,6 +4652,7 @@ run (void *cls)
       .expected_response_code = MHD_HTTP_OK,
       .details.pay.contract_ref = "create-proposal-tip-1",
       .details.pay.coin_ref = "pickup-tip-1",
+      .details.pay.refund_fee = "EUR:0.01",
       .details.pay.amount_with_fee = "EUR:5",
       .details.pay.amount_without_fee = "EUR:4.99" },
     /* Run transfers. */
@@ -3722,6 +4672,212 @@ run (void *cls)
       .label = "check_bank_empty" },
 
 
+    /* ****************** /pay again logic ************* */
+
+    /* Fill reserve with EUR:10.02, as withdraw fee is 1 ct per
+       config */
+    { .oc = OC_ADMIN_ADD_INCOMING,
+      .label = "create-reserve-10",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.admin_add_incoming.debit_account_no = 62,
+      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
+      .details.admin_add_incoming.auth_username = "user62",
+      .details.admin_add_incoming.auth_password = "pass62",
+      .details.admin_add_incoming.amount = "EUR:10.02" },
+    /* Run wirewatch to observe /admin/add/incoming */
+    { .oc = OC_RUN_WIREWATCH,
+      .label = "wirewatch-10" },
+    { .oc = OC_CHECK_BANK_TRANSFER,
+      .label = "check_bank_transfer-10",
+      .details.check_bank_transfer.amount = "EUR:10.02",
+      .details.check_bank_transfer.account_debit = 62,
+      .details.check_bank_transfer.account_credit = EXCHANGE_ACCOUNT_NO
+    },
+
+    /* Withdraw a 5 EUR coin, at fee of 1 ct */
+    { .oc = OC_WITHDRAW_SIGN,
+      .label = "withdraw-coin-10a",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.reserve_withdraw.reserve_reference
+        = "create-reserve-10",
+      .details.reserve_withdraw.amount = "EUR:5" },
+
+    /* Withdraw a 5 EUR coin, at fee of 1 ct */
+    { .oc = OC_WITHDRAW_SIGN,
+      .label = "withdraw-coin-10b",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.reserve_withdraw.reserve_reference
+        = "create-reserve-10",
+      .details.reserve_withdraw.amount = "EUR:5" },
+
+    /* Check that deposit and withdraw operation are in history,
+       and that the balance is now at zero */
+    { .oc = OC_WITHDRAW_STATUS,
+      .label = "withdraw-status-10",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.reserve_status.reserve_reference
+        = "create-reserve-10",
+      .details.reserve_status.expected_balance = "EUR:0" },
+
+    /* Create proposal */
+    { .oc = OC_PROPOSAL,
+      .label = "create-proposal-10",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.proposal.order = "{\
+        \"max_fee\":\
+          {\"currency\":\"EUR\",\
+           \"value\":0,\
+           \"fraction\":50000000},\
+        \"order_id\":\"10\",\
+        \"refund_deadline\":\"\\/Date(0)\\/\",\
+        \"pay_deadline\":\"\\/Date(99999999999)\\/\",\
+        \"amount\":\
+          {\"currency\":\"EUR\",\
+           \"value\":10,\
+           \"fraction\":0},\
+       \"summary\": \"merchant-lib testcase\",\
+        \"products\":\
+          [ {\"description\":\"ice cream\",\
+             \"value\":\"{EUR:10}\"} ] }"},
+
+    /* execute simple payment, re-using one ancient coin */
+    { .oc = OC_PAY,
+      .label = "pay-fail-partial-double-10",
+      .expected_response_code = MHD_HTTP_FORBIDDEN,
+      .details.pay.contract_ref = "create-proposal-10",
+      .details.pay.coin_ref = "withdraw-coin-10a;withdraw-coin-1",
+      /* These amounts are given per coin! */
+      .details.pay.refund_fee = "EUR:0.01",
+      .details.pay.amount_with_fee = "EUR:5",
+      .details.pay.amount_without_fee = "EUR:4.99" },
+
+    /* Try to replay payment reusing coin */
+    { .oc = OC_PAY_AGAIN,
+      .label = "pay-again-10",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.pay.refund_fee = "EUR:0.01",
+      .details.pay_again.pay_ref = "pay-fail-partial-double-10",
+      .details.pay_again.coin_ref = "withdraw-coin-10a;withdraw-coin-10b" },
+
+    /* Run transfers. */
+    { .oc = OC_RUN_AGGREGATOR,
+      .label = "run-aggregator-10" },
+
+    /* Obtain WTID of the transfer */
+    { .oc = OC_CHECK_BANK_TRANSFER,
+      .label = "check_bank_transfer-9.97-10",
+      .details.check_bank_transfer.amount = "EUR:9.97",
+      /* exchange-outgoing */
+      .details.check_bank_transfer.account_debit = 2,
+      /* merchant */
+      .details.check_bank_transfer.account_credit = 62
+    },
+
+    /* Check that there are no other unusual transfers */
+    { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
+      .label = "check_bank_empty-10" },
+
+
+    /* ****************** /pay abort logic ************* */
+
+    /* Fill reserve with EUR:10.02, as withdraw fee is 1 ct per
+       config */
+    { .oc = OC_ADMIN_ADD_INCOMING,
+      .label = "create-reserve-11",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.admin_add_incoming.debit_account_no = 62,
+      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
+      .details.admin_add_incoming.auth_username = "user62",
+      .details.admin_add_incoming.auth_password = "pass62",
+      .details.admin_add_incoming.amount = "EUR:10.02" },
+    /* Run wirewatch to observe /admin/add/incoming */
+    { .oc = OC_RUN_WIREWATCH,
+      .label = "wirewatch-11" },
+    { .oc = OC_CHECK_BANK_TRANSFER,
+      .label = "check_bank_transfer-11",
+      .details.check_bank_transfer.amount = "EUR:10.02",
+      .details.check_bank_transfer.account_debit = 62,
+      .details.check_bank_transfer.account_credit = EXCHANGE_ACCOUNT_NO
+    },
+
+    /* Withdraw a 5 EUR coin, at fee of 1 ct */
+    { .oc = OC_WITHDRAW_SIGN,
+      .label = "withdraw-coin-11a",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.reserve_withdraw.reserve_reference
+        = "create-reserve-11",
+      .details.reserve_withdraw.amount = "EUR:5" },
+
+    /* Withdraw a 5 EUR coin, at fee of 1 ct */
+    { .oc = OC_WITHDRAW_SIGN,
+      .label = "withdraw-coin-11b",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.reserve_withdraw.reserve_reference
+        = "create-reserve-11",
+      .details.reserve_withdraw.amount = "EUR:5" },
+
+    /* Check that deposit and withdraw operation are in history,
+       and that the balance is now at zero */
+    { .oc = OC_WITHDRAW_STATUS,
+      .label = "withdraw-status-11",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.reserve_status.reserve_reference
+        = "create-reserve-11",
+      .details.reserve_status.expected_balance = "EUR:0" },
+
+    /* Create proposal */
+    { .oc = OC_PROPOSAL,
+      .label = "create-proposal-11",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.proposal.order = "{\
+        \"max_fee\":\
+          {\"currency\":\"EUR\",\
+           \"value\":0,\
+           \"fraction\":50000000},\
+        \"order_id\":\"11\",\
+        \"refund_deadline\":\"\\/Date(0)\\/\",\
+        \"pay_deadline\":\"\\/Date(99999999999)\\/\",\
+        \"amount\":\
+          {\"currency\":\"EUR\",\
+           \"value\":10,\
+           \"fraction\":0},\
+       \"summary\": \"merchant-lib testcase\",\
+        \"products\":\
+          [ {\"description\":\"ice cream\",\
+             \"value\":\"{EUR:10}\"} ] }"},
+
+    /* execute simple payment, re-using one ancient coin */
+    { .oc = OC_PAY,
+      .label = "pay-fail-partial-double-11",
+      .expected_response_code = MHD_HTTP_FORBIDDEN,
+      .details.pay.contract_ref = "create-proposal-11",
+      .details.pay.coin_ref = "withdraw-coin-11a;withdraw-coin-1",
+      /* These amounts are given per coin! */
+      .details.pay.refund_fee = "EUR:0.01",
+      .details.pay.amount_with_fee = "EUR:5",
+      .details.pay.amount_without_fee = "EUR:4.99" },
+
+    /* Try to replay payment reusing coin */
+    { .oc = OC_PAY_ABORT,
+      .label = "pay-abort-11",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.pay_abort.pay_ref = "pay-fail-partial-double-11",
+    },
+
+    { .oc = OC_PAY_ABORT_REFUND,
+      .label = "pay-abort-refund-11",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.pay_abort_refund.abort_ref = "pay-abort-11",
+      .details.pay_abort_refund.num_coin = 0,
+      .details.pay_abort_refund.refund_amount = "EUR:5",
+      .details.pay_abort_refund.refund_fee = "EUR:0.01" },
+
+    /* Run transfers. */
+    { .oc = OC_RUN_AGGREGATOR,
+      .label = "run-aggregator-11" },
+    /* Check that there are no other unusual transfers */
+    { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
+      .label = "check_bank_empty-11" },
     /* end of testcase */
     { .oc = OC_END }
   };
diff --git a/src/lib/test_merchant_api.conf b/src/lib/test_merchant_api.conf
index c4a4eb7..924aaa5 100644
--- a/src/lib/test_merchant_api.conf
+++ b/src/lib/test_merchant_api.conf
@@ -45,7 +45,7 @@ DEFAULT_MAX_DEPOSIT_FEE = EUR:0.10
 # Enable 'test' for testing of the actual coin operations.
 ENABLE = YES
 
-BANK_URI = http://localhost:8083/
+BANK_URL = http://localhost:8083/
 
 # Fees for the forseeable future...
 # If you see this after 2017, update to match the next 10 years...
@@ -72,27 +72,32 @@ CLOSING-FEE-2026 = EUR:0.01
 
 
 [merchant-exchange-test]
-URI = http://localhost:8084/
+URL = http://localhost:8084/
 MASTER_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
 
 [merchant-instance-default]
 KEYFILE = test_merchant.priv
+NAME = Kudos Inc.
 
 [merchant-instance-wireformat-default]
 TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/merchant/wire/test.json
 
 [merchant-instance-tor]
 KEYFILE = tor_merchant.priv
+NAME = The Tor Project
 
 [merchant-instance-tip]
 KEYFILE = reserve_tip.priv
 TIP_EXCHANGE = http://localhost:8084/
 TIP_RESERVE_PRIV_FILENAME = reserve_key.priv
+NAME = Test Tipping Merchant
 
 [merchant-instance-dtip]
 KEYFILE = reserve_dtip.priv
 TIP_EXCHANGE = http://localhost:8084/
 TIP_RESERVE_PRIV_FILENAME = reserve_dkey.priv
+NAME = Test Tipping Merchant 2
+
 
 [merchant-instance-wireformat-tor]
 TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/merchant/wire/test.json
@@ -109,10 +114,10 @@ TEST_RESPONSE_FILE = 
${TALER_CONFIG_HOME}/merchant/wire/test.json
 # Informal name of the auditor. Just for the user.
 NAME = European Central Bank
 
-# URI of the auditor (especially for in the future, when the
+# URL of the auditor (especially for in the future, when the
 # auditor offers an automated issue reporting system).
 # Not really used today.
-URI = http://taler.ezb.eu/
+URL = http://taler.ezb.eu/
 
 # This is the important bit: the signing key of the auditor.
 PUBLIC_KEY = 9QXF7XY7E9VPV47B5Z806NDFSX2VJ79SVHHD29QEQ3BG31ANHZ60
@@ -158,7 +163,7 @@ DB_CONN_STR = "postgres:///talercheck"
 TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json
 
 # What is the main website of the bank?
-BANK_URI = "http://localhost:8083/";
+BANK_URL = "http://localhost:8083/";
 # From which account at the 'bank' should outgoing wire transfers be made?
 BANK_ACCOUNT_NUMBER = 2
 
diff --git 
a/src/lib/test_merchant_api_home/.config/taler/merchant/wire/test.json 
b/src/lib/test_merchant_api_home/.config/taler/merchant/wire/test.json
index 99c9c66..6898b00 100644
--- a/src/lib/test_merchant_api_home/.config/taler/merchant/wire/test.json
+++ b/src/lib/test_merchant_api_home/.config/taler/merchant/wire/test.json
@@ -1,5 +1,5 @@
 {
   "type":"test",
-  "bank_uri":"http://localhost:8083/";,
+  "bank_url":"http://localhost:8083/";,
   "account_number":62
 }
diff --git a/src/lib/test_merchant_api_home/.config/taler/test.json 
b/src/lib/test_merchant_api_home/.config/taler/test.json
index 6c8c61f..74cdc92 100644
--- a/src/lib/test_merchant_api_home/.config/taler/test.json
+++ b/src/lib/test_merchant_api_home/.config/taler/test.json
@@ -1,8 +1,8 @@
 {
   "name": "The exchange",
   "account_number": 3,
-  "bank_uri": "http://localhost:8083/";,
+  "bank_url": "http://localhost:8083/";,
   "salt": 
"6259MV4W9V8D2A75RSGGPKYHQRXRPQZ33EBG263JZRJ6SA5HK0RRKHV70TNA1RVRG77M57CCFVSK2B0EJN3SR8S21F0ZX2MR9DNVG50",
   "type": "test",
   "sig": 
"8C3D3J816S29AA2AJ7P9TS6W13KFNFS2RCVYJEWRBNHRRMTTRAWKY7WA1N3G54E4K3XAC2HN6JDHS42TWR5315J34JHHCKV618K221G"
-}
\ No newline at end of file
+}
diff --git a/src/merchant-tools/Makefile.am b/src/merchant-tools/Makefile.am
index df271a2..4e68837 100644
--- a/src/merchant-tools/Makefile.am
+++ b/src/merchant-tools/Makefile.am
@@ -3,8 +3,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/include
 
 bin_PROGRAMS = \
   taler-merchant-dbinit \
-  taler-merchant-generate-payments \
-  taler-merchant-tip-enable
+  taler-merchant-generate-payments
 
 taler_merchant_dbinit_SOURCES = \
   taler-merchant-dbinit.c
@@ -16,16 +15,6 @@ taler_merchant_dbinit_LDADD = \
   -ltalerutil \
   -ltalerpq
 
-taler_merchant_tip_enable_SOURCES = \
-  taler-merchant-tip-enable.c
-
-taler_merchant_tip_enable_LDADD = \
-  $(LIBGCRYPT_LIBS) \
-  $(top_builddir)/src/lib/libtalermerchant.la \
-  -lgnunetcurl \
-  -lgnunetutil \
-  -ltalerutil
-
 taler_merchant_generate_payments_SOURCES = \
   taler-merchant-generate-payments.c
 
@@ -33,6 +22,7 @@ taler_merchant_generate_payments_LDADD = \
   $(top_srcdir)/src/backenddb/libtalermerchantdb.la \
   $(top_srcdir)/src/lib/libtalermerchant.la \
   $(LIBGCRYPT_LIBS) \
+  -ltalerbank \
   -ltalerfakebank \
   -ltalerexchange \
   -ltalerjson \
diff --git a/src/merchant-tools/README b/src/merchant-tools/README
index 993a87e..d84ca59 100644
--- a/src/merchant-tools/README
+++ b/src/merchant-tools/README
@@ -28,7 +28,7 @@ exchange = http://localexchange/
 # where on this machine the merchant listens
 merchant = http://localshop/
 
-# bank's URI of the customer who withdraws coins.
+# bank's URL of the customer who withdraws coins.
 # Must be known by the exchange.
 bank = http://localbank/
 
diff --git a/src/merchant-tools/taler-merchant-generate-payments.c 
b/src/merchant-tools/taler-merchant-generate-payments.c
index 3e19943..7d40473 100644
--- a/src/merchant-tools/taler-merchant-generate-payments.c
+++ b/src/merchant-tools/taler-merchant-generate-payments.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 GNUnet e.V. and INRIA
+  Copyright (C) 2014-2017 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Lesser General Public License as published by the Free 
Software
@@ -13,9 +13,18 @@
   You should have received a copy of the GNU Lesser General Public License 
along with
   TALER; see the file COPYING.LGPL.  If not, see <http://www.gnu.org/licenses/>
 */
-
+/**
+ * @file taler-merchant-generate-payments.c
+ * @brief tool to use (or test) the taler backend by running some transactions
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ *
+ * TODO:
+ * - trigger wirewatch after /admin/add/incoming!
+ */
 #include "platform.h"
 #include <taler/taler_exchange_service.h>
+#include <taler/taler_bank_service.h>
 #include <taler/taler_fakebank_lib.h>
 #include <taler/taler_json_lib.h>
 #include <taler/taler_util.h>
@@ -27,6 +36,11 @@
 #include <microhttpd.h>
 
 /**
+ * Number of the account the exchange has at the bank.
+ */
+#define EXCHANGE_ACCOUNT_NO 2
+
+/**
  * The exchange process launched by the generator
  */
 static struct GNUNET_OS_Process *exchanged;
@@ -50,6 +64,12 @@ static unsigned int j = 0;
  * Indicates whether we use an external exchange.
  * By default, the generator forks a local exchange.
  */
+static int remote_bank = 0;
+
+/**
+ * Indicates whether we use an external exchange.
+ * By default, the generator forks a local exchange.
+ */
 static int remote_exchange = 0;
 
 /**
@@ -60,25 +80,25 @@ static int remote_exchange = 0;
 static int remote_merchant = 0;
 
 /**
- * Exchange URI to withdraw from and deposit to.
+ * Exchange URL to withdraw from and deposit to.
  */
-static char *exchange_uri;
+static char *exchange_url;
 
 /**
  * Base URL of exchange's admin interface.
  */
-static char *exchange_uri_admin;
+static char *exchange_url_admin;
 
 /**
  * Merchant backend to get proposals from and pay.
  */
-static char *merchant_uri;
+static char *merchant_url;
 
 /**
- * Customer's bank URI, communicated at withdrawal time
+ * Customer's bank URL, communicated at withdrawal time
  * to the exchange; must be the same as the exchange's bank.
  */
-static char *bank_uri;
+static char *bank_url;
 
 /**
  * Which merchant instance we use.
@@ -91,6 +111,11 @@ static char *instance;
 static char *currency;
 
 /**
+ * Handle to our fakebank.
+ */
+static struct TALER_FAKEBANK_Handle *fakebank;
+
+/**
  * Task run on timeout.
  */
 static struct GNUNET_SCHEDULER_Task *timeout_task;
@@ -121,6 +146,17 @@ static int result;
 static struct GNUNET_DISK_PipeHandle *sigpipe;
 
 /**
+ * Signal handler we overrode.
+ */
+static struct GNUNET_SIGNAL_Context *shc_chld;
+
+/**
+ * Name of the configuration file we are using.
+ */
+static char *cfgfilename;
+
+
+/**
  * State of the interpreter loop.
  */
 struct InterpreterState
@@ -169,6 +205,11 @@ enum OpCode
   OC_ADMIN_ADD_INCOMING,
 
   /**
+   * Run the wirewatcher to check for incoming transactions.
+   */
+  OC_RUN_WIREWATCH,
+
+  /**
    * Check status of a reserve.
    */
   OC_WITHDRAW_STATUS,
@@ -281,14 +322,14 @@ struct Command
       char *amount;
 
       /**
-       * Sender's bank account details (JSON).
+       * Sender's bank account number.
        */
-      const char *sender_details;
+      uint64_t debit_account_no;
 
       /**
-       * Transfer details (JSON)
+       * Receiver's bank account number.
        */
-       const char *transfer_details;
+      uint64_t credit_account_no;
 
       /**
        * Set (by the interpreter) to the reserve's private key
@@ -299,10 +340,29 @@ struct Command
       /**
        * Set to the API's handle during the operation.
        */
-      struct TALER_EXCHANGE_AdminAddIncomingHandle *aih;
+      struct TALER_BANK_AdminAddIncomingHandle *aih;
+
+      /**
+       * Set to bank's identifier for the wire transfer.
+       */
+      uint64_t serial_id;
 
     } admin_add_incoming;
 
+    struct {
+
+      /**
+       * Process for the wirewatcher.
+       */
+      struct GNUNET_OS_Process *wirewatch_proc;
+
+      /**
+       * ID of task called whenever we get a SIGCHILD.
+       */
+      struct GNUNET_SCHEDULER_Task *child_death_task;
+
+    } run_wirewatch;
+
     /**
      * Information for an #OC_PROPOSAL command.
      */
@@ -327,6 +387,11 @@ struct Command
       struct TALER_MERCHANT_ProposalOperation *po;
 
       /**
+       * Handle to the active GET /proposal operation, or NULL
+       */
+      struct TALER_MERCHANT_ProposalLookupOperation *plo;
+
+      /**
        * Full contract in JSON, set by the /contract operation.
        * FIXME: verify in the code that this bit is actually proposal
        * data and not the whole proposal.
@@ -459,7 +524,7 @@ next_command (struct InterpreterState *is)
 
 
 /**
- * Callback that works PUT /proposal's output.
+ * Callback that processes GET /proposal's output.
  *
  * @param cls closure
  * @param http_status HTTP response code, 200 indicates success;
@@ -473,18 +538,17 @@ next_command (struct InterpreterState *is)
  * @param h_contract hash of the contract, NULL on error
  */
 static void
-proposal_cb (void *cls,
-             unsigned int http_status,
-            enum TALER_ErrorCode ec,
-             const json_t *obj,
-             const json_t *contract_terms,
-             const struct TALER_MerchantSignatureP *sig,
-             const struct GNUNET_HashCode *hash)
+proposal_lookup_cb (void *cls,
+                    unsigned int http_status,
+                    const json_t *obj,
+                    const json_t *contract_terms,
+                    const struct TALER_MerchantSignatureP *sig,
+                    const struct GNUNET_HashCode *hash)
 {
   struct InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
 
-  cmd->details.proposal.po = NULL;
+  cmd->details.proposal.plo = NULL;
   switch (http_status)
   {
   case MHD_HTTP_OK:
@@ -508,6 +572,59 @@ proposal_cb (void *cls,
   next_command (is);
 }
 
+/**
+ * Callback that works PUT /proposal's output.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, 200 indicates success;
+ *                    0 if the backend's reply is bogus (fails to follow the 
protocol)
+ * @param ec taler-specific error code
+ * @param obj the full received JSON reply, or
+ *            error details if the request failed
+ */
+static void
+proposal_cb (void *cls,
+             unsigned int http_status,
+            enum TALER_ErrorCode ec,
+             const json_t *obj,
+             const char *order_id)
+{
+  struct InterpreterState *is = cls;
+  struct Command *cmd = &is->commands[is->ip];
+
+  cmd->details.proposal.po = NULL;
+  switch (http_status)
+  {
+  case MHD_HTTP_OK: {
+    struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
+    struct GNUNET_CRYPTO_EddsaPublicKey pub;
+
+    priv = GNUNET_CRYPTO_eddsa_key_create ();
+    GNUNET_CRYPTO_eddsa_key_get_public (priv, &pub);
+    GNUNET_free (priv);
+
+    cmd->details.proposal.plo
+        = TALER_MERCHANT_proposal_lookup (ctx,
+                                          merchant_url,
+                                          order_id,
+                                          instance,
+                                          &pub,
+                                          &proposal_lookup_cb,
+                                          is);
+    }
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "unexpected status code from /proposal: %u. Step %u\n",
+                http_status,
+                is->ip);
+    json_dumpf (obj, stderr, 0);
+    GNUNET_break (0);
+    fail (is);
+    return;
+  }
+}
+
 
 /**
  * Function called with the result of a /pay operation.
@@ -593,24 +710,28 @@ pay_cb (void *cls,
  * @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 for the transfer at the bank
  * @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 InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
 
   cmd->details.admin_add_incoming.aih = NULL;
+  cmd->details.admin_add_incoming.serial_id = serial_id;
   if (MHD_HTTP_OK != http_status)
   {
     GNUNET_break (0);
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "%s",
-                json_dumps (full_response, JSON_INDENT (2)));
+                json_dumps (full_response,
+                           JSON_INDENT (2)));
     fail (is);
     return;
   }
@@ -823,7 +944,7 @@ make_order (const char *maxfee,
 
 
 /**
- * Free amount stringsin interpreter state.
+ * Free amount strings in interpreter state.
  *
  * @param is state to reset
  */
@@ -852,6 +973,8 @@ free_interpreter_amounts (struct InterpreterState *is)
     case OC_ADMIN_ADD_INCOMING:
       GNUNET_free (cmd->details.admin_add_incoming.amount);
       break;
+    case OC_RUN_WIREWATCH:
+      break;
     default:
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Shutdown: unknown instruction %d at %u (%s)\n",
@@ -933,10 +1056,26 @@ reset_interpreter (struct InterpreterState *is)
                     "Command %u (%s) did not complete\n",
                     i,
                     cmd->label);
-        TALER_EXCHANGE_admin_add_incoming_cancel 
(cmd->details.admin_add_incoming.aih);
+        TALER_BANK_admin_add_incoming_cancel 
(cmd->details.admin_add_incoming.aih);
         cmd->details.admin_add_incoming.aih = NULL;
       }
       break;
+    case OC_RUN_WIREWATCH:
+      if (NULL != cmd->details.run_wirewatch.wirewatch_proc)
+      {
+        GNUNET_break (0 ==
+                      GNUNET_OS_process_kill 
(cmd->details.run_wirewatch.wirewatch_proc,
+                                              SIGKILL));
+        GNUNET_OS_process_wait (cmd->details.run_wirewatch.wirewatch_proc);
+        GNUNET_OS_process_destroy (cmd->details.run_wirewatch.wirewatch_proc);
+        cmd->details.run_wirewatch.wirewatch_proc = NULL;
+      }
+      if (NULL != cmd->details.run_wirewatch.child_death_task)
+      {
+        GNUNET_SCHEDULER_cancel (cmd->details.run_wirewatch.child_death_task);
+        cmd->details.run_wirewatch.child_death_task = NULL;
+      }
+      break;
     default:
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Shutdown: unknown instruction %d at %u (%s)\n",
@@ -949,6 +1088,30 @@ reset_interpreter (struct InterpreterState *is)
 
 
 /**
+ * Task triggered whenever we receive a SIGCHLD (child
+ * process died).
+ *
+ * @param cls closure, NULL if we need to self-restart
+ */
+static void
+maint_child_death (void *cls)
+{
+  struct InterpreterState *is = cls;
+  struct Command *cmd = &is->commands[is->ip];
+  const struct GNUNET_DISK_FileHandle *pr;
+  char c[16];
+
+  cmd->details.run_wirewatch.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)));
+  GNUNET_OS_process_wait (cmd->details.run_wirewatch.wirewatch_proc);
+  GNUNET_OS_process_destroy (cmd->details.run_wirewatch.wirewatch_proc);
+  cmd->details.run_wirewatch.wirewatch_proc = NULL;
+  next_command (is);
+}
+
+
+/**
  * Run the main interpreter loop that performs exchange operations.
  *
  * @param cls contains the `struct InterpreterState`
@@ -960,11 +1123,7 @@ interpreter_run (void *cls)
   struct InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
   const struct Command *ref;
-  struct TALER_ReservePublicKeyP reserve_pub;
   struct TALER_Amount amount;
-  struct GNUNET_TIME_Absolute execution_date;
-  json_t *sender_details;
-  json_t *transfer_details;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Interpreter runs command %u/%s(%u)\n",
@@ -985,25 +1144,20 @@ interpreter_run (void *cls)
   switch (cmd->oc)
   {
     case OC_END:
-
       j++;
-
       if (j < times)
       {
         reset_interpreter (is);
         is->ip = 0;
         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                     "Rewinding the interpreter.\n");
-
         GNUNET_SCHEDULER_add_now (&interpreter_run,
                                   is);
         return;
       }
       result = GNUNET_OK;
       GNUNET_SCHEDULER_shutdown ();
-
       return;
-
     case OC_PAY:
       {
         struct TALER_MERCHANT_PayCoin pc;
@@ -1070,6 +1224,7 @@ interpreter_run (void *cls)
             pc.denom_pub = coin_ref->details.reserve_withdraw.pk->key;
             pc.denom_sig = coin_ref->details.reserve_withdraw.sig;
             pc.denom_value = coin_ref->details.reserve_withdraw.pk->value;
+           pc.exchange_url = exchange_url;
             break;
           default:
             GNUNET_assert (0);
@@ -1101,7 +1256,7 @@ interpreter_run (void *cls)
         }
         cmd->details.pay.ph
           = TALER_MERCHANT_pay_wallet (ctx,
-                                       merchant_uri,
+                                       merchant_url,
                                        instance,
                                        &ref->details.proposal.hash,
                                        &total_amount,
@@ -1112,7 +1267,6 @@ interpreter_run (void *cls)
                                        refund_deadline,
                                        pay_deadline,
                                        &h_wire,
-                                       exchange_uri,
                                        order_id,
                                        1 /* num_coins */,
                                        &pc /* coins */,
@@ -1155,7 +1309,7 @@ interpreter_run (void *cls)
         json_decref (merchant_obj);
         cmd->details.proposal.po
           = TALER_MERCHANT_order_put (ctx,
-                                      merchant_uri,
+                                      merchant_url,
                                       order,
                                       &proposal_cb,
                                       is);
@@ -1170,92 +1324,102 @@ interpreter_run (void *cls)
       }
 
     case OC_ADMIN_ADD_INCOMING:
-      if (NULL !=
-          cmd->details.admin_add_incoming.reserve_reference)
-      {
-        ref = find_command (is,
-                            cmd->details.admin_add_incoming.reserve_reference);
-        GNUNET_assert (NULL != ref);
-        GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
-        cmd->details.admin_add_incoming.reserve_priv
-          = ref->details.admin_add_incoming.reserve_priv;
-      }
-      else
-      {
-        struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
-
-        priv = GNUNET_CRYPTO_eddsa_key_create ();
-        cmd->details.admin_add_incoming.reserve_priv.eddsa_priv = *priv;
-        GNUNET_free (priv);
-      }
-      GNUNET_CRYPTO_eddsa_key_get_public 
(&cmd->details.admin_add_incoming.reserve_priv.eddsa_priv,
-                                          &reserve_pub.eddsa_pub);
-      if (GNUNET_OK !=
-          TALER_string_to_amount (cmd->details.admin_add_incoming.amount,
-                                  &amount))
       {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Failed to parse amount `%s' at %u\n",
-                    cmd->details.admin_add_incoming.amount,
-                    is->ip);
-        fail (is);
-        return;
+       char *subject;
+       struct TALER_BANK_AuthenticationData auth;
+       struct TALER_ReservePublicKeyP reserve_pub;
+
+       if (NULL !=
+           cmd->details.admin_add_incoming.reserve_reference)
+       {
+         ref = find_command (is,
+                             
cmd->details.admin_add_incoming.reserve_reference);
+         GNUNET_assert (NULL != ref);
+         GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
+         cmd->details.admin_add_incoming.reserve_priv
+           = ref->details.admin_add_incoming.reserve_priv;
+       }
+       else
+       {
+         struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
+
+         priv = GNUNET_CRYPTO_eddsa_key_create ();
+         cmd->details.admin_add_incoming.reserve_priv.eddsa_priv = *priv;
+         GNUNET_free (priv);
+       }
+       GNUNET_CRYPTO_eddsa_key_get_public 
(&cmd->details.admin_add_incoming.reserve_priv.eddsa_priv,
+                                           &reserve_pub.eddsa_pub);
+       if (GNUNET_OK !=
+           TALER_string_to_amount (cmd->details.admin_add_incoming.amount,
+                                   &amount))
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                     "Failed to parse amount `%s' at %u\n",
+                     cmd->details.admin_add_incoming.amount,
+                     is->ip);
+         fail (is);
+         return;
+       }
+
+       subject
+         = GNUNET_STRINGS_data_to_string_alloc (&reserve_pub,
+                                                sizeof (reserve_pub));
+       auth.method = TALER_BANK_AUTH_BASIC;
+       /* TODO: obtain authentication details from configuration */
+       auth.details.basic.username = "admin";
+       auth.details.basic.password = "x";
+       cmd->details.admin_add_incoming.aih
+         = TALER_BANK_admin_add_incoming (ctx,
+                                          bank_url,
+                                          &auth,
+                                          exchange_url,
+                                          subject,
+                                          &amount,
+                                          
cmd->details.admin_add_incoming.debit_account_no,
+                                          
cmd->details.admin_add_incoming.credit_account_no,
+                                          &add_incoming_cb,
+                                          is);
+       GNUNET_free (subject);
+       if (NULL == cmd->details.admin_add_incoming.aih)
+       {
+         GNUNET_break (0);
+         fail (is);
+         return;
+       }
+       return;
       }
-
-      execution_date = GNUNET_TIME_absolute_get ();
-      GNUNET_TIME_round_abs (&execution_date);
-      sender_details = json_loads 
(cmd->details.admin_add_incoming.sender_details,
-                                   JSON_REJECT_DUPLICATES,
-                                   NULL);
-      if (NULL == sender_details)
+    case OC_RUN_WIREWATCH:
       {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Failed to parse sender details `%s' at %u\n",
-                    cmd->details.admin_add_incoming.sender_details,
-                    is->ip);
-        fail (is);
-        return;
+       const struct GNUNET_DISK_FileHandle *pr;
+
+       cmd->details.run_wirewatch.wirewatch_proc
+         = GNUNET_OS_start_process (GNUNET_NO,
+                                    GNUNET_OS_INHERIT_STD_ALL,
+                                    NULL, NULL, NULL,
+                                    "taler-exchange-wirewatch",
+                                    "taler-exchange-wirewatch",
+                                    "-c", cfgfilename,
+                                    "-t", "test", /* use Taler's bank/fakebank 
*/
+                                    "-T", /* exit when done */
+                                    NULL);
+       if (NULL == cmd->details.run_wirewatch.wirewatch_proc)
+       {
+         GNUNET_break (0);
+         fail (is);
+         return;
+       }
+       pr = GNUNET_DISK_pipe_handle (sigpipe,
+                                     GNUNET_DISK_PIPE_END_READ);
+       cmd->details.run_wirewatch.child_death_task
+         = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+                                           pr,
+                                           &maint_child_death,
+                                            is);
+       return;
       }
-      json_object_set_new (sender_details,
-                           "bank_uri",
-                           json_string (bank_uri));
-
-      transfer_details = json_loads 
(cmd->details.admin_add_incoming.transfer_details,
-                                     JSON_REJECT_DUPLICATES,
-                                     NULL);
-
-      if (NULL == transfer_details)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Failed to parse transfer details `%s' at %u\n",
-                    cmd->details.admin_add_incoming.transfer_details,
-                    is->ip);
-        fail (is);
-        return;
-      }
-      cmd->details.admin_add_incoming.aih
-        = TALER_EXCHANGE_admin_add_incoming (exchange,
-                                             exchange_uri_admin,
-                                             &reserve_pub,
-                                             &amount,
-                                             execution_date,
-                                             sender_details,
-                                             transfer_details,
-                                             &add_incoming_cb,
-                                             is);
-      json_decref (sender_details);
-      json_decref (transfer_details);
-      if (NULL == cmd->details.admin_add_incoming.aih)
-      {
-        GNUNET_break (0);
-        fail (is);
-        return;
-      }
-      return;
-
     case OC_WITHDRAW_SIGN:
       GNUNET_assert (NULL !=
-                     cmd->details.reserve_withdraw.reserve_reference);
+                    cmd->details.reserve_withdraw.reserve_reference);
       ref = find_command (is,
                           cmd->details.reserve_withdraw.reserve_reference);
       GNUNET_assert (NULL != ref);
@@ -1408,6 +1572,11 @@ do_shutdown (void *cls)
     GNUNET_CURL_gnunet_rc_destroy (rc);
     rc = NULL;
   }
+  if (NULL != fakebank)
+  {
+    TALER_FAKEBANK_stop (fakebank);
+    fakebank = NULL;
+  }
 }
 
 
@@ -1422,12 +1591,15 @@ do_shutdown (void *cls)
  * formatted string.
  */
 static char *
-concat_amount (char *currency, char *rpart)
+concat_amount (const char *currency,
+              const char *rpart)
 {
   char *str;
 
-  GNUNET_asprintf (&str, "%s:%s",
-                   currency, rpart);
+  GNUNET_asprintf (&str,
+                  "%s:%s",
+                   currency,
+                  rpart);
   return str;
 }
 
@@ -1445,25 +1617,27 @@ run_test ()
     { .oc = OC_ADMIN_ADD_INCOMING,
       .label = "create-reserve-1",
       .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.sender_details = "{ \"type\":\"test\", 
\"account_number\":62, \"uuid\":1 }",
-      .details.admin_add_incoming.transfer_details = "{ \"uuid\": 1}",
+      .details.admin_add_incoming.debit_account_no = 62,
+      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
       .details.admin_add_incoming.amount = concat_amount (currency, "5.01") },
 
     /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */
     { .oc = OC_ADMIN_ADD_INCOMING,
       .label = "create-reserve-2",
       .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.sender_details = "{ \"type\":\"test\", 
\"account_number\":62, \"uuid\":1 }",
-      .details.admin_add_incoming.transfer_details = "{ \"uuid\": 1}",
+      .details.admin_add_incoming.debit_account_no = 62,
+      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
       .details.admin_add_incoming.amount = concat_amount (currency, "5.01") },
     /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */
     { .oc = OC_ADMIN_ADD_INCOMING,
       .label = "create-reserve-3",
       .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.sender_details = "{ \"type\":\"test\", 
\"account_number\":62, \"uuid\":1 }",
-      .details.admin_add_incoming.transfer_details = "{ \"uuid\": 1}",
+      .details.admin_add_incoming.debit_account_no = 62,
+      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
       .details.admin_add_incoming.amount = concat_amount (currency, "5.01") },
-
+    { .oc = OC_RUN_WIREWATCH,
+      .label = "run-wirewatch-1"
+    },
     /* Withdraw a 5 EUR coin, at fee of 1 ct */
     { .oc = OC_WITHDRAW_SIGN,
       .label = "withdraw-coin-1",
@@ -1544,7 +1718,7 @@ run_test ()
   GNUNET_assert (NULL != ctx);
   rc = GNUNET_CURL_gnunet_rc_create (ctx);
   exchange = TALER_EXCHANGE_connect (ctx,
-                                     exchange_uri,
+                                     exchange_url,
                                      &cert_cb,
                                      is,
                                      TALER_EXCHANGE_OPTION_END);
@@ -1572,10 +1746,23 @@ run (void *cls,
   unsigned int cnt;
   char *wget_cmd;
 
-  if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (config,
-                                                              
"payments-generator",
-                                                              "exchange",
-                                                              &exchange_uri))
+  cfgfilename = GNUNET_strdup (cfgfile);
+  if (! remote_bank)
+  {
+    /* TODO: do not hard-code port, find in cfg */
+    fakebank = TALER_FAKEBANK_start (8888);
+    if (NULL == fakebank)
+    {
+      fprintf (stderr,
+              "Failed to launch fakebank\n");
+      GNUNET_SCHEDULER_shutdown ();
+    }
+  }
+  if (GNUNET_SYSERR ==
+      GNUNET_CONFIGURATION_get_value_string (config,
+                                            "payments-generator",
+                                            "exchange",
+                                            &exchange_url))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                "payments-generator",
@@ -1583,10 +1770,11 @@ run (void *cls,
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
-  if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (config,
-                                                              
"payments-generator",
-                                                              "exchange_admin",
-                                                              
&exchange_uri_admin))
+  if (GNUNET_SYSERR ==
+      GNUNET_CONFIGURATION_get_value_string (config,
+                                            "payments-generator",
+                                            "exchange_admin",
+                                            &exchange_url_admin))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                "payments-generator",
@@ -1595,10 +1783,11 @@ run (void *cls,
     return;
   }
 
-  if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (config,
-                                                              
"payments-generator",
-                                                              "merchant",
-                                                              &merchant_uri))
+  if (GNUNET_SYSERR ==
+      GNUNET_CONFIGURATION_get_value_string (config,
+                                            "payments-generator",
+                                            "merchant",
+                                            &merchant_url))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                "payments-generator",
@@ -1607,12 +1796,12 @@ run (void *cls,
     return;
   }
 
-  if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (config,
-                                                              
"payments-generator",
-                                                              "bank",
-                                                              &bank_uri))
+  if (GNUNET_SYSERR ==
+      GNUNET_CONFIGURATION_get_value_string (config,
+                                            "payments-generator",
+                                            "bank",
+                                            &bank_url))
   {
-
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                "payments-generator",
                                "bank");
@@ -1620,10 +1809,11 @@ run (void *cls,
     return;
   }
 
-  if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (config,
-                                                              
"payments-generator",
-                                                              "instance",
-                                                              &instance))
+  if (GNUNET_SYSERR ==
+      GNUNET_CONFIGURATION_get_value_string (config,
+                                            "payments-generator",
+                                            "instance",
+                                            &instance))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                "payments-generator",
@@ -1632,12 +1822,12 @@ run (void *cls,
     return;
   }
 
-  if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (config,
-                                                              
"payments-generator",
-                                                              "currency",
-                                                              &currency))
+  if (GNUNET_SYSERR ==
+      GNUNET_CONFIGURATION_get_value_string (config,
+                                            "payments-generator",
+                                            "currency",
+                                            &currency))
   {
-
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                "payments-generator",
                                "currency");
@@ -1645,7 +1835,7 @@ run (void *cls,
     return;
   }
 
-  if (!remote_exchange)
+  if (! remote_exchange)
   {
     exchanged = GNUNET_OS_start_process (GNUNET_NO,
                                          GNUNET_OS_INHERIT_STD_ALL,
@@ -1665,7 +1855,9 @@ run (void *cls,
              "Waiting for taler-exchange-httpd to be ready\n");
     cnt = 0;
 
-    GNUNET_asprintf (&wget_cmd, "wget -q -t 1 -T 1 %skeys -o /dev/null -O 
/dev/null", exchange_uri);
+    GNUNET_asprintf (&wget_cmd,
+                    "wget -q -t 1 -T 1 %skeys -o /dev/null -O /dev/null",
+                    exchange_url);
 
     do
       {
@@ -1707,7 +1899,6 @@ run (void *cls,
                               SIGKILL);
       GNUNET_OS_process_wait (exchanged);
       GNUNET_OS_process_destroy (exchanged);
-
       GNUNET_SCHEDULER_shutdown ();
       return;
     }
@@ -1715,7 +1906,9 @@ run (void *cls,
     fprintf (stderr,
              "Waiting for taler-merchant-httpd to be ready\n");
     cnt = 0;
-    GNUNET_asprintf (&wget_cmd, "wget -q -t 1 -T 1 %s -o /dev/null -O 
/dev/null", merchant_uri);
+    GNUNET_asprintf (&wget_cmd,
+                     "wget -q -t 1 -T 1 %s -o /dev/null -O /dev/null",
+                     merchant_url);
 
     do
       {
@@ -1734,7 +1927,6 @@ run (void *cls,
                                   SIGKILL);
           GNUNET_OS_process_wait (exchanged);
           GNUNET_OS_process_destroy (exchanged);
-
           GNUNET_SCHEDULER_shutdown ();
           return;
         }
@@ -1743,6 +1935,10 @@ run (void *cls,
     fprintf (stderr, "\n");
     GNUNET_free (wget_cmd);
   }
+
+  shc_chld = GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD,
+                                            &sighandler_child_death);
+
   /* timeout, given 60s + 5s per command, which should be more
      than enough */
   timeout_task
@@ -1760,13 +1956,16 @@ int
 main (int argc,
       char *argv[])
 {
-  struct GNUNET_SIGNAL_Context *shc_chld;
   struct GNUNET_GETOPT_CommandLineOption options[] = {
     GNUNET_GETOPT_option_uint ('n',
                                "times",
                                "TIMES",
                                "How many times the commands should be run.",
                                &times),
+    GNUNET_GETOPT_option_flag ('b',
+                               "remote-bank",
+                               "Do not start fakebank",
+                               &remote_bank),
     GNUNET_GETOPT_option_flag ('e',
                                "remote-exchange",
                                "Do not fork any exchange",
@@ -1784,11 +1983,11 @@ main (int argc,
                     "DEBUG",
                     NULL);
   result = GNUNET_SYSERR;
+
   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);
+
   if (GNUNET_OK !=
       GNUNET_PROGRAM_run (argc, argv,
                           "taler-merchant-generate-payments",
@@ -1797,9 +1996,11 @@ main (int argc,
                           &run, NULL))
     return 77;
 
-
-  GNUNET_SIGNAL_handler_uninstall (shc_chld);
-  shc_chld = NULL;
+  if (NULL != shc_chld)
+  {
+    GNUNET_SIGNAL_handler_uninstall (shc_chld);
+    shc_chld = NULL;
+  }
   GNUNET_DISK_pipe_close (sigpipe);
   if (!remote_merchant && NULL != merchantd)
   {
diff --git a/src/merchant-tools/taler-merchant-tip-enable.c 
b/src/merchant-tools/taler-merchant-tip-enable.c
deleted file mode 100644
index e6e5201..0000000
--- a/src/merchant-tools/taler-merchant-tip-enable.c
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2017 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 merchant-tools/taler-merchant-tip-enable.c
- * @brief enable tips by telling the backend that a reserve was charged
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <taler/taler_util.h>
-#include <gnunet/gnunet_util_lib.h>
-#include "taler_merchant_service.h"
-#include <microhttpd.h> /* just for HTTP status code, no need to link against 
*/
-
-/**
- * Return value from main().
- */
-static int global_ret;
-
-/**
- * -a option: how much money was deposited into the reserve
- */
-static struct TALER_Amount amount;
-
-/**
- * For which instance did we charge the reserve?
- */
-static char *instance;
-
-/**
- * Under which URI does the backend run?
- */
-static char *backend_uri;
-
-/**
- * UUID of the wire transfer.
- */
-static char *credit_uuid;
-
-/**
- * Expiration time for the reserve.
- */
-static struct GNUNET_TIME_Absolute expiration;
-
-/**
- * Main execution context for the main loop of the exchange.
- */
-static struct GNUNET_CURL_Context *ctx;
-
-/**
- * Context for running the #ctx's event loop.
- */
-static struct GNUNET_CURL_RescheduleContext *rc;
-
-/**
- * Handle for the /tip-enable operation.
- */
-static struct TALER_MERCHANT_TipEnableOperation *teo;
-
-
-/**
- * Function run when the test terminates (good or bad).
- * Cleans up our state.
- *
- * @param cls the interpreter state.
- */
-static void
-do_shutdown (void *cls)
-{
-  if (NULL != teo)
-  {
-    TALER_MERCHANT_tip_enable_cancel (teo);
-    teo = NULL;
-  }
-  if (NULL != ctx)
-  {
-    GNUNET_CURL_fini (ctx);
-    ctx = NULL;
-  }
-  if (NULL != rc)
-  {
-    GNUNET_CURL_gnunet_rc_destroy (rc);
-    rc = NULL;
-  }
-}
-
-
-/**
- * Callback for a /tip-enable request.  Returns the result of
- * the operation.
- *
- * @param cls closure
- * @param http_status HTTP status returned by the merchant backend
- * @param ec taler-specific error code
- */
-static void
-enable_cb (void *cls,
-           unsigned int http_status,
-           enum TALER_ErrorCode ec)
-{
-  teo = NULL;
-  GNUNET_SCHEDULER_shutdown ();
-  if ( (MHD_HTTP_OK == http_status) &&
-       (TALER_EC_NONE == ec) )
-  {
-    global_ret = 0;
-    return;
-  }
-  fprintf (stderr,
-           "Failed with HTTP status %u and error code %u\n",
-           http_status,
-           ec);
-  global_ret = 3;
-}
-
-
-/**
- * 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_ReservePrivateKeyP reserve_priv;
-  struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
-  char *section;
-  char *tip_reserves;
-  struct GNUNET_HashCode hcredit_uuid;
-  struct GNUNET_CURL_Context *ctx;
-
-  GNUNET_asprintf (&section,
-                   "merchant-instance-%s",
-                   instance);
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (cfg,
-                                               section,
-                                               "TIP_RESERVE_PRIV_FILENAME",
-                                               &tip_reserves))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               section,
-                               "TIP_RESERVE_PRIV_FILENAME");
-    GNUNET_free (section);
-    global_ret = 1;
-    return;
-  }
-  pk = GNUNET_CRYPTO_eddsa_key_create_from_file (tip_reserves);
-  if (NULL == pk)
-  {
-    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                               section,
-                               "TIP_RESERVE_PRIV_FILENAME",
-                               "Failed to read private key");
-    GNUNET_free (section);
-    GNUNET_free (tip_reserves);
-    global_ret = 1;
-    return;
-  }
-  GNUNET_free (tip_reserves);
-  GNUNET_free (section);
-
-  GNUNET_CRYPTO_hash (credit_uuid,
-                      strlen (credit_uuid),
-                      &hcredit_uuid);
-
-  ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
-                          &rc);
-  if (NULL == ctx)
-  {
-    GNUNET_break (0);
-    global_ret = 1;
-    return;
-  }
-  rc = GNUNET_CURL_gnunet_rc_create (ctx);
-  reserve_priv.eddsa_priv = *pk;
-  GNUNET_free (pk);
-  teo = TALER_MERCHANT_tip_enable (ctx,
-                                   backend_uri,
-                                   &amount,
-                                   expiration,
-                                   &reserve_priv,
-                                   &hcredit_uuid,
-                                   &enable_cb,
-                                   NULL);
-  GNUNET_assert (NULL != teo);
-  GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
-                                 NULL);
-}
-
-
-/**
- * The main function of the database initialization tool.
- * Used to initialize the Taler Exchange's database.
- *
- * @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)
-{
-  struct GNUNET_GETOPT_CommandLineOption options[] = {
-    GNUNET_GETOPT_option_mandatory
-    (TALER_getopt_get_amount ('a',
-                              "amount",
-                              "VALUE",
-                              "value that was added to the reserve",
-                              &amount)),
-    GNUNET_GETOPT_option_mandatory
-    (GNUNET_GETOPT_option_string ('b',
-                                  "backend",
-                                  "URI",
-                                  "URI of the backend to use",
-                                  &backend_uri)),
-    GNUNET_GETOPT_option_mandatory
-    (GNUNET_GETOPT_option_string ('C',
-                                  "credit-uuid",
-                                  "UUID",
-                                  "unique identifier of the wire transfer (to 
detect duplicate invocations)",
-                                  &credit_uuid)),
-    GNUNET_GETOPT_option_mandatory
-    (GNUNET_GETOPT_option_absolute_time ('e',
-                                         "expiration",
-                                         "TIMESTAMP",
-                                         "when does the reserve expire",
-                                         &expiration)),
-    GNUNET_GETOPT_option_mandatory
-    (GNUNET_GETOPT_option_string ('i',
-                                  "instance",
-                                  "NAME",
-                                  "name of the instance of which the reserve 
was charged",
-                                  &instance)),
-    GNUNET_GETOPT_OPTION_END
-  };
-
-  /* force linker to link against libtalerutil; if we do
-     not do this, the linker may "optimize" libtalerutil
-     away and skip #TALER_OS_init(), which we do need */
-  (void) TALER_project_data_default ();
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_log_setup ("taler-merchant-tip-enable",
-                                   "INFO",
-                                   NULL));
-  global_ret = 2;
-  if (GNUNET_OK !=
-      GNUNET_PROGRAM_run (argc, argv,
-                          "taler-merchant-tip-enable",
-                         "Enable tipping by telling the backend that a reserve 
was charged",
-                         options,
-                         &run,
-                          NULL))
-    return 1;
-  return global_ret;
-}
-
-
-/* end of taler-exchange-tip-enable.c */

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



reply via email to

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