gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] branch master updated: expand merchant order logic to r


From: gnunet
Subject: [taler-merchant] branch master updated: expand merchant order logic to return exchanges trusted by the merchant and indicate priority based on exchange /wire respons availability and restrictions
Date: Wed, 03 May 2023 15:46:10 +0200

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

grothoff pushed a commit to branch master
in repository merchant.

The following commit(s) were added to refs/heads/master by this push:
     new 634fc309 expand merchant order logic to return exchanges trusted by 
the merchant and indicate priority based on exchange /wire respons availability 
and restrictions
634fc309 is described below

commit 634fc3090574ca303d0fe30ad3189c889782d2cb
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Wed May 3 15:46:00 2023 +0200

    expand merchant order logic to return exchanges trusted by the merchant and 
indicate priority based on exchange /wire respons availability and restrictions
---
 src/backend/Makefile.am                            |    1 -
 src/backend/taler-merchant-httpd.c                 |   13 +-
 src/backend/taler-merchant-httpd_auditors.c        |  319 -----
 src/backend/taler-merchant-httpd_auditors.h        |   76 --
 src/backend/taler-merchant-httpd_exchanges.c       |  400 +++++-
 src/backend/taler-merchant-httpd_exchanges.h       |   43 +-
 src/backend/taler-merchant-httpd_get-orders-ID.c   |  114 +-
 src/backend/taler-merchant-httpd_get-tips-ID.c     |   97 +-
 src/backend/taler-merchant-httpd_helper.c          |  134 +-
 src/backend/taler-merchant-httpd_helper.h          |   34 +
 .../taler-merchant-httpd_post-orders-ID-abort.c    |    7 +-
 .../taler-merchant-httpd_post-orders-ID-pay.c      |   58 +-
 .../taler-merchant-httpd_post-orders-ID-refund.c   |    7 +-
 .../taler-merchant-httpd_post-tips-ID-pickup.c     |   10 +-
 ...r-merchant-httpd_private-get-instances-ID-kyc.c |    6 +-
 .../taler-merchant-httpd_private-get-orders-ID.c   |    3 +-
 ...-merchant-httpd_private-post-orders-ID-refund.c |   39 +-
 .../taler-merchant-httpd_private-post-orders.c     | 1377 ++++++++++----------
 .../taler-merchant-httpd_private-post-reserves.c   |    5 +-
 .../taler-merchant-httpd_private-post-transfers.c  |    7 +-
 src/backend/taler-merchant-httpd_reserves.c        |    5 +-
 src/testing/test_merchant_api.conf                 |   25 -
 22 files changed, 1292 insertions(+), 1488 deletions(-)

diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index d5bcda16..9da9164e 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -22,7 +22,6 @@ bin_PROGRAMS = \
 
 taler_merchant_httpd_SOURCES = \
   taler-merchant-httpd.c taler-merchant-httpd.h \
-  taler-merchant-httpd_auditors.c taler-merchant-httpd_auditors.h \
   taler-merchant-httpd_config.c taler-merchant-httpd_config.h \
   taler-merchant-httpd_exchanges.c taler-merchant-httpd_exchanges.h \
   taler-merchant-httpd_get-orders-ID.c \
diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index 8f455f58..a676a009 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -28,7 +28,6 @@
 #include <taler/taler_mhd_lib.h>
 #include <taler/taler_templating_lib.h>
 #include <taler/taler_exchange_service.h>
-#include "taler-merchant-httpd_auditors.h"
 #include "taler-merchant-httpd_config.h"
 #include "taler-merchant-httpd_exchanges.h"
 #include "taler-merchant-httpd_get-orders-ID.h"
@@ -313,7 +312,6 @@ do_shutdown (void *cls)
     TMH_db = NULL;
   }
   TMH_EXCHANGES_done ();
-  TMH_AUDITORS_done ();
   if (NULL != TMH_by_id_map)
   {
     GNUNET_CONTAINER_multihashmap_iterate (TMH_by_id_map,
@@ -1892,7 +1890,6 @@ run (void *cls,
   int fh;
   enum TALER_MHD_GlobalOptions go;
   int elen;
-  int alen;
   const char *tok;
 
   (void) cls;
@@ -1966,16 +1963,10 @@ run (void *cls,
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
-  alen = TMH_AUDITORS_init (config);
-  if (GNUNET_SYSERR == alen)
-  {
-    GNUNET_SCHEDULER_shutdown ();
-    return;
-  }
-  if (0 == elen + alen)
+  if (0 == elen)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Fatal: no trusted exchanges and no trusted auditors 
configured. Exiting.\n");
+                "Fatal: no trusted exchanges configured. Exiting.\n");
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
diff --git a/src/backend/taler-merchant-httpd_auditors.c 
b/src/backend/taler-merchant-httpd_auditors.c
deleted file mode 100644
index 8a09af63..00000000
--- a/src/backend/taler-merchant-httpd_auditors.c
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
-  This file is part of TALER
-  (C) 2014-2023 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 taler-merchant-httpd_auditors.c
- * @brief logic this HTTPD keeps for each exchange we interact with
- * @author Marcello Stanisci
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <taler/taler_json_lib.h>
-#include "taler-merchant-httpd_auditors.h"
-
-
-/**
- * Auditors are currently not a supported feature, as having merchants
- * allow all exchanges of an auditor creates problems when exchanges
- * have restricted their bank accounts via ``/wire``. Thus, for now,
- * merchants must specify the exact list of trusted exchanges.
- */
-#define ENABLE_AUDITORS 0
-
-/**
- * Our representation of an auditor.
- */
-struct Auditor
-{
-  /**
-   * Auditor's legal name.
-   */
-  char *name;
-
-  /**
-   * Auditor's URL.
-   */
-  char *url;
-
-  /**
-   * Public key of the auditor.
-   */
-  struct TALER_AuditorPublicKeyP public_key;
-
-};
-
-
-#if ENABLE_AUDITORS
-/**
- * Array of the auditors this merchant is willing to accept.
- */
-static struct Auditor *auditors;
-
-/**
- * The length of the #auditors array.
- */
-static unsigned int nauditors;
-
-#endif
-
-/**
- * JSON representation of the auditors accepted by this exchange.
- */
-json_t *j_auditors;
-
-enum GNUNET_GenericReturnValue
-TMH_AUDITORS_check_dk (struct TALER_EXCHANGE_Handle *mh,
-                       const struct TALER_EXCHANGE_DenomPublicKey *dk,
-                       bool exchange_trusted,
-                       unsigned int *hc,
-                       enum TALER_ErrorCode *ec)
-{
-  const struct TALER_EXCHANGE_Keys *keys;
-
-  if (GNUNET_TIME_absolute_is_past (dk->expire_deposit.abs_time))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Denomination key offered by client has expired for 
deposits\n");
-    *hc = MHD_HTTP_GONE;
-    *ec = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_DEPOSIT_EXPIRED;
-    return GNUNET_SYSERR; /* expired */
-  }
-  if (exchange_trusted)
-  {
-    *ec = TALER_EC_NONE;
-    *hc = MHD_HTTP_OK;
-    return GNUNET_OK;
-  }
-  keys = TALER_EXCHANGE_get_keys (mh);
-  if (NULL == keys)
-  {
-    /* this should never happen, keys should have been successfully
-       obtained before we even got into this function */
-    GNUNET_break (0);
-    *ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
-    *hc = MHD_HTTP_INTERNAL_SERVER_ERROR;
-    return GNUNET_SYSERR;
-  }
-#if ENABLE_AUDITORS
-  for (unsigned int i = 0; i<keys->num_auditors; i++)
-  {
-    const struct TALER_EXCHANGE_AuditorInformation *ai = &keys->auditors[i];
-
-    for (unsigned int j = 0; j<nauditors; j++)
-    {
-      if (0 == GNUNET_memcmp (&ai->auditor_pub,
-                              &auditors[j].public_key))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                    "Found supported auditor `%s' (%s)\n",
-                    auditors[j].name,
-                    TALER_B2S (&auditors[j].public_key));
-      }
-      for (unsigned int k = 0; k<ai->num_denom_keys; k++)
-        if (&keys->denom_keys[k] == dk)
-        {
-          *ec = TALER_EC_NONE;
-          *hc = MHD_HTTP_OK;
-          return GNUNET_OK;
-        }
-    }
-  }
-#endif
-  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-              "Denomination key %s offered by client not audited by any 
accepted auditor\n",
-              GNUNET_h2s (&dk->h_key.hash));
-  *hc = MHD_HTTP_BAD_REQUEST;
-  *ec = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_AUDITOR_FAILURE;
-  return GNUNET_NO;
-}
-
-
-#if ENABLE_AUDITORS
-/**
- * Function called on each configuration section. Finds sections
- * about auditors and parses the entries.
- *
- * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *`
- * @param section name of the section
- */
-static void
-parse_auditors (void *cls,
-                const char *section)
-{
-  const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
-  char *pks;
-  struct Auditor auditor;
-  char *currency;
-
-  if (0 != strncasecmp (section,
-                        "merchant-auditor-",
-                        strlen ("merchant-auditor-")))
-    return;
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (cfg,
-                                             section,
-                                             "CURRENCY",
-                                             &currency))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               section,
-                               "CURRENCY");
-    return;
-  }
-  if (0 != strcasecmp (currency,
-                       TMH_currency))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Auditor given in section `%s' is for another currency. 
Skipping.\n",
-                section);
-    GNUNET_free (currency);
-    return;
-  }
-  GNUNET_free (currency);
-  auditor.name = GNUNET_strdup (&section[strlen ("merchant-auditor-")]);
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (cfg,
-                                             section,
-                                             "AUDITOR_BASE_URL",
-                                             &auditor.url))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               section,
-                               "URL");
-    GNUNET_free (auditor.name);
-    return;
-  }
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (cfg,
-                                             section,
-                                             "AUDITOR_KEY",
-                                             &pks))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               section,
-                               "AUDITOR_KEY");
-    GNUNET_free (auditor.name);
-    GNUNET_free (auditor.url);
-    return;
-  }
-  if (GNUNET_OK !=
-      GNUNET_CRYPTO_eddsa_public_key_from_string (pks,
-                                                  strlen (pks),
-                                                  
&auditor.public_key.eddsa_pub))
-  {
-    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                               section,
-                               "AUDITOR_KEY",
-                               "need a valid EdDSA public key");
-    GNUNET_free (auditor.name);
-    GNUNET_free (auditor.url);
-    GNUNET_free (pks);
-    return;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Loaded key data of auditor `%s' (%s)\n",
-              auditor.name,
-              TALER_B2S (&auditor.public_key));
-  GNUNET_free (pks);
-  GNUNET_array_append (auditors,
-                       nauditors,
-                       auditor);
-}
-
-
-#endif
-
-
-int
-TMH_AUDITORS_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
-#if ENABLE_AUDITORS
-  GNUNET_CONFIGURATION_iterate_sections (cfg,
-                                         &parse_auditors,
-                                         (void *) cfg);
-#endif
-  /* Generate preferred exchange(s) array. */
-  j_auditors = json_array ();
-#if ENABLE_AUDITORS
-  for (unsigned int cnt = 0; cnt < nauditors; cnt++)
-    GNUNET_assert (0 ==
-                   json_array_append_new (
-                     j_auditors,
-                     GNUNET_JSON_PACK (
-                       GNUNET_JSON_pack_string ("name",
-                                                auditors[cnt].name),
-                       GNUNET_JSON_pack_data_auto ("auditor_pub",
-                                                   &auditors[cnt].public_key),
-                       GNUNET_JSON_pack_string ("url",
-                                                auditors[cnt].url))));
-  return nauditors;
-#else
-  return 0;
-#endif
-}
-
-
-/**
- * Release auditor information state.
- */
-void
-TMH_AUDITORS_done ()
-{
-  json_decref (j_auditors);
-  j_auditors = NULL;
-#if ENABLE_AUDITORS
-  for (unsigned int i = 0; i<nauditors; i++)
-  {
-    GNUNET_free (auditors[i].name);
-    GNUNET_free (auditors[i].url);
-  }
-  GNUNET_free (auditors);
-  auditors = NULL;
-  nauditors = 0;
-#endif
-}
-
-
-#if ENABLE_AUDITORS
-
-/*
-Something like the following text should be added to the taler.5.conf man-page
-if auditor support is brought back.
-
-KNOWN AUDITORS (for merchants)
-------------------------------
-
-The merchant configuration can include a list of known exchanges if the
-merchant wants to specify that certain auditors are explicitly trusted.
-For each trusted exchange, a section “[merchant-auditor-$NAME]” must exist, 
where
-``$NAME`` is a merchant-given name for the auditor. The following options
-must be given in each “[merchant-auditor-$NAME]” section.
-
-AUDITOR_BASE_URL
-  Base URL of the auditor, e.g. “https://auditor.demo.taler.net/”
-
-AUDITOR_KEY
-  Crockford Base32 encoded auditor public key.
-
-CURRENCY
-  Name of the currency for which this auditor is trusted, e.g. “KUDOS”
-  The entire section is ignored if the currency does not match the currency
-  we use, which must be given in the ``[taler]`` section.
-
-*/
-
-#endif
-
-/* end of taler-merchant-httpd_auditors.c */
diff --git a/src/backend/taler-merchant-httpd_auditors.h 
b/src/backend/taler-merchant-httpd_auditors.h
deleted file mode 100644
index 1d66c801..00000000
--- a/src/backend/taler-merchant-httpd_auditors.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-  This file is part of TALER
-  (C) 2014, 2015 GNUnet e.V. and INRIA
-
-  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 taler-merchant-httpd_auditors.h
- * @brief logic this HTTPD keeps for each exchange we interact with
- * @author Marcello Stanisci
- * @author Christian Grothoff
- */
-#ifndef TALER_MERCHANT_HTTPD_AUDITORS_H
-#define TALER_MERCHANT_HTTPD_AUDITORS_H
-
-#include <jansson.h>
-#include <gnunet/gnunet_util_lib.h>
-#include <taler/taler_util.h>
-#include <taler/taler_exchange_service.h>
-#include "taler-merchant-httpd.h"
-
-
-/**
- * JSON representation of the auditors accepted by this exchange.
- */
-extern json_t *j_auditors;
-
-
-/**
- * Parses auditor information from the configuration.
- *
- * @param cfg the configuration
- * @return the number of auditors found; #GNUNET_SYSERR upon error in
- *          parsing.
- */
-int
-TMH_AUDITORS_init (const struct GNUNET_CONFIGURATION_Handle *cfg);
-
-
-/**
- * Check if the given @a dk issued by exchange @a mh is audited by
- * an auditor that is acceptable for this merchant. (And if the
- * denomination is not yet expired or something silly like that.)
- *
- * @param mh exchange issuing @a dk
- * @param dk a denomination issued by @a mh
- * @param exchange_trusted true if the exchange of @a dk is trusted by config
- * @param[out] hc set to the HTTP status code to return
- * @param[out] ec set to the Taler error code to return
- * @return #GNUNET_OK on success
- */
-int
-TMH_AUDITORS_check_dk (struct TALER_EXCHANGE_Handle *mh,
-                       const struct TALER_EXCHANGE_DenomPublicKey *dk,
-                       bool exchange_trusted,
-                       unsigned int *hc,
-                       enum TALER_ErrorCode *ec);
-
-
-/**
- * Release auditor information state.
- */
-void
-TMH_AUDITORS_done (void);
-
-
-#endif
diff --git a/src/backend/taler-merchant-httpd_exchanges.c 
b/src/backend/taler-merchant-httpd_exchanges.c
index f4695184..e415bbf6 100644
--- a/src/backend/taler-merchant-httpd_exchanges.c
+++ b/src/backend/taler-merchant-httpd_exchanges.c
@@ -23,6 +23,7 @@
 #include <taler/taler_json_lib.h>
 #include "taler-merchant-httpd_exchanges.h"
 #include "taler-merchant-httpd.h"
+#include <regex.h>
 
 /**
  * How often do we retry DB transactions with soft errors?
@@ -62,12 +63,6 @@
                                                        (r)), 2));
 
 
-/**
- * Exchange
- */
-struct Exchange;
-
-
 /**
  * Information we keep for a pending #MMH_EXCHANGES_find_exchange() operation.
  */
@@ -97,7 +92,7 @@ struct TMH_EXCHANGES_FindOperation
   /**
    * Exchange we wait for the /keys for.
    */
-  struct Exchange *my_exchange;
+  struct TMH_Exchange *my_exchange;
 
   /**
    * Task scheduled to asynchronously return the result to
@@ -137,21 +132,102 @@ struct FeesByWireMethod
 };
 
 
+/**
+ * Restriction that applies to an exchange account.
+ */
+struct Restriction
+{
+  /**
+   * Kept in a DLL.
+   */
+  struct Restriction *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct Restriction *prev;
+
+  /**
+   * Type of restriction imposed on the account.
+   */
+  enum TALER_EXCHANGE_AccountRestrictionType type;
+
+  /**
+   * Details depending on @e type.
+   */
+  union
+  {
+
+    /**
+     * Accounts must match the given regular expression.
+     */
+    struct
+    {
+
+      /**
+       * Pre-compiled regex.
+       */
+      regex_t ex;
+
+    } regex;
+
+  } details;
+};
+
+
+/**
+ * Information about a bank account of the exchange.
+ */
+struct ExchangeAccount
+{
+  /**
+   * Kept in a DLL.
+   */
+  struct ExchangeAccount *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct ExchangeAccount *prev;
+
+  /**
+   * Wire method of this exchange account.
+   */
+  char *wire_method;
+
+  /**
+   * Currency conversion that applies to this account,
+   * NULL if none.
+   */
+  char *conversion_url;
+
+  /**
+   * Head of DLL of debit restrictions of this account.
+   */
+  struct Restriction *d_head;
+
+  /**
+   * Tail of DLL of debit restrictions of this account.
+   */
+  struct Restriction *d_tail;
+};
+
+
 /**
  * Exchange
  */
-struct Exchange
+struct TMH_Exchange
 {
 
   /**
    * Kept in a DLL.
    */
-  struct Exchange *next;
+  struct TMH_Exchange *next;
 
   /**
    * Kept in a DLL.
    */
-  struct Exchange *prev;
+  struct TMH_Exchange *prev;
 
   /**
    * Head of FOs pending for this exchange.
@@ -163,6 +239,16 @@ struct Exchange
    */
   struct TMH_EXCHANGES_FindOperation *fo_tail;
 
+  /**
+   * Head of accounts of this exchange.
+   */
+  struct ExchangeAccount *acc_head;
+
+  /**
+   * Tail of accounts of this exchange.
+   */
+  struct ExchangeAccount *acc_tail;
+
   /**
    * (base) URL of the exchange.
    */
@@ -261,27 +347,28 @@ static struct GNUNET_CURL_RescheduleContext 
*merchant_curl_rc;
 /**
  * Head of exchanges we know about.
  */
-static struct Exchange *exchange_head;
+static struct TMH_Exchange *exchange_head;
 
 /**
  * Tail of exchanges we know about.
  */
-static struct Exchange *exchange_tail;
+static struct TMH_Exchange *exchange_tail;
 
 /**
- * List of our trusted exchanges for inclusion in contracts.
+ * How many exchanges do we trust (for our configured
+ * currency) as per our configuration? Used for a
+ * sanity-check on startup.
  */
-json_t *TMH_trusted_exchanges;
-
+static int trusted_exchange_count;
 
 /**
  * Function called with information about who is auditing
  * a particular exchange and what key the exchange is using.
  *
- * @param cls closure, will be `struct Exchange` so that
+ * @param cls closure, will be `struct TMH_Exchange` so that
  *   when this function gets called, it will change the flag 'have_keys'
  *   to 'true'. Note: 'keys' is automatically saved inside the exchange's
- *   handle, which is contained inside 'struct Exchange', when
+ *   handle, which is contained inside 'struct TMH_Exchange', when
  *   this callback is called. Thus, once 'have_keys' turns 'true',
  *   it is safe to call 'TALER_EXCHANGE_get_keys()' on the exchange's handle,
  *   in order to get the "good" keys.
@@ -292,6 +379,121 @@ keys_mgmt_cb (void *cls,
               const struct TALER_EXCHANGE_KeysResponse *kr);
 
 
+/**
+ * Free data structures within @a ea, but not @a ea
+ * pointer itself.
+ *
+ * @param[in] ea data structure to free
+ */
+static void
+free_exchange_account (struct ExchangeAccount *ea)
+{
+  struct Restriction *r;
+
+  while (NULL != (r = ea->d_head))
+  {
+    GNUNET_CONTAINER_DLL_remove (ea->d_head,
+                                 ea->d_tail,
+                                 r);
+    switch (r->type)
+    {
+    case TALER_EXCHANGE_AR_INVALID:
+      GNUNET_assert (0);
+      break;
+    case TALER_EXCHANGE_AR_DENY:
+      break;
+    case TALER_EXCHANGE_AR_REGEX:
+      regfree (&r->details.regex.ex);
+      break;
+    }
+    GNUNET_free (r);
+  }
+  GNUNET_free (ea->wire_method);
+  GNUNET_free (ea->conversion_url);
+}
+
+
+/**
+ * Free list of all accounts in @a exchange.
+ *
+ * @param[in,out] exchange entry to free accounts for
+ */
+static void
+purge_exchange_accounts (struct TMH_Exchange *exchange)
+{
+  struct ExchangeAccount *acc;
+
+  while (NULL != (acc = exchange->acc_head))
+  {
+    GNUNET_CONTAINER_DLL_remove (exchange->acc_head,
+                                 exchange->acc_tail,
+                                 acc);
+    free_exchange_account (acc);
+    GNUNET_free (acc);
+  }
+}
+
+
+/**
+ * Set the list of accounts of @a exchange.
+ *
+ * @param[in,out] exchange exchange to initialize or update
+ * @param accounts_len length of @a accounts array
+ * @param accounts array of accounts to convert
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+set_exchange_accounts (struct TMH_Exchange *exchange,
+                       unsigned int accounts_len,
+                       const struct TALER_EXCHANGE_WireAccount *accounts)
+{
+  purge_exchange_accounts (exchange);
+  for (unsigned int i = 0; i<accounts_len; i++)
+  {
+    const struct TALER_EXCHANGE_WireAccount *account = &accounts[i];
+    struct ExchangeAccount *acc;
+
+    acc = GNUNET_new (struct ExchangeAccount);
+    acc->wire_method = TALER_payto_get_method (account->payto_uri);
+    if (NULL != account->conversion_url)
+      acc->conversion_url = GNUNET_strdup (account->conversion_url);
+    GNUNET_CONTAINER_DLL_insert (exchange->acc_head,
+                                 exchange->acc_tail,
+                                 acc);
+    for (unsigned int j = 0; j<account->debit_restrictions_length; j++)
+    {
+      const struct TALER_EXCHANGE_AccountRestriction *ar =
+        &account->debit_restrictions[j];
+      struct Restriction *r;
+
+      r = GNUNET_new (struct Restriction);
+      r->type = ar->type;
+      switch (ar->type)
+      {
+      case TALER_EXCHANGE_AR_INVALID:
+        GNUNET_assert (0);
+        break;
+      case TALER_EXCHANGE_AR_DENY:
+        break;
+      case TALER_EXCHANGE_AR_REGEX:
+        if (0 != regcomp (&r->details.regex.ex,
+                          ar->details.regex.posix_egrep,
+                          REG_NOSUB | REG_EXTENDED))
+        {
+          GNUNET_break_op (0);
+          return GNUNET_SYSERR;
+        }
+        break;
+      }
+      GNUNET_CONTAINER_DLL_insert (acc->d_head,
+                                   acc->d_tail,
+                                   r);
+    }
+  }
+  return GNUNET_OK;
+}
+
+
 /**
  * Retry getting information from the given exchange in
  * the closure.
@@ -301,7 +503,7 @@ keys_mgmt_cb (void *cls,
 static void
 retry_exchange (void *cls)
 {
-  struct Exchange *exchange = cls;
+  struct TMH_Exchange *exchange = cls;
 
   /* might be a scheduled reload and not our first attempt */
   exchange->retry_task = NULL;
@@ -347,7 +549,7 @@ retry_exchange (void *cls)
  * @return #TALER_EC_NONE on success
  */
 static enum TALER_ErrorCode
-process_wire_fees (struct Exchange *exchange,
+process_wire_fees (struct TMH_Exchange *exchange,
                    const struct TALER_MasterPublicKeyP *master_pub,
                    unsigned int num_methods,
                    const struct TALER_EXCHANGE_WireFeesByMethod *fbm)
@@ -533,7 +735,7 @@ add_restriction (json_t *restrictions,
  * @return #TALER_EC_NONE on success
  */
 static enum TALER_ErrorCode
-process_wire_accounts (struct Exchange *exchange,
+process_wire_accounts (struct TMH_Exchange *exchange,
                        const struct TALER_MasterPublicKeyP *master_pub,
                        unsigned int accounts_len,
                        const struct TALER_EXCHANGE_WireAccount *accounts)
@@ -623,6 +825,9 @@ process_wire_accounts (struct Exchange *exchange,
       GNUNET_break (0);
       return TALER_EC_GENERIC_DB_COMMIT_FAILED;
     }
+    set_exchange_accounts (exchange,
+                           accounts_len,
+                           accounts);
     return TALER_EC_NONE;
 outer:;
   }
@@ -639,7 +844,7 @@ outer:;
  * @return NULL if we do not have fees for this method yet
  */
 static const struct FeesByWireMethod *
-get_wire_fees (struct Exchange *exchange,
+get_wire_fees (struct TMH_Exchange *exchange,
                struct GNUNET_TIME_Timestamp now,
                const char *wire_method)
 {
@@ -680,7 +885,7 @@ get_wire_fees (struct Exchange *exchange,
  * @return true if we need /wire data from @a exchange
  */
 static bool
-process_find_operations (struct Exchange *exchange)
+process_find_operations (struct TMH_Exchange *exchange)
 {
   struct TMH_EXCHANGES_FindOperation *fn;
   struct GNUNET_TIME_Timestamp now;
@@ -745,7 +950,7 @@ process_find_operations (struct Exchange *exchange)
     fo->fc (fo->fc_cls,
             &hr,
             exchange->conn,
-            exchange->trusted);
+            exchange);
     fn = fo->next;
     TMH_EXCHANGES_find_exchange_cancel (fo);
   }
@@ -767,14 +972,14 @@ wire_task_cb (void *cls);
  * Must only be called if 'exchange->have_keys' is true.
  * that is #TALER_EXCHANGE_get_keys() will succeed.
  *
- * @param cls closure, a `struct Exchange`
+ * @param cls closure, a `struct TMH_Exchange`
  * @param wr response details
  */
 static void
 handle_wire_data (void *cls,
                   const struct TALER_EXCHANGE_WireResponse *wr)
 {
-  struct Exchange *exchange = cls;
+  struct TMH_Exchange *exchange = cls;
   const struct TALER_EXCHANGE_Keys *keys;
   enum TALER_ErrorCode ecx;
 
@@ -869,12 +1074,12 @@ handle_wire_data (void *cls,
  * Must only be called if 'exchange->have_keys' is true,
  * that is #TALER_EXCHANGE_get_keys() will succeed.
  *
- * @param cls a `struct Exchange` to check
+ * @param cls a `struct TMH_Exchange` to check
  */
 static void
 wire_task_cb (void *cls)
 {
-  struct Exchange *exchange = cls;
+  struct TMH_Exchange *exchange = cls;
 
   exchange->wire_task = NULL;
   GNUNET_assert (exchange->have_keys);
@@ -895,13 +1100,14 @@ wire_task_cb (void *cls)
  * @param[in] exchange entry to free
  */
 static void
-free_exchange_entry (struct Exchange *exchange)
+free_exchange_entry (struct TMH_Exchange *exchange)
 {
   struct FeesByWireMethod *f;
 
   GNUNET_CONTAINER_DLL_remove (exchange_head,
                                exchange_tail,
                                exchange);
+  purge_exchange_accounts (exchange);
   while (NULL != (f = exchange->wire_fees_head))
   {
     struct TALER_EXCHANGE_WireAggregateFees *af;
@@ -952,7 +1158,7 @@ free_exchange_entry (struct Exchange *exchange)
  * @param hr details about the HTTP reply
  */
 static void
-fail_and_retry (struct Exchange *exchange,
+fail_and_retry (struct TMH_Exchange *exchange,
                 const struct TALER_EXCHANGE_HttpResponse *hr)
 {
   struct TMH_EXCHANGES_FindOperation *fo;
@@ -1005,7 +1211,7 @@ static void
 keys_mgmt_cb (void *cls,
               const struct TALER_EXCHANGE_KeysResponse *kr)
 {
-  struct Exchange *exchange = cls;
+  struct TMH_Exchange *exchange = cls;
   struct GNUNET_TIME_Relative delay;
   const struct TALER_EXCHANGE_Keys *keys;
 
@@ -1029,7 +1235,7 @@ keys_mgmt_cb (void *cls,
   if (! exchange->trusted)
   {
     exchange->master_pub = keys->master_pub;
-    for (struct Exchange *e = exchange_head;
+    for (struct TMH_Exchange *e = exchange_head;
          NULL != e;
          e = e->next)
     {
@@ -1112,7 +1318,7 @@ static void
 return_result (void *cls)
 {
   struct TMH_EXCHANGES_FindOperation *fo = cls;
-  struct Exchange *exchange = fo->my_exchange;
+  struct TMH_Exchange *exchange = fo->my_exchange;
 
   fo->at = NULL;
   if ( (process_find_operations (exchange)) &&
@@ -1139,10 +1345,10 @@ return_result (void *cls)
  * @param exchange_url base URL to match against
  * @return NULL if exchange is not yet known
  */
-static struct Exchange *
+static struct TMH_Exchange *
 lookup_exchange (const char *exchange_url)
 {
-  for (struct Exchange *exchange = exchange_head;
+  for (struct TMH_Exchange *exchange = exchange_head;
        NULL != exchange;
        exchange = exchange->next)
     if (0 == strcmp (exchange->url,
@@ -1158,7 +1364,7 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
                              TMH_EXCHANGES_FindContinuation fc,
                              void *fc_cls)
 {
-  struct Exchange *exchange;
+  struct TMH_Exchange *exchange;
   struct TMH_EXCHANGES_FindOperation *fo;
 
   if (NULL == merchant_curl_ctx)
@@ -1174,7 +1380,7 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
   if (NULL == exchange)
   {
     /* This is a new exchange */
-    exchange = GNUNET_new (struct Exchange);
+    exchange = GNUNET_new (struct TMH_Exchange);
     exchange->url = GNUNET_strdup (chosen_exchange);
     GNUNET_CONTAINER_DLL_insert (exchange_head,
                                  exchange_tail,
@@ -1271,7 +1477,7 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
 void
 TMH_EXCHANGES_find_exchange_cancel (struct TMH_EXCHANGES_FindOperation *fo)
 {
-  struct Exchange *exchange = fo->my_exchange;
+  struct TMH_Exchange *exchange = fo->my_exchange;
 
   if (NULL != fo->at)
   {
@@ -1300,7 +1506,7 @@ accept_exchanges (void *cls,
   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
   char *url;
   char *mks;
-  struct Exchange *exchange;
+  struct TMH_Exchange *exchange;
   char *currency;
 
   if (0 != strncasecmp (section,
@@ -1339,7 +1545,7 @@ accept_exchanges (void *cls,
                                "EXCHANGE_BASE_URL");
     return;
   }
-  exchange = GNUNET_new (struct Exchange);
+  exchange = GNUNET_new (struct TMH_Exchange);
   exchange->url = url;
   if (GNUNET_OK ==
       GNUNET_CONFIGURATION_get_value_string (cfg,
@@ -1354,6 +1560,7 @@ accept_exchanges (void *cls,
                                                     eddsa_pub))
     {
       exchange->trusted = true;
+      trusted_exchange_count++;
     }
     else
     {
@@ -1385,22 +1592,17 @@ TMH_EXCHANGES_lookup_wire_fee (const char *exchange_url,
                                const char *wire_method,
                                struct TALER_Amount *wire_fee)
 {
-  struct Exchange *exchange;
+  struct TMH_Exchange *exchange;
   const struct FeesByWireMethod *fbm;
   const struct TALER_EXCHANGE_WireAggregateFees *af;
 
   exchange = lookup_exchange (exchange_url);
   if (NULL == exchange)
   {
-    fprintf (stderr,
-             "No %s yet\n",
-             exchange_url);
     return GNUNET_SYSERR;
   }
   if (! exchange->have_wire)
   {
-    fprintf (stderr,
-             "No wire yet\n");
     return GNUNET_SYSERR;
   }
   fbm = get_wire_fees (exchange,
@@ -1433,25 +1635,7 @@ TMH_EXCHANGES_init (const struct 
GNUNET_CONFIGURATION_Handle *cfg)
                                          &accept_exchanges,
                                          (void *) cfg);
   /* build JSON with list of trusted exchanges (will be included in contracts) 
*/
-  TMH_trusted_exchanges = json_array ();
-  for (struct Exchange *exchange = exchange_head;
-       NULL != exchange;
-       exchange = exchange->next)
-  {
-    json_t *j_exchange;
-
-    if (! exchange->trusted)
-      continue;
-    j_exchange = GNUNET_JSON_PACK (
-      GNUNET_JSON_pack_string ("url",
-                               exchange->url),
-      GNUNET_JSON_pack_data_auto ("master_pub",
-                                  &exchange->master_pub));
-    GNUNET_assert (0 ==
-                   json_array_append_new (TMH_trusted_exchanges,
-                                          j_exchange));
-  }
-  return json_array_size (TMH_trusted_exchanges);
+  return trusted_exchange_count;
 }
 
 
@@ -1470,11 +1654,93 @@ TMH_EXCHANGES_done ()
     GNUNET_CURL_gnunet_rc_destroy (merchant_curl_rc);
     merchant_curl_rc = NULL;
   }
-  if (NULL != TMH_trusted_exchanges)
+}
+
+
+enum GNUNET_GenericReturnValue
+TMH_exchange_check_debit (struct TMH_Exchange *ex,
+                          const struct TMH_WireMethod *wm)
+{
+  for (struct ExchangeAccount *acc = ex->acc_head;
+       NULL != acc;
+       acc = acc->next)
   {
-    json_decref (TMH_trusted_exchanges);
-    TMH_trusted_exchanges = NULL;
+    bool ok = true;
+
+    if (0 != strcmp (acc->wire_method,
+                     wm->wire_method))
+      continue;
+    if (NULL != acc->conversion_url)
+      continue; /* never use accounts with conversion */
+    for (struct Restriction *r = acc->d_head;
+         NULL != r;
+         r = r->next)
+    {
+      switch (r->type)
+      {
+      case TALER_EXCHANGE_AR_INVALID:
+        GNUNET_break (0);
+        ok = false;
+        break;
+      case TALER_EXCHANGE_AR_DENY:
+        ok = false;
+        break;
+      case TALER_EXCHANGE_AR_REGEX:
+        if (0 != regexec (&r->details.regex.ex,
+                          wm->payto_uri,
+                          0, NULL, 0))
+          ok = false;
+        break;
+      }
+      if (! ok)
+        break;
+    }
+
+    if (ok)
+      return GNUNET_OK;
+  }
+  return GNUNET_NO;
+}
+
+
+json_t *
+TMH_exchange_get_acceptable (const struct TMH_WireMethod *wm)
+{
+  json_t *te = json_array ();
+
+  GNUNET_assert (NULL != te);
+  for (struct TMH_Exchange *exchange = exchange_head;
+       NULL != exchange;
+       exchange = exchange->next)
+  {
+    json_t *j_exchange;
+    unsigned int priority;
+
+    if (! exchange->trusted)
+      continue;
+    priority = 512; /* medium */
+    if (exchange->have_wire)
+    {
+      if (GNUNET_OK ==
+          TMH_exchange_check_debit (exchange,
+                                    wm))
+        priority = 1024; /* high */
+      else
+        priority = 0; /* negative response */
+    }
+    j_exchange = GNUNET_JSON_PACK (
+      GNUNET_JSON_pack_string ("url",
+                               exchange->url),
+      GNUNET_JSON_pack_uint64 ("priority",
+                               priority),
+      GNUNET_JSON_pack_data_auto ("master_pub",
+                                  &exchange->master_pub));
+    GNUNET_assert (NULL != j_exchange);
+    GNUNET_assert (0 ==
+                   json_array_append_new (te,
+                                          j_exchange));
   }
+  return te;
 }
 
 
diff --git a/src/backend/taler-merchant-httpd_exchanges.h 
b/src/backend/taler-merchant-httpd_exchanges.h
index 2f4e69fe..23424f40 100644
--- a/src/backend/taler-merchant-httpd_exchanges.h
+++ b/src/backend/taler-merchant-httpd_exchanges.h
@@ -31,9 +31,37 @@
 
 
 /**
- * List of our trusted exchanges in JSON format for inclusion in contracts.
+ * Exchange
  */
-extern json_t *TMH_trusted_exchanges;
+struct TMH_Exchange;
+
+
+/**
+ * Check if we would trust @a ex to deposit funds
+ * into our account @a wm. Checks that both @a ex
+ * is trusted and that @a ex allows wire transfers
+ * into the account given in @a wm.
+ *
+ * @param ex the exchange to check
+ * @param wm the wire method to check with
+ * @return #GNUNET_OK if such a debit can happen
+ */
+enum GNUNET_GenericReturnValue
+TMH_exchange_check_debit (struct TMH_Exchange *ex,
+                          const struct TMH_WireMethod *wm);
+
+
+/**
+ * Return the list of exchanges we would find
+ * acceptable for a payment given the @a wire_method.
+ *
+ * @param wm wire method the payment should be
+ *        made with
+ * @return list of exchanges we accept for @a wire_method
+ *   (and that would accept our bank account)
+ */
+json_t *
+TMH_exchange_get_acceptable (const struct TMH_WireMethod *wm);
 
 
 /**
@@ -61,13 +89,14 @@ TMH_EXCHANGES_done (void);
  * @param cls closure
  * @param hr HTTP response details
  * @param eh handle to the exchange context
- * @param exchange_trusted true if this exchange is trusted by config
+ * @param ih internal handle to the exchange
  */
 typedef void
-(*TMH_EXCHANGES_FindContinuation)(void *cls,
-                                  const struct TALER_EXCHANGE_HttpResponse *hr,
-                                  struct TALER_EXCHANGE_Handle *eh,
-                                  bool exchange_trusted);
+(*TMH_EXCHANGES_FindContinuation)(
+  void *cls,
+  const struct TALER_EXCHANGE_HttpResponse *hr,
+  struct TALER_EXCHANGE_Handle *eh,
+  struct TMH_Exchange *ih);
 
 
 /**
diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.c 
b/src/backend/taler-merchant-httpd_get-orders-ID.c
index ab4de015..498ad1c4 100644
--- a/src/backend/taler-merchant-httpd_get-orders-ID.c
+++ b/src/backend/taler-merchant-httpd_get-orders-ID.c
@@ -28,6 +28,7 @@
 #include <taler/taler_templating_lib.h>
 #include <taler/taler_exchange_service.h>
 #include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_helper.h"
 #include "taler-merchant-httpd_get-orders-ID.h"
 #include "taler-merchant-httpd_mhd.h"
 #include "taler-merchant-httpd_qr.h"
@@ -377,62 +378,26 @@ TMH_make_order_status_url (struct MHD_Connection *con,
                            struct TALER_ClaimTokenP *claim_token,
                            struct TALER_PrivateContractHashP *h_contract)
 {
-  const char *host;
-  const char *forwarded_host;
-  const char *uri_path;
-  struct GNUNET_Buffer buf = { 0 };
+  struct GNUNET_Buffer buf;
   /* Number of query parameters written so far */
   unsigned int num_qp = 0;
 
-  host = MHD_lookup_connection_value (con,
-                                      MHD_HEADER_KIND,
-                                      MHD_HTTP_HEADER_HOST);
-  forwarded_host = MHD_lookup_connection_value (con,
-                                                MHD_HEADER_KIND,
-                                                "X-Forwarded-Host");
-  uri_path = MHD_lookup_connection_value (con,
-                                          MHD_HEADER_KIND,
-                                          "X-Forwarded-Prefix");
-  if (NULL != forwarded_host)
-    host = forwarded_host;
-  if (NULL == host)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  if (NULL != strchr (host, '/'))
-  {
-    GNUNET_break_op (0);
-    return NULL;
-  }
   GNUNET_assert (NULL != instance_id);
   GNUNET_assert (NULL != order_id);
-
-  if (GNUNET_NO == TALER_mhd_is_https (con))
-    GNUNET_buffer_write_str (&buf,
-                             "http://";);
-  else
-    GNUNET_buffer_write_str (&buf,
-                             "https://";);
-  GNUNET_buffer_write_str (&buf,
-                           host);
-  if (NULL != uri_path)
-    GNUNET_buffer_write_path (&buf,
-                              uri_path);
-  if (0 != strcmp ("default",
-                   instance_id))
+  if (GNUNET_OK !=
+      TMH_base_url_by_connection (con,
+                                  instance_id,
+                                  &buf))
   {
-    GNUNET_buffer_write_path (&buf,
-                              "instances");
-    GNUNET_buffer_write_path (&buf,
-                              instance_id);
+    GNUNET_break (0);
+    return NULL;
   }
   GNUNET_buffer_write_path (&buf,
                             "/orders");
   GNUNET_buffer_write_path (&buf,
                             order_id);
-  if ((NULL != claim_token) &&
-      (GNUNET_NO == GNUNET_is_zero (claim_token)))
+  if ( (NULL != claim_token) &&
+       (! GNUNET_is_zero (claim_token)) )
   {
     /* 'token=' for human readability */
     GNUNET_buffer_write_str (&buf,
@@ -480,60 +445,27 @@ TMH_make_taler_pay_uri (struct MHD_Connection *con,
                         const char *instance_id,
                         struct TALER_ClaimTokenP *claim_token)
 {
-  const char *host;
-  const char *forwarded_host;
-  const char *uri_path;
-  struct GNUNET_Buffer buf = { 0 };
+  struct GNUNET_Buffer buf;
 
-  host = MHD_lookup_connection_value (con,
-                                      MHD_HEADER_KIND,
-                                      MHD_HTTP_HEADER_HOST);
-  forwarded_host = MHD_lookup_connection_value (con,
-                                                MHD_HEADER_KIND,
-                                                "X-Forwarded-Host");
-  uri_path = MHD_lookup_connection_value (con,
-                                          MHD_HEADER_KIND,
-                                          "X-Forwarded-Prefix");
-  if (NULL != forwarded_host)
-    host = forwarded_host;
-  if (NULL == host)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  if (NULL != strchr (host, '/'))
-  {
-    GNUNET_break_op (0);
-    return NULL;
-  }
   GNUNET_assert (NULL != instance_id);
   GNUNET_assert (NULL != order_id);
-  GNUNET_buffer_write_str (&buf,
-                           "taler");
-  if (GNUNET_NO == TALER_mhd_is_https (con))
-    GNUNET_buffer_write_str (&buf,
-                             "+http");
-  GNUNET_buffer_write_str (&buf,
-                           "://pay/");
-  GNUNET_buffer_write_str (&buf,
-                           host);
-  if (NULL != uri_path)
-    GNUNET_buffer_write_path (&buf,
-                              uri_path);
-  if (0 != strcmp ("default",
-                   instance_id))
+  if (GNUNET_OK !=
+      TMH_taler_uri_by_connection (con,
+                                   "pay",
+                                   instance_id,
+                                   &buf))
   {
-    GNUNET_buffer_write_path (&buf,
-                              "instances");
-    GNUNET_buffer_write_path (&buf,
-                              instance_id);
+    GNUNET_break (0);
+    return NULL;
   }
   GNUNET_buffer_write_path (&buf,
                             order_id);
   GNUNET_buffer_write_path (&buf,
-                            (session_id == NULL) ? "" : session_id);
-  if ((NULL != claim_token) &&
-      (GNUNET_NO == GNUNET_is_zero (claim_token)))
+                            (NULL == session_id)
+                            ? ""
+                            : session_id);
+  if ( (NULL != claim_token) &&
+       (! GNUNET_is_zero (claim_token)))
   {
     /* Just 'c=' because this goes into QR
        codes, so this is more compact. */
diff --git a/src/backend/taler-merchant-httpd_get-tips-ID.c 
b/src/backend/taler-merchant-httpd_get-tips-ID.c
index f9e6c8cd..71c66d51 100644
--- a/src/backend/taler-merchant-httpd_get-tips-ID.c
+++ b/src/backend/taler-merchant-httpd_get-tips-ID.c
@@ -25,6 +25,7 @@
 #include <taler/taler_json_lib.h>
 #include <taler/taler_templating_lib.h>
 #include "taler-merchant-httpd_get-tips-ID.h"
+#include "taler-merchant-httpd_helper.h"
 #include "taler-merchant-httpd_mhd.h"
 #include "taler-merchant-httpd_qr.h"
 
@@ -34,52 +35,18 @@ TMH_make_taler_tip_uri (struct MHD_Connection *con,
                         const struct TALER_TipIdentifierP *tip_id,
                         const char *instance_id)
 {
-  const char *host;
-  const char *forwarded_host;
-  const char *uri_path;
   struct GNUNET_Buffer buf = { 0 };
 
-  host = MHD_lookup_connection_value (con,
-                                      MHD_HEADER_KIND,
-                                      "Host");
-  forwarded_host = MHD_lookup_connection_value (con,
-                                                MHD_HEADER_KIND,
-                                                "X-Forwarded-Host");
-
-  uri_path = MHD_lookup_connection_value (con,
-                                          MHD_HEADER_KIND,
-                                          "X-Forwarded-Prefix");
-  if (NULL != forwarded_host)
-    host = forwarded_host;
-
-  if (NULL == host)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-
   GNUNET_assert (NULL != instance_id);
   GNUNET_assert (NULL != tip_id);
-
-  GNUNET_buffer_write_str (&buf,
-                           "taler");
-  if (GNUNET_NO == TALER_mhd_is_https (con))
-    GNUNET_buffer_write_str (&buf,
-                             "+http");
-  GNUNET_buffer_write_str (&buf,
-                           "://tip/");
-  GNUNET_buffer_write_str (&buf,
-                           host);
-  if (NULL != uri_path)
-    GNUNET_buffer_write_path (&buf,
-                              uri_path);
-  if (0 != strcmp ("default",
-                   instance_id))
+  if (GNUNET_OK !=
+      TMH_taler_uri_by_connection (con,
+                                   "tip",
+                                   instance_id,
+                                   &buf))
   {
-    GNUNET_buffer_write_path (&buf,
-                              "instances");
-    GNUNET_buffer_write_path (&buf,
-                              instance_id);
+    GNUNET_break (0);
+    return NULL;
   }
   /* Ensure previous part is slash-terminated */
   GNUNET_buffer_write_path (&buf,
@@ -96,51 +63,17 @@ TMH_make_tip_status_url (struct MHD_Connection *con,
                          const struct TALER_TipIdentifierP *tip_id,
                          const char *instance_id)
 {
-  const char *host;
-  const char *forwarded_host;
-  const char *uri_path;
-  struct GNUNET_Buffer buf = { 0 };
-
-  host = MHD_lookup_connection_value (con,
-                                      MHD_HEADER_KIND,
-                                      "Host");
-  forwarded_host = MHD_lookup_connection_value (con,
-                                                MHD_HEADER_KIND,
-                                                "X-Forwarded-Host");
-
-  uri_path = MHD_lookup_connection_value (con,
-                                          MHD_HEADER_KIND,
-                                          "X-Forwarded-Prefix");
-  if (NULL != forwarded_host)
-    host = forwarded_host;
-
-  if (NULL == host)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
+  struct GNUNET_Buffer buf;
 
   GNUNET_assert (NULL != instance_id);
   GNUNET_assert (NULL != tip_id);
-
-  if (GNUNET_NO == TALER_mhd_is_https (con))
-    GNUNET_buffer_write_str (&buf,
-                             "http://";);
-  else
-    GNUNET_buffer_write_str (&buf,
-                             "https://";);
-  GNUNET_buffer_write_str (&buf,
-                           host);
-  if (NULL != uri_path)
-    GNUNET_buffer_write_path (&buf,
-                              uri_path);
-  if (0 != strcmp ("default",
-                   instance_id))
+  if (GNUNET_OK !=
+      TMH_base_url_by_connection (con,
+                                  instance_id,
+                                  &buf))
   {
-    GNUNET_buffer_write_path (&buf,
-                              "instances");
-    GNUNET_buffer_write_path (&buf,
-                              instance_id);
+    GNUNET_break (0);
+    return NULL;
   }
   GNUNET_buffer_write_path (&buf,
                             "tips/");
diff --git a/src/backend/taler-merchant-httpd_helper.c 
b/src/backend/taler-merchant-httpd_helper.c
index 0e026a87..9e7a1883 100644
--- a/src/backend/taler-merchant-httpd_helper.c
+++ b/src/backend/taler-merchant-httpd_helper.c
@@ -314,7 +314,6 @@ TMH_products_array_valid (const json_t *products)
   {
     const char *product_id = NULL;
     const char *description;
-    json_t *description_i18n = NULL;
     uint64_t quantity = 0;
     const char *unit = NULL;
     struct TALER_Amount price = { .value = 0 };
@@ -326,12 +325,8 @@ TMH_products_array_valid (const json_t *products)
         GNUNET_JSON_spec_string ("product_id",
                                  &product_id),
         NULL),
-      GNUNET_JSON_spec_string ("description",
-                               &description),
-      GNUNET_JSON_spec_mark_optional (
-        GNUNET_JSON_spec_json ("description_i18n",
-                               &description_i18n),
-        NULL),
+      TALER_JSON_spec_i18n_str ("description",
+                                &description),
       GNUNET_JSON_spec_mark_optional (
         GNUNET_JSON_spec_uint64 ("quantity",
                                  &quantity),
@@ -386,12 +381,6 @@ TMH_products_array_valid (const json_t *products)
       GNUNET_break_op (0);
       valid = false;
     }
-    if ( (NULL != description_i18n) &&
-         (! TALER_JSON_check_i18n (description_i18n)) )
-    {
-      GNUNET_break_op (0);
-      valid = false;
-    }
     GNUNET_JSON_parse_free (spec);
     if (! valid)
       break;
@@ -598,12 +587,6 @@ TMH_check_auth_config (struct MHD_Connection *connection,
 }
 
 
-/**
- * Generate binary UUID from client-provided UUID-string.
- *
- * @param uuids string intpu
- * @param[out] uuid set to binary UUID
- */
 void
 TMH_uuid_from_string (const char *uuids,
                       struct GNUNET_Uuid *uuid)
@@ -855,3 +838,116 @@ TMH_exchange_accounts_by_method (
   }
   return emc.accounts;
 }
+
+
+enum GNUNET_GenericReturnValue
+TMH_base_url_by_connection (struct MHD_Connection *connection,
+                            const char *instance,
+                            struct GNUNET_Buffer *buf)
+{
+  const char *host;
+  const char *forwarded_host;
+  const char *uri_path;
+
+  memset (buf,
+          0,
+          sizeof (*buf));
+  if (GNUNET_YES == TALER_mhd_is_https (connection))
+    GNUNET_buffer_write_str (buf, "https://";);
+  else
+    GNUNET_buffer_write_str (buf, "http://";);
+  host = MHD_lookup_connection_value (connection,
+                                      MHD_HEADER_KIND,
+                                      MHD_HTTP_HEADER_HOST);
+  forwarded_host = MHD_lookup_connection_value (connection,
+                                                MHD_HEADER_KIND,
+                                                "X-Forwarded-Host");
+  if (NULL != forwarded_host)
+  {
+    GNUNET_buffer_write_str (buf,
+                             forwarded_host);
+  }
+  else
+  {
+    if (NULL == host)
+    {
+      GNUNET_buffer_clear (buf);
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+    GNUNET_buffer_write_str (buf,
+                             host);
+  }
+  uri_path = MHD_lookup_connection_value (connection,
+                                          MHD_HEADER_KIND,
+                                          "X-Forwarded-Prefix");
+  if (NULL != uri_path)
+    GNUNET_buffer_write_path (buf,
+                              uri_path);
+  if (0 != strcmp (instance,
+                   "default"))
+  {
+    GNUNET_buffer_write_path (buf,
+                              "/instances/");
+    GNUNET_buffer_write_str (buf,
+                             instance);
+  }
+  return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+TMH_taler_uri_by_connection (struct MHD_Connection *connection,
+                             const char *method,
+                             const char *instance,
+                             struct GNUNET_Buffer *buf)
+{
+  const char *host;
+  const char *forwarded_host;
+  const char *uri_path;
+
+  memset (buf,
+          0,
+          sizeof (*buf));
+  host = MHD_lookup_connection_value (connection,
+                                      MHD_HEADER_KIND,
+                                      "Host");
+  forwarded_host = MHD_lookup_connection_value (connection,
+                                                MHD_HEADER_KIND,
+                                                "X-Forwarded-Host");
+  uri_path = MHD_lookup_connection_value (connection,
+                                          MHD_HEADER_KIND,
+                                          "X-Forwarded-Prefix");
+  if (NULL != forwarded_host)
+    host = forwarded_host;
+  if (NULL == host)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_buffer_write_str (buf,
+                           "taler");
+  if (GNUNET_NO == TALER_mhd_is_https (connection))
+    GNUNET_buffer_write_str (buf,
+                             "+http");
+  GNUNET_buffer_write_str (buf,
+                           "://");
+  GNUNET_buffer_write_str (buf,
+                           method);
+  GNUNET_buffer_write_str (buf,
+                           "/");
+  GNUNET_buffer_write_str (buf,
+                           host);
+  if (NULL != uri_path)
+    GNUNET_buffer_write_path (buf,
+                              uri_path);
+  if (0 != strcmp ("default",
+                   instance))
+  {
+    GNUNET_buffer_write_path (buf,
+                              "instances");
+    GNUNET_buffer_write_path (buf,
+                              instance);
+  }
+  return GNUNET_OK;
+}
diff --git a/src/backend/taler-merchant-httpd_helper.h 
b/src/backend/taler-merchant-httpd_helper.h
index 3b64c04b..827cc03b 100644
--- a/src/backend/taler-merchant-httpd_helper.h
+++ b/src/backend/taler-merchant-httpd_helper.h
@@ -158,6 +158,40 @@ TMH_uuid_from_string (const char *uuids,
                       struct GNUNET_Uuid *uuid);
 
 
+/**
+ * Initializes a buffer with
+ * the ``http[s]://$HOST/[$PATH/][instances/$INSTANCE/]``
+ * string using $HOST and $PATH from @a connection.
+ *
+ * @param[in] connection connection to base the construction on
+ * @param instance instance to set, NULL for none
+ * @param[out] buf buffer to initialize
+ * @return #GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+TMH_base_url_by_connection (struct MHD_Connection *connection,
+                            const char *instance,
+                            struct GNUNET_Buffer *buf);
+
+
+/**
+ * Initializes a buffer with
+ * the ``taler[+http]://$METHOD/$HOST/[instances/$INSTANCE/]``
+ * string using $HOST from @a connection.
+ *
+ * @param[in] connection connection to base the construction on
+ * @param method taler-URI method to inject
+ * @param instance instance to set, NULL for none
+ * @param[out] buf buffer to initialize
+ * @return #GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+TMH_taler_uri_by_connection (struct MHD_Connection *connection,
+                             const char *method,
+                             const char *instance,
+                             struct GNUNET_Buffer *buf);
+
+
 /**
  * Put data from an exchange's HTTP response into
  * a JSON reply
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c 
b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
index 9ab10939..1853e88a 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
@@ -498,18 +498,17 @@ refund_cb (void *cls,
  * @param cls the `struct AbortContext`
  * @param hr HTTP response details
  * @param exchange_handle NULL if exchange was not found to be acceptable
- * @param exchange_trusted true if this exchange is
- *        trusted by config
+ * @param ih internal handle to the exchange
  */
 static void
 process_abort_with_exchange (void *cls,
                              const struct TALER_EXCHANGE_HttpResponse *hr,
                              struct TALER_EXCHANGE_Handle *exchange_handle,
-                             bool exchange_trusted)
+                             struct TMH_Exchange *ih)
 {
   struct AbortContext *ac = cls;
 
-  (void) exchange_trusted;
+  (void) ih;
   ac->fo = NULL;
   GNUNET_assert (GNUNET_YES == ac->suspended);
   if (NULL == hr)
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c 
b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
index afec3b25..81189a6d 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c
@@ -29,7 +29,6 @@
 #include <taler/taler_signatures.h>
 #include <taler/taler_json_lib.h>
 #include <taler/taler_exchange_service.h>
-#include "taler-merchant-httpd_auditors.h"
 #include "taler-merchant-httpd_exchanges.h"
 #include "taler-merchant-httpd_helper.h"
 #include "taler-merchant-httpd_post-orders-ID-pay.h"
@@ -805,18 +804,18 @@ deposit_get_callback (
  * @param cls the `struct KycContext`
  * @param hr HTTP response details
  * @param exchange_handle NULL if exchange was not found to be acceptable
- * @param exchange_trusted true if this exchange is
- *        trusted by config
+ * @param ih internal handle to the exchange
  */
 static void
 process_kyc_with_exchange (
   void *cls,
   const struct TALER_EXCHANGE_HttpResponse *hr,
   struct TALER_EXCHANGE_Handle *exchange_handle,
-  bool exchange_trusted)
+  struct TMH_Exchange *ih)
 {
   struct KycContext *kc = cls;
 
+  (void) ih;
   kc->fo = NULL;
   if (NULL == exchange_handle)
   {
@@ -1175,15 +1174,14 @@ batch_deposit_cb (
  * @param cls the `struct ExchangeGroup`
  * @param hr HTTP response details
  * @param exchange_handle NULL if exchange was not found to be acceptable
- * @param exchange_trusted true if this exchange is
- *        trusted by config
+ * @param ih internal handle to the exchange
  */
 static void
 process_pay_with_exchange (
   void *cls,
   const struct TALER_EXCHANGE_HttpResponse *hr,
   struct TALER_EXCHANGE_Handle *exchange_handle,
-  bool exchange_trusted)
+  struct TMH_Exchange *ih)
 {
   struct ExchangeGroup *eg = cls;
   struct PayContext *pc = eg->pc;
@@ -1221,6 +1219,21 @@ process_pay_with_exchange (
         TMH_pack_exchange_reply (hr)));
     return;
   }
+  if (GNUNET_OK !=
+      TMH_exchange_check_debit (ih,
+                                pc->wm))
+  {
+    GNUNET_break_op (0);
+    pc->pending_at_eg--;
+    resume_pay_with_response (
+      pc,
+      MHD_HTTP_CONFLICT,
+      TALER_MHD_MAKE_JSON_PACK (
+        TALER_JSON_pack_ec (
+          TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED)));
+    return;
+  }
+
   keys = TALER_EXCHANGE_get_keys (exchange_handle);
   if (NULL == keys)
   {
@@ -1240,8 +1253,6 @@ process_pay_with_exchange (
   {
     struct DepositConfirmation *dc = &pc->dc[i];
     const struct TALER_EXCHANGE_DenomPublicKey *denom_details;
-    unsigned int http_status;
-    enum TALER_ErrorCode ec;
     bool is_age_restricted_denom = false;
 
     if (0 != strcmp (eg->exchange_url,
@@ -1288,33 +1299,18 @@ process_pay_with_exchange (
     dc->deposit_fee = denom_details->fees.deposit;
     dc->refund_fee = denom_details->fees.refund;
 
-    if (GNUNET_OK !=
-        TMH_AUDITORS_check_dk (exchange_handle,
-                               denom_details,
-                               exchange_trusted,
-                               &http_status,
-                               &ec))
+    if (GNUNET_TIME_absolute_is_past (
+          denom_details->expire_deposit.abs_time))
     {
-      if (! eg->tried_force_keys)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                    "Denomination not audited by trusted auditor, re-fetching 
/keys\n");
-        /* let's try *forcing* a re-download of /keys from the exchange.
-           Maybe the wallet has seen auditors that we missed. */
-        eg->tried_force_keys = true;
-        eg->fo = TMH_EXCHANGES_find_exchange (eg->exchange_url,
-                                              true,
-                                              &process_pay_with_exchange,
-                                              eg);
-        if (NULL != eg->fo)
-          return;
-      }
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Denomination key offered by client has expired for 
deposits\n");
       pc->pending_at_eg--;
       resume_pay_with_response (
         pc,
-        http_status,
+        MHD_HTTP_GONE,
         TALER_MHD_MAKE_JSON_PACK (
-          TALER_JSON_pack_ec (ec),
+          TALER_JSON_pack_ec (
+            TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_DEPOSIT_EXPIRED),
           GNUNET_JSON_pack_data_auto ("h_denom_pub",
                                       &denom_details->h_key)));
       return;
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-refund.c 
b/src/backend/taler-merchant-httpd_post-orders-ID-refund.c
index 766c8814..ba562f9e 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-refund.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-refund.c
@@ -28,7 +28,6 @@
 #include <taler/taler_json_lib.h>
 #include <taler/taler_exchange_service.h>
 #include "taler-merchant-httpd.h"
-#include "taler-merchant-httpd_auditors.h"
 #include "taler-merchant-httpd_exchanges.h"
 #include "taler-merchant-httpd_post-orders-ID-refund.h"
 
@@ -440,18 +439,18 @@ refund_cb (void *cls,
  * @param cls a `struct CoinRefund *`
  * @param hr HTTP response details
  * @param eh handle to the exchange context
- * @param exchange_trusted true if this exchange is trusted by config
+ * @param ih internal handle to the exchange
  */
 static void
 exchange_found_cb (void *cls,
                    const struct TALER_EXCHANGE_HttpResponse *hr,
                    struct TALER_EXCHANGE_Handle *eh,
-                   bool exchange_trusted)
+                   struct TMH_Exchange *ih)
 {
   struct CoinRefund *cr = cls;
   struct PostRefundData *prd = cr->prd;
 
-  (void) exchange_trusted;
+  (void) ih;
   cr->fo = NULL;
   if (NULL == hr)
   {
diff --git a/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c 
b/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c
index fb560de6..da482cda 100644
--- a/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c
+++ b/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c
@@ -379,17 +379,18 @@ withdraw_cb (void *cls,
  * @param cls closure, with our `struct PlanchetOperation *`
  * @param hr HTTP response details
  * @param eh handle to the exchange context
- * @param exchange_trusted true if this exchange is trusted by config
+ * @param ih internal handle to the exchange
  */
 static void
 do_withdraw (void *cls,
              const struct TALER_EXCHANGE_HttpResponse *hr,
              struct TALER_EXCHANGE_Handle *eh,
-             bool exchange_trusted)
+             struct TMH_Exchange *ih)
 {
   struct PlanchetOperation *po = cls;
   struct PickupContext *pc = po->pc;
 
+  (void) ih;
   po->fo = NULL;
   TMH_db->preflight (TMH_db->cls);
   if (NULL == hr)
@@ -501,17 +502,18 @@ do_timeout (void *cls)
  * @param cls closure, with our `struct PickupContext *`
  * @param hr HTTP response details
  * @param eh handle to the exchange context
- * @param exchange_trusted true if this exchange is trusted by config
+ * @param ih internal handle to the exchange
  */
 static void
 compute_total_requested (void *cls,
                          const struct TALER_EXCHANGE_HttpResponse *hr,
                          struct TALER_EXCHANGE_Handle *eh,
-                         bool exchange_trusted)
+                         struct TMH_Exchange *ih)
 {
   struct PickupContext *pc = cls;
   const struct TALER_EXCHANGE_Keys *keys;
 
+  (void) ih;
   pc->fo = NULL;
   stop_operations (pc); /* stops timeout job */
   if (NULL == hr)
diff --git a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c 
b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
index 773415f8..3d807569 100644
--- a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
+++ b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c
@@ -651,19 +651,19 @@ exchange_check_cb (void *cls,
  * @param cls closure with our `struct ExchangeKycRequest *`
  * @param hr HTTP response details
  * @param eh handle to the exchange context
- * @param exchange_trusted true if this exchange is trusted by config
+ * @param ih internal handle to the exchange
  */
 static void
 kyc_with_exchange (void *cls,
                    const struct TALER_EXCHANGE_HttpResponse *hr,
                    struct TALER_EXCHANGE_Handle *eh,
-                   bool exchange_trusted)
+                   struct TMH_Exchange *ih)
 {
   struct ExchangeKycRequest *ekr = cls;
   struct KycContext *kc = ekr->kc;
   struct TALER_PaytoHashP h_payto;
 
-  (void) exchange_trusted;
+  (void) ih;
   ekr->fo = NULL;
   if (MHD_HTTP_OK != hr->http_status)
   {
diff --git a/src/backend/taler-merchant-httpd_private-get-orders-ID.c 
b/src/backend/taler-merchant-httpd_private-get-orders-ID.c
index cff03c7e..960058ea 100644
--- a/src/backend/taler-merchant-httpd_private-get-orders-ID.c
+++ b/src/backend/taler-merchant-httpd_private-get-orders-ID.c
@@ -598,11 +598,12 @@ static void
 exchange_found_cb (void *cls,
                    const struct TALER_EXCHANGE_HttpResponse *hr,
                    struct TALER_EXCHANGE_Handle *eh,
-                   bool exchange_trusted)
+                   struct TMH_Exchange *ih)
 {
   struct TransferQuery *tq = cls;
   struct GetOrderRequestContext *gorc = tq->gorc;
 
+  (void) ih;
   tq->fo = NULL;
   if (NULL == hr)
   {
diff --git a/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c 
b/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c
index 3953fa06..5b5f6b05 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c
+++ b/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c
@@ -26,6 +26,7 @@
 #include <taler/taler_json_lib.h>
 #include "taler-merchant-httpd_private-post-orders-ID-refund.h"
 #include "taler-merchant-httpd_private-get-orders.h"
+#include "taler-merchant-httpd_helper.h"
 
 
 /**
@@ -82,43 +83,21 @@ make_taler_refund_uri (struct MHD_Connection *connection,
                        const char *instance_id,
                        const char *order_id)
 {
-  const char *host;
-  const char *forwarded_host;
-  const char *uri_path;
-  struct GNUNET_Buffer buf = { 0 };
+  struct GNUNET_Buffer buf;
 
   GNUNET_assert (NULL != instance_id);
   GNUNET_assert (NULL != order_id);
-  host = MHD_lookup_connection_value (connection,
-                                      MHD_HEADER_KIND,
-                                      "Host");
-  forwarded_host = MHD_lookup_connection_value (connection,
-                                                MHD_HEADER_KIND,
-                                                "X-Forwarded-Host");
-  uri_path = MHD_lookup_connection_value (connection,
-                                          MHD_HEADER_KIND,
-                                          "X-Forwarded-Prefix");
-  if (NULL != forwarded_host)
-    host = forwarded_host;
-  if (NULL == host)
+  if (GNUNET_OK !=
+      TMH_taler_uri_by_connection (connection,
+                                   "refund",
+                                   instance_id,
+                                   &buf))
   {
-    /* Should never happen, at least the host header should be defined */
     GNUNET_break (0);
     return NULL;
   }
-  GNUNET_buffer_write_str (&buf, "taler");
-  if (GNUNET_NO == TALER_mhd_is_https (connection))
-    GNUNET_buffer_write_str (&buf, "+http");
-  GNUNET_buffer_write_str (&buf, "://refund/");
-  GNUNET_buffer_write_str (&buf, host);
-  if (NULL != uri_path)
-    GNUNET_buffer_write_path (&buf, uri_path);
-  if (0 != strcmp ("default", instance_id))
-  {
-    GNUNET_buffer_write_path (&buf, "instances");
-    GNUNET_buffer_write_path (&buf, instance_id);
-  }
-  GNUNET_buffer_write_path (&buf, order_id);
+  GNUNET_buffer_write_path (&buf,
+                            order_id);
   GNUNET_buffer_write_path (&buf,
                             ""); /* Trailing slash */
   return GNUNET_buffer_reap_str (&buf);
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c 
b/src/backend/taler-merchant-httpd_private-post-orders.c
index e75049d0..718909d3 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders.c
+++ b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -28,7 +28,6 @@
 #include <taler/taler_signatures.h>
 #include <taler/taler_json_lib.h>
 #include "taler-merchant-httpd_private-post-orders.h"
-#include "taler-merchant-httpd_auditors.h"
 #include "taler-merchant-httpd_exchanges.h"
 #include "taler-merchant-httpd_helper.h"
 #include "taler-merchant-httpd_private-get-orders.h"
@@ -52,109 +51,6 @@
 #define STANDARD_LABEL_MERCHANT_ADDRESS "_ma"
 
 
-/**
- * Check that the given JSON array of products is well-formed.
- *
- * @param products JSON array to check
- * @return #GNUNET_OK if all is fine
- */
-static enum GNUNET_GenericReturnValue
-check_products (const json_t *products)
-{
-  size_t index;
-  json_t *value;
-
-  if (! json_is_array (products))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  json_array_foreach (products, index, value) {
-    const char *description;
-    const char *product_id = NULL;
-    uint64_t quantity;
-    const char *unit = NULL;
-    struct TALER_Amount price;
-    const char *image = NULL;
-    json_t *taxes = NULL;
-    struct GNUNET_TIME_Timestamp delivery_date;
-    const char *error_name;
-    unsigned int error_line;
-    enum GNUNET_GenericReturnValue res;
-    struct GNUNET_JSON_Specification spec[] = {
-      GNUNET_JSON_spec_mark_optional (
-        GNUNET_JSON_spec_string ("product_id",
-                                 &product_id),
-        NULL),
-      TALER_JSON_spec_i18n_str ("description",
-                                &description),
-      GNUNET_JSON_spec_mark_optional (
-        GNUNET_JSON_spec_uint64 ("quantity",
-                                 &quantity),
-        NULL),
-      GNUNET_JSON_spec_mark_optional (
-        GNUNET_JSON_spec_string ("unit",
-                                 &unit),
-        NULL),
-      GNUNET_JSON_spec_mark_optional (
-        TALER_JSON_spec_amount ("price",
-                                TMH_currency,
-                                &price),
-        NULL),
-      GNUNET_JSON_spec_mark_optional (
-        GNUNET_JSON_spec_string ("image",
-                                 &image),
-        NULL),
-      GNUNET_JSON_spec_mark_optional (
-        GNUNET_JSON_spec_json ("taxes",
-                               &taxes),
-        NULL),
-      GNUNET_JSON_spec_mark_optional (
-        GNUNET_JSON_spec_timestamp ("delivery_date",
-                                    &delivery_date),
-        NULL),
-      GNUNET_JSON_spec_end ()
-    };
-
-    /* extract fields we need to sign separately */
-    res = GNUNET_JSON_parse (value,
-                             spec,
-                             &error_name,
-                             &error_line);
-    if (GNUNET_OK != res)
-    {
-      GNUNET_break (0);
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Product parsing failed at #%u: %s:%u\n",
-                  (unsigned int) index,
-                  error_name,
-                  error_line);
-      return GNUNET_SYSERR;
-    }
-    if ( (NULL != taxes) &&
-         (! TMH_taxes_array_valid (taxes) ) )
-    {
-      GNUNET_break (0);
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Product parsing failed for taxes\n");
-      GNUNET_JSON_parse_free (spec);
-      return GNUNET_SYSERR;
-    }
-    if ( (NULL != image) &&
-         (! TMH_image_data_url_valid (image) ) )
-    {
-      GNUNET_break (0);
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Product parsing failed for image\n");
-      GNUNET_JSON_parse_free (spec);
-      return GNUNET_SYSERR;
-    }
-    GNUNET_JSON_parse_free (spec);
-  }
-  return GNUNET_OK;
-}
-
-
 /**
  * Generate the base URL for the given merchant instance.
  *
@@ -166,46 +62,13 @@ static char *
 make_merchant_base_url (struct MHD_Connection *connection,
                         const char *instance_id)
 {
-  const char *host;
-  const char *forwarded_host;
-  const char *uri_path;
-  struct GNUNET_Buffer buf = { 0 };
-
-  if (GNUNET_YES == TALER_mhd_is_https (connection))
-    GNUNET_buffer_write_str (&buf, "https://";);
-  else
-    GNUNET_buffer_write_str (&buf, "http://";);
-  host = MHD_lookup_connection_value (connection,
-                                      MHD_HEADER_KIND,
-                                      MHD_HTTP_HEADER_HOST);
-  forwarded_host = MHD_lookup_connection_value (connection,
-                                                MHD_HEADER_KIND,
-                                                "X-Forwarded-Host");
-  if (NULL != forwarded_host)
-  {
-    GNUNET_buffer_write_str (&buf,
-                             forwarded_host);
-  }
-  else
-  {
-    GNUNET_assert (NULL != host);
-    GNUNET_buffer_write_str (&buf,
-                             host);
-  }
-  uri_path = MHD_lookup_connection_value (connection,
-                                          MHD_HEADER_KIND,
-                                          "X-Forwarded-Prefix");
-  if (NULL != uri_path)
-    GNUNET_buffer_write_path (&buf, uri_path);
-
-  if (0 != strcmp (instance_id,
-                   "default"))
-  {
-    GNUNET_buffer_write_path (&buf,
-                              "/instances/");
-    GNUNET_buffer_write_str (&buf,
-                             instance_id);
-  }
+  struct GNUNET_Buffer buf;
+
+  if (GNUNET_OK !=
+      TMH_base_url_by_connection (connection,
+                                  instance_id,
+                                  &buf))
+    return NULL;
   GNUNET_buffer_write_path (&buf,
                             "");
   return GNUNET_buffer_reap_str (&buf);
@@ -230,38 +93,219 @@ struct InventoryProduct
 };
 
 
+/**
+ * Information we keep per order we are processing.
+ */
+struct OrderContext
+{
+
+  /**
+   * Connection of the request.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * Handler context for the request.
+   */
+  struct TMH_HandlerContext *hc;
+
+  /**
+   * Hash of the POST request data, used to detect
+   * idempotent requests.
+   */
+  struct TALER_MerchantPostDataHashP h_post_data;
+
+  /**
+   * Payment deadline.
+   */
+  struct GNUNET_TIME_Timestamp pay_deadline;
+
+  /**
+   * Set to how long refunds will be allowed.
+   */
+  struct GNUNET_TIME_Relative refund_delay;
+
+  /**
+   * Order we are building (modified as we process
+   * the request).
+   */
+  json_t *order;
+
+  /**
+   * RFC8905 payment target type to find a matching merchant account
+   */
+  const char *payment_target;
+
+  /**
+   * Wire method (and our bank account) we have selected
+   * to be included for this order.
+   */
+  const struct TMH_WireMethod *wm;
+
+  /**
+   * Claim token for the request.
+   */
+  struct TALER_ClaimTokenP claim_token;
+
+  /**
+   * Length of the @e inventory_products array.
+   */
+  unsigned int inventory_products_length;
+
+  /**
+   * Array of inventory products in the @e order.
+   */
+  struct InventoryProduct *inventory_products;
+
+  /**
+   * Length of the @e uuids array.
+   */
+  unsigned int uuids_length;
+
+  /**
+   * array of UUIDs used to reserve products from @a inventory_products.
+   */
+  struct GNUNET_Uuid *uuids;
+
+  /**
+   * which product (by offset) is out of stock, UINT_MAX if all were in-stock
+   */
+  unsigned int out_of_stock_index;
+
+  /**
+   * Shared key to use with @e pos_algorithm.
+   */
+  char *pos_key;
+
+  /**
+   * Our order ID. Pointer into @e order.
+   */
+  const char *order_id;
+
+  /**
+   * Selected algorithm (by template) when we are to
+   * generate an OTP code for payment confirmation.
+   */
+  enum TALER_MerchantConfirmationAlgorithm pos_algorithm;
+
+  /**
+   * Current phase of setting up the order.
+   */
+  enum
+  {
+    ORDER_PHASE_INIT,
+    ORDER_PHASE_MERGE_INVENTORY,
+    ORDER_PHASE_ADD_PAYMENT_DETAILS,
+    ORDER_PHASE_PATCH_ORDER,
+    ORDER_PHASE_SET_EXCHANGES,
+    ORDER_PHASE_CHECK_CONTRACT,
+    ORDER_PHASE_EXECUTE_ORDER,
+
+    /**
+     * Processing is done, we should return #MHD_YES.
+     */
+    ORDER_PHASE_FINISHED_MHD_YES,
+
+    /**
+     * Processing is done, we should return #MHD_NO.
+     */
+    ORDER_PHASE_FINISHED_MHD_NO
+  } phase;
+
+};
+
+
+/**
+ * Update the phase of @a oc based on @a mret.
+ *
+ * @param[in,out] oc order to update phase for
+ * @param mret #MHD_NO to close with #MHD_NO
+ *             #MHD_YES to close with #MHD_YES
+ */
+static void
+finalize_order (struct OrderContext *oc,
+                MHD_RESULT mret)
+{
+  oc->phase = (MHD_YES == mret)
+    ? ORDER_PHASE_FINISHED_MHD_YES
+    : ORDER_PHASE_FINISHED_MHD_NO;
+}
+
+
+/**
+ * Update the phase of @a oc based on @a ret.
+ *
+ * @param[in,out] oc order to update phase for
+ * @param ret #GNUNET_SYSERR to close with #MHD_NO
+ *            #GNUNET_NO to close with #MHD_YES
+ *            #GNUNET_OK is not allowed!
+ */
+static void
+finalize_order2 (struct OrderContext *oc,
+                 enum GNUNET_GenericReturnValue ret)
+{
+  GNUNET_assert (GNUNET_OK != ret);
+  oc->phase = (GNUNET_NO == ret)
+    ? ORDER_PHASE_FINISHED_MHD_YES
+    : ORDER_PHASE_FINISHED_MHD_NO;
+}
+
+
+/**
+ * Generate an error response for @a oc.
+ *
+ * @param[in,out] oc order context to respond to
+ * @param http_status HTTP status code to set
+ * @param ec error code to set
+ * @param detail error message detail to set
+ */
+static void
+reply_with_error (struct OrderContext *oc,
+                  unsigned int http_status,
+                  enum TALER_ErrorCode ec,
+                  const char *detail)
+{
+  MHD_RESULT mret;
+
+  mret = TALER_MHD_reply_with_error (oc->connection,
+                                     http_status,
+                                     ec,
+                                     detail);
+  finalize_order (oc,
+                  mret);
+}
+
+
+/**
+ * Clean up memory used by @a cls.
+ *
+ * @param[in] cls the `struct OrderContext` to clean up
+ */
+static void
+clean_order (void *cls)
+{
+  struct OrderContext *oc = cls;
+
+  GNUNET_array_grow (oc->inventory_products,
+                     oc->inventory_products_length,
+                     0);
+  GNUNET_array_grow (oc->uuids,
+                     oc->uuids_length,
+                     0);
+  json_decref (oc->order);
+  GNUNET_free (oc->pos_key);
+  GNUNET_free (oc);
+}
+
+
 /**
  * Execute the database transaction to setup the order.
  *
- * @param hc handler context for the request
- * @param order_id unique ID for the order
- * @param h_post_data hash of the client's POST request, for idempotency checks
- * @param pay_deadline until when does the order have to be paid
- * @param[in] order order to process (not modified)
- * @param claim_token token to use for access control
- * @param inventory_products_length length of the @a inventory_products array
- * @param inventory_products array of products to add to @a order from our 
inventory
- * @param uuids_length length of the @a uuids array
- * @param uuids array of UUIDs used to reserve products from @a 
inventory_products
- * @param[out] out_of_stock_index which product (by offset) is out of stock, 
UINT_MAX if all were in-stock
- * @param pos_key shared secret with the POS
- * @param pos_algorithm algorithm for payment confirmation generation
- * @return transaction status, 0 if @a uuids were insufficient to reserve 
required inventory
+ * @param[in,out] oc order context
+ * @return transaction status, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a 
uuids were insufficient to reserve required inventory
  */
 static enum GNUNET_DB_QueryStatus
-execute_transaction (struct TMH_HandlerContext *hc,
-                     const char *order_id,
-                     const struct TALER_MerchantPostDataHashP *h_post_data,
-                     struct GNUNET_TIME_Timestamp pay_deadline,
-                     const json_t *order,
-                     const struct TALER_ClaimTokenP *claim_token,
-                     unsigned int inventory_products_length,
-                     const struct InventoryProduct inventory_products[],
-                     unsigned int uuids_length,
-                     const struct GNUNET_Uuid uuids[],
-                     unsigned int *out_of_stock_index,
-                     const char *pos_key,
-                     enum TALER_MerchantConfirmationAlgorithm pos_algorithm)
+execute_transaction (struct OrderContext *oc)
 {
   enum GNUNET_DB_QueryStatus qs;
   struct GNUNET_TIME_Timestamp timestamp;
@@ -276,14 +320,14 @@ execute_transaction (struct TMH_HandlerContext *hc,
   }
   /* Setup order */
   qs = TMH_db->insert_order (TMH_db->cls,
-                             hc->instance->settings.id,
-                             order_id,
-                             h_post_data,
-                             pay_deadline,
-                             claim_token,
-                             order,  /* called 'contract terms' at database. */
-                             pos_key,
-                             pos_algorithm);
+                             oc->hc->instance->settings.id,
+                             oc->order_id,
+                             &oc->h_post_data,
+                             oc->pay_deadline,
+                             &oc->claim_token,
+                             oc->order,  /* called 'contract terms' at 
database. */
+                             oc->pos_key,
+                             oc->pos_algorithm);
   if (qs <= 0)
   {
     /* qs == 0: probably instance does not exist (anymore) */
@@ -291,10 +335,10 @@ execute_transaction (struct TMH_HandlerContext *hc,
     return qs;
   }
   /* Migrate locks from UUIDs to new order: first release old locks */
-  for (unsigned int i = 0; i<uuids_length; i++)
+  for (unsigned int i = 0; i<oc->uuids_length; i++)
   {
     qs = TMH_db->unlock_inventory (TMH_db->cls,
-                                   &uuids[i]);
+                                   &oc->uuids[i]);
     if (qs < 0)
     {
       TMH_db->rollback (TMH_db->cls);
@@ -307,13 +351,14 @@ execute_transaction (struct TMH_HandlerContext *hc,
      (note: this can basically ONLY fail on serializability OR
      because the UUID locks were insufficient for the desired
      quantities). */
-  for (unsigned int i = 0; i<inventory_products_length; i++)
+  for (unsigned int i = 0; i<oc->inventory_products_length; i++)
   {
-    qs = TMH_db->insert_order_lock (TMH_db->cls,
-                                    hc->instance->settings.id,
-                                    order_id,
-                                    inventory_products[i].product_id,
-                                    inventory_products[i].quantity);
+    qs = TMH_db->insert_order_lock (
+      TMH_db->cls,
+      oc->hc->instance->settings.id,
+      oc->order_id,
+      oc->inventory_products[i].product_id,
+      oc->inventory_products[i].quantity);
     if (qs < 0)
     {
       TMH_db->rollback (TMH_db->cls);
@@ -323,17 +368,17 @@ execute_transaction (struct TMH_HandlerContext *hc,
     {
       /* qs == 0: lock acquisition failed due to insufficient stocks */
       TMH_db->rollback (TMH_db->cls);
-      *out_of_stock_index = i; /* indicate which product is causing the issue 
*/
+      oc->out_of_stock_index = i; /* indicate which product is causing the 
issue */
       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     }
   }
-  *out_of_stock_index = UINT_MAX;
+  oc->out_of_stock_index = UINT_MAX;
 
   /* Get the order serial and timestamp for the order we just created to
      update long-poll clients. */
   qs = TMH_db->lookup_order_summary (TMH_db->cls,
-                                     hc->instance->settings.id,
-                                     order_id,
+                                     oc->hc->instance->settings.id,
+                                     oc->order_id,
                                      &timestamp,
                                      &order_serial);
   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
@@ -341,7 +386,7 @@ execute_transaction (struct TMH_HandlerContext *hc,
     TMH_db->rollback (TMH_db->cls);
     return qs;
   }
-  TMH_notify_order_change (hc->instance,
+  TMH_notify_order_change (oc->hc->instance,
                            TMH_OSF_NONE,
                            timestamp,
                            order_serial);
@@ -359,36 +404,14 @@ execute_transaction (struct TMH_HandlerContext *hc,
  * database. Write the resulting proposal or an error message
  * of a MHD connection.
  *
- * @param connection connection to write the result or error to
- * @param hc handler context for the request
- * @param h_post_data hash of the client's POST request, for idempotency checks
- * @param[in,out] order order to process (can be modified)
- * @param claim_token token to use for access control
- * @param inventory_products_length length of the @a inventory_products array
- * @param inventory_products array of products to add to @a order from our 
inventory
- * @param uuids_length length of the @a uuids array
- * @param uuids array of UUIDs used to reserve products from @a 
inventory_products
- * @param pos_key encoded key for verification payment
- * @param pos_algorithm  algorithm to compute the payment verification
- * @return MHD result code
+ * @param[in,out] oc order context
  */
-static MHD_RESULT
-execute_order (struct MHD_Connection *connection,
-               struct TMH_HandlerContext *hc,
-               const struct TALER_MerchantPostDataHashP *h_post_data,
-               json_t *order,
-               const struct TALER_ClaimTokenP *claim_token,
-               unsigned int inventory_products_length,
-               const struct InventoryProduct inventory_products[],
-               unsigned int uuids_length,
-               const struct GNUNET_Uuid uuids[],
-               const char *pos_key,
-               enum TALER_MerchantConfirmationAlgorithm pos_algorithm)
+static void
+execute_order (struct OrderContext *oc)
 {
   const struct TALER_MERCHANTDB_InstanceSettings *settings =
-    &hc->instance->settings;
+    &oc->hc->instance->settings;
   struct TALER_Amount total;
-  const char *order_id;
   const char *summary;
   const char *fulfillment_msg = NULL;
   json_t *products;
@@ -398,13 +421,12 @@ execute_order (struct MHD_Connection *connection,
   struct GNUNET_TIME_Timestamp timestamp;
   struct GNUNET_TIME_Timestamp refund_deadline = { 0 };
   struct GNUNET_TIME_Timestamp wire_transfer_deadline;
-  struct GNUNET_TIME_Timestamp pay_deadline;
   struct GNUNET_JSON_Specification spec[] = {
     TALER_JSON_spec_amount ("amount",
                             TMH_currency,
                             &total),
     GNUNET_JSON_spec_string ("order_id",
-                             &order_id),
+                             &oc->order_id),
     GNUNET_JSON_spec_string ("summary",
                              &summary),
     /**
@@ -433,57 +455,62 @@ execute_order (struct MHD_Connection *connection,
                                   &refund_deadline),
       NULL),
     GNUNET_JSON_spec_timestamp ("pay_deadline",
-                                &pay_deadline),
+                                &oc->pay_deadline),
     GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
                                 &wire_transfer_deadline),
     GNUNET_JSON_spec_end ()
   };
   enum GNUNET_DB_QueryStatus qs;
-  unsigned int out_of_stock_index;
 
   /* extract fields we need to sign separately */
   {
     enum GNUNET_GenericReturnValue res;
 
-    res = TALER_MHD_parse_json_data (connection,
-                                     order,
+    res = TALER_MHD_parse_json_data (oc->connection,
+                                     oc->order,
                                      spec);
     if (GNUNET_OK != res)
     {
       GNUNET_break_op (0);
-      return (GNUNET_NO == res)
-             ? MHD_YES
-             : MHD_NO;
+      finalize_order2 (oc,
+                       res);
     }
   }
 
   /* check product list in contract is well-formed */
-  if (GNUNET_OK != check_products (products))
+  if (! TMH_products_array_valid (products))
   {
     GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_BAD_REQUEST,
-                                       TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                       "order:products");
+    GNUNET_break_op (0);
+    reply_with_error (oc,
+                      MHD_HTTP_BAD_REQUEST,
+                      TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                      "order:products");
+    return;
   }
 
+  // FIXME: we can do this better now...
   if ( (NULL != fulfillment_i18n) &&
        (! TALER_JSON_check_i18n (fulfillment_i18n)) )
   {
+    GNUNET_break_op (0);
     GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_BAD_REQUEST,
-                                       TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                       "order:fulfillment_message_i18n");
+    reply_with_error (oc,
+                      MHD_HTTP_BAD_REQUEST,
+                      TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                      "order:fulfillment_message_i18n");
+    return;
   }
   if ( (NULL != summary_i18n) &&
        (! TALER_JSON_check_i18n (summary_i18n)) )
   {
+    GNUNET_break_op (0);
     GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_BAD_REQUEST,
-                                       TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                       "order:summary_i18n");
+    reply_with_error (oc,
+                      MHD_HTTP_BAD_REQUEST,
+                      TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                      "order:summary_i18n");
+    return;
   }
 
   /* Test if we already have an order with this id */
@@ -494,8 +521,8 @@ execute_order (struct MHD_Connection *connection,
 
     TMH_db->preflight (TMH_db->cls);
     qs = TMH_db->lookup_order (TMH_db->cls,
-                               hc->instance->settings.id,
-                               order_id,
+                               oc->hc->instance->settings.id,
+                               oc->order_id,
                                &token,
                                &orig_post,
                                &contract_terms);
@@ -505,10 +532,11 @@ execute_order (struct MHD_Connection *connection,
       GNUNET_break (0);
       TMH_db->rollback (TMH_db->cls);
       GNUNET_JSON_parse_free (spec);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                         "lookup_order");
+      reply_with_error (oc,
+                        MHD_HTTP_INTERNAL_SERVER_ERROR,
+                        TALER_EC_GENERIC_DB_FETCH_FAILED,
+                        "lookup_order");
+      return;
     }
     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     {
@@ -518,13 +546,15 @@ execute_order (struct MHD_Connection *connection,
       /* Comparing the contract terms is sufficient because all the other
          params get added to it at some point. */
       if (0 == GNUNET_memcmp (&orig_post,
-                              h_post_data))
+                              &oc->h_post_data))
       {
+        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                    "Order creation idempotent\n");
         ret = TALER_MHD_REPLY_JSON_PACK (
-          connection,
+          oc->connection,
           MHD_HTTP_OK,
           GNUNET_JSON_pack_string ("order_id",
-                                   order_id),
+                                   oc->order_id),
           GNUNET_JSON_pack_allow_null (
             GNUNET_JSON_pack_data_varsize (
               "token",
@@ -532,40 +562,29 @@ execute_order (struct MHD_Connection *connection,
               ? NULL
               : &token,
               sizeof (token))));
+        GNUNET_JSON_parse_free (spec);
+        finalize_order (oc,
+                        ret);
+        return;
       }
-      else
-      {
-        /* This request is not idempotent */
-        ret = TALER_MHD_reply_with_error (
-          connection,
-          MHD_HTTP_CONFLICT,
-          TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS,
-          order_id);
-      }
-      GNUNET_JSON_parse_free (spec);
-      return ret;
+      /* This request is not idempotent */
+      GNUNET_break_op (0);
+      reply_with_error (
+        oc,
+        MHD_HTTP_CONFLICT,
+        TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS,
+        oc->order_id);
+      return;
     }
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Executing database transaction to create order '%s' for 
instance '%s'\n",
-              order_id,
+              oc->order_id,
               settings->id);
   for (unsigned int i = 0; i<MAX_RETRIES; i++)
   {
     TMH_db->preflight (TMH_db->cls);
-    qs = execute_transaction (hc,
-                              order_id,
-                              h_post_data,
-                              pay_deadline,
-                              order,
-                              claim_token,
-                              inventory_products_length,
-                              inventory_products,
-                              uuids_length,
-                              uuids,
-                              &out_of_stock_index,
-                              pos_key,
-                              pos_algorithm);
+    qs = execute_transaction (oc);
     if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
       break;
   }
@@ -576,57 +595,64 @@ execute_order (struct MHD_Connection *connection,
     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     {
       GNUNET_break (0);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         TALER_EC_GENERIC_DB_SOFT_FAILURE,
-                                         NULL);
+      reply_with_error (oc,
+                        MHD_HTTP_INTERNAL_SERVER_ERROR,
+                        TALER_EC_GENERIC_DB_SOFT_FAILURE,
+                        NULL);
+      return;
     }
     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     {
       /* should be: contract (!) with same order ID
          already exists */
-      return TALER_MHD_reply_with_error (
-        connection,
+      reply_with_error (
+        oc,
         MHD_HTTP_CONFLICT,
         TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS,
-        order_id);
+        oc->order_id);
+      return;
     }
     /* Other hard transaction error (disk full, etc.) */
     GNUNET_break (0);
-    return TALER_MHD_reply_with_error (
-      connection,
+    reply_with_error (
+      oc,
       MHD_HTTP_INTERNAL_SERVER_ERROR,
       TALER_EC_GENERIC_DB_COMMIT_FAILED,
       NULL);
+    return;
   }
 
   /* DB transaction succeeded, check for out-of-stock */
-  if (out_of_stock_index < UINT_MAX)
+  if (oc->out_of_stock_index < UINT_MAX)
   {
     /* We had a product that has insufficient quantities,
        generate the details for the response. */
     struct TALER_MERCHANTDB_ProductDetails pd;
     MHD_RESULT ret;
 
-    memset (&pd, 0, sizeof (pd));
+    memset (&pd,
+            0,
+            sizeof (pd));
     qs = TMH_db->lookup_product (
       TMH_db->cls,
-      hc->instance->settings.id,
-      inventory_products[out_of_stock_index].product_id,
+      oc->hc->instance->settings.id,
+      oc->inventory_products[oc->out_of_stock_index].product_id,
       &pd);
     GNUNET_JSON_parse_free (spec);
     switch (qs)
     {
     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Order creation failed: product out of stock\n");
       ret = TALER_MHD_REPLY_JSON_PACK (
-        connection,
+        oc->connection,
         MHD_HTTP_GONE,
         GNUNET_JSON_pack_string (
           "product_id",
-          inventory_products[out_of_stock_index].product_id),
+          oc->inventory_products[oc->out_of_stock_index].product_id),
         GNUNET_JSON_pack_uint64 (
           "requested_quantity",
-          inventory_products[out_of_stock_index].quantity),
+          oc->inventory_products[oc->out_of_stock_index].quantity),
         GNUNET_JSON_pack_uint64 (
           "available_quantity",
           pd.total_stock - pd.total_sold - pd.total_lost),
@@ -635,57 +661,145 @@ execute_order (struct MHD_Connection *connection,
             "restock_expected",
             pd.next_restock)));
       TALER_MERCHANTDB_product_details_free (&pd);
-      return ret;
+      finalize_order (oc,
+                      ret);
+      return;
     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
-      return TALER_MHD_REPLY_JSON_PACK (
-        connection,
-        MHD_HTTP_GONE,
-        GNUNET_JSON_pack_string (
-          "product_id",
-          inventory_products[out_of_stock_index].product_id),
-        GNUNET_JSON_pack_uint64 (
-          "requested_quantity",
-          inventory_products[out_of_stock_index].quantity),
-        GNUNET_JSON_pack_uint64 (
-          "available_quantity",
-          0));
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Order creation failed: unknown product out of stock\n");
+      finalize_order (oc,
+                      TALER_MHD_REPLY_JSON_PACK (
+                        oc->connection,
+                        MHD_HTTP_GONE,
+                        GNUNET_JSON_pack_string (
+                          "product_id",
+                          oc->inventory_products[oc->out_of_stock_index].
+                          product_id),
+                        GNUNET_JSON_pack_uint64 (
+                          "requested_quantity",
+                          oc->inventory_products[oc->out_of_stock_index].
+                          quantity),
+                        GNUNET_JSON_pack_uint64 (
+                          "available_quantity",
+                          0)));
+      return;
     case GNUNET_DB_STATUS_SOFT_ERROR:
       GNUNET_break (0);
-      return TALER_MHD_reply_with_error (
-        connection,
+      reply_with_error (
+        oc,
         MHD_HTTP_INTERNAL_SERVER_ERROR,
         TALER_EC_GENERIC_DB_SOFT_FAILURE,
         NULL);
+      return;
     case GNUNET_DB_STATUS_HARD_ERROR:
-      return TALER_MHD_reply_with_error (
-        connection,
+      GNUNET_break (0);
+      reply_with_error (
+        oc,
         MHD_HTTP_INTERNAL_SERVER_ERROR,
         TALER_EC_GENERIC_DB_FETCH_FAILED,
         NULL);
+      return;
     }
     GNUNET_break (0);
-    return MHD_NO;
-  }
+    oc->phase = ORDER_PHASE_FINISHED_MHD_NO;
+    return;
+  } /* end 'out of stock' case */
 
   /* Everything in-stock, generate positive response */
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Order creation succeeded\n");
   {
     MHD_RESULT ret;
 
     ret = TALER_MHD_REPLY_JSON_PACK (
-      connection,
+      oc->connection,
       MHD_HTTP_OK,
       GNUNET_JSON_pack_string ("order_id",
-                               order_id),
+                               oc->order_id),
       GNUNET_JSON_pack_allow_null (
         GNUNET_JSON_pack_data_varsize (
           "token",
-          GNUNET_is_zero (claim_token)
+          GNUNET_is_zero (&oc->claim_token)
           ? NULL
-          : claim_token,
-          sizeof (*claim_token))));
+          : &oc->claim_token,
+          sizeof (oc->claim_token))));
     GNUNET_JSON_parse_free (spec);
-    return ret;
+    finalize_order (oc,
+                    ret);
+  }
+}
+
+
+/**
+ * Check that the contract is now well-formed.
+ *
+ * @param[in,out] oc order context
+ */
+static void
+check_contract (struct OrderContext *oc)
+{
+  struct TALER_PrivateContractHashP h_control;
+
+  switch (TALER_JSON_contract_hash (oc->order,
+                                    &h_control))
+  {
+  case GNUNET_SYSERR:
+    GNUNET_break (0);
+    reply_with_error (
+      oc,
+      MHD_HTTP_INTERNAL_SERVER_ERROR,
+      TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
+      "could not compute hash of patched order");
+    return;
+  case GNUNET_NO:
+    GNUNET_break_op (0);
+    reply_with_error (
+      oc,
+      MHD_HTTP_BAD_REQUEST,
+      TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
+      "order contained unallowed values");
+    return;
+  case GNUNET_OK:
+    oc->phase++;
+    return;
   }
+  GNUNET_assert (0);
+}
+
+
+/**
+ * Set list of acceptable exchanges in @a oc.
+ *
+ * @param[in,out] oc order context
+ */
+static void
+set_exchanges (struct OrderContext *oc)
+{
+  json_t *exchanges;
+
+  exchanges = TMH_exchange_get_acceptable (oc->wm);
+  if (0 == json_array_size (exchanges))
+  {
+    json_decref (exchanges);
+    /* FIXME: maybe there are exchanges for which we simply
+       did not yet get /wire replies. We should *try* to
+       run find_exchange() or something to give those
+       exchanges a chance to respond => need to suspend! */
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Cannot create order: lacking trusted exchanges for wire 
method `%s'\n",
+                oc->wm->wire_method);
+    reply_with_error (
+      oc,
+      MHD_HTTP_CONFLICT,
+      TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGES_FOR_WIRE_METHOD,
+      oc->wm->wire_method);
+    return;
+  }
+  GNUNET_assert (0 ==
+                 json_object_set (oc->order,
+                                  "exchanges",
+                                  exchanges));
+  oc->phase++;
 }
 
 
@@ -693,41 +807,18 @@ execute_order (struct MHD_Connection *connection,
  * Add missing fields to the order.  Upon success, continue
  * processing with execute_order().
  *
- * @param connection connection to write the result or error to
- * @param hc handler context for the request
- * @param h_post_data hash of the client's POST request, for idempotency checks
- * @param[in,out] order order to process (can be modified)
- * @param claim_token token to use for access control
- * @param refund_delay refund delay
- * @param inventory_products_length length of the @a inventory_products array
- * @param inventory_products array of products to add to @a order from our 
inventory
- * @param uuids_length length of the @a uuids array
- * @param uuids array of UUIDs used to reserve products from @a 
inventory_products
- * @param pos_key encoded key for verification payment
- * @param pos_algorithm  algorithm to compute the payment verification
- * @return MHD result code
+ * @param[in,out] oc order context
  */
-static MHD_RESULT
-patch_order (struct MHD_Connection *connection,
-             struct TMH_HandlerContext *hc,
-             const struct TALER_MerchantPostDataHashP *h_post_data,
-             json_t *order,
-             const struct TALER_ClaimTokenP *claim_token,
-             struct GNUNET_TIME_Relative refund_delay,
-             unsigned int inventory_products_length,
-             const struct InventoryProduct inventory_products[],
-             unsigned int uuids_length,
-             const struct GNUNET_Uuid uuids[],
-             const char *pos_key,
-             enum TALER_MerchantConfirmationAlgorithm pos_algorithm)
+static void
+patch_order (struct OrderContext *oc)
 {
   const struct TALER_MERCHANTDB_InstanceSettings *settings =
-    &hc->instance->settings;
+    &oc->hc->instance->settings;
   const char *order_id = NULL;
   const char *fulfillment_url = NULL;
   const char *merchant_base_url = NULL;
-  json_t *jmerchant = NULL;
-  json_t *delivery_location = NULL;
+  const json_t *jmerchant = NULL;
+  const json_t *delivery_location = NULL;
   struct TALER_Amount max_wire_fee = { 0 };
   struct TALER_Amount max_fee = { 0 };
   uint32_t wire_fee_amortization = 0;
@@ -737,8 +828,6 @@ patch_order (struct MHD_Connection *connection,
     = GNUNET_TIME_UNIT_ZERO_TS;
   struct GNUNET_TIME_Timestamp refund_deadline
     = GNUNET_TIME_UNIT_FOREVER_TS;
-  struct GNUNET_TIME_Timestamp pay_deadline
-    = GNUNET_TIME_UNIT_ZERO_TS;
   struct GNUNET_TIME_Timestamp wire_deadline
     = GNUNET_TIME_UNIT_FOREVER_TS;
   /* auto_refund only needs to be type-checked,
@@ -751,8 +840,8 @@ patch_order (struct MHD_Connection *connection,
                                &merchant_base_url),
       NULL),
     GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_json ("merchant",
-                             &jmerchant),
+      GNUNET_JSON_spec_object_const ("merchant",
+                                     &jmerchant),
       NULL),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_string ("order_id",
@@ -772,7 +861,7 @@ patch_order (struct MHD_Connection *connection,
       NULL),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_timestamp ("pay_deadline",
-                                  &pay_deadline),
+                                  &oc->pay_deadline),
       NULL),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
@@ -801,22 +890,22 @@ patch_order (struct MHD_Connection *connection,
                                       &auto_refund),
       NULL),
     GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_json ("delivery_location",
-                             &delivery_location),
+      GNUNET_JSON_spec_object_const ("delivery_location",
+                                     &delivery_location),
       NULL),
     GNUNET_JSON_spec_end ()
   };
   enum GNUNET_GenericReturnValue ret;
 
-  ret = TALER_MHD_parse_json_data (connection,
-                                   order,
+  ret = TALER_MHD_parse_json_data (oc->connection,
+                                   oc->order,
                                    spec);
   if (GNUNET_OK != ret)
   {
     GNUNET_break_op (0);
-    return (GNUNET_NO == ret)
-           ? MHD_YES
-           : MHD_NO;
+    finalize_order2 (oc,
+                     ret);
+    return;
   }
 
   /* Add order_id if it doesn't exist. */
@@ -835,8 +924,8 @@ patch_order (struct MHD_Connection *connection,
     if (NULL == tm_info)
     {
       GNUNET_JSON_parse_free (spec);
-      return TALER_MHD_reply_with_error (
-        connection,
+      reply_with_error (
+        oc,
         MHD_HTTP_INTERNAL_SERVER_ERROR,
         TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_LOCALTIME,
         NULL);
@@ -862,7 +951,7 @@ patch_order (struct MHD_Connection *connection,
                 "Assigning order ID `%s' server-side\n",
                 buf);
     GNUNET_break (0 ==
-                  json_object_set_new (order,
+                  json_object_set_new (oc->order,
                                        "order_id",
                                        jbuf));
     order_id = json_string_value (jbuf);
@@ -886,11 +975,11 @@ patch_order (struct MHD_Connection *connection,
                   "${ORDER_ID}"))
       {
         GNUNET_break_op (0);
-        GNUNET_JSON_parse_free (spec);
-        return TALER_MHD_reply_with_error (connection,
-                                           MHD_HTTP_BAD_REQUEST,
-                                           
TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                           "fulfillment_url");
+        reply_with_error (oc,
+                          MHD_HTTP_BAD_REQUEST,
+                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                          "fulfillment_url");
+        return;
       }
 
       GNUNET_asprintf (&nurl,
@@ -904,7 +993,7 @@ patch_order (struct MHD_Connection *connection,
                        pos + strlen ("${ORDER_ID}"));
       /* replace in JSON of the order */
       GNUNET_break (0 ==
-                    json_object_set_new (order,
+                    json_object_set_new (oc->order,
                                          "fulfillment_url",
                                          json_string (nurl)));
       GNUNET_free (nurl);
@@ -920,7 +1009,7 @@ patch_order (struct MHD_Connection *connection,
     if (GNUNET_TIME_absolute_is_zero (timestamp.abs_time))
     {
       GNUNET_assert (0 ==
-                     json_object_set_new (order,
+                     json_object_set_new (oc->order,
                                           "timestamp",
                                           GNUNET_JSON_from_timestamp (now)));
     }
@@ -928,7 +1017,7 @@ patch_order (struct MHD_Connection *connection,
     /* If no refund_deadline given, set one based on refund_delay.  */
     if (GNUNET_TIME_absolute_is_never (refund_deadline.abs_time))
     {
-      if (GNUNET_TIME_relative_is_zero (refund_delay))
+      if (GNUNET_TIME_relative_is_zero (oc->refund_delay))
       {
         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                     "Refund delay is zero, no refunds are possible for this 
order\n");
@@ -936,10 +1025,10 @@ patch_order (struct MHD_Connection *connection,
       }
       else
       {
-        refund_deadline = GNUNET_TIME_relative_to_timestamp (refund_delay);
+        refund_deadline = GNUNET_TIME_relative_to_timestamp (oc->refund_delay);
       }
       GNUNET_assert (0 ==
-                     json_object_set_new (order,
+                     json_object_set_new (oc->order,
                                           "refund_deadline",
                                           GNUNET_JSON_from_timestamp (
                                             refund_deadline)));
@@ -948,45 +1037,45 @@ patch_order (struct MHD_Connection *connection,
          (GNUNET_TIME_absolute_is_past (delivery_date.abs_time)) )
     {
       GNUNET_break_op (0);
-      GNUNET_JSON_parse_free (spec);
-      return TALER_MHD_reply_with_error (
-        connection,
+      reply_with_error (
+        oc,
         MHD_HTTP_BAD_REQUEST,
         TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_DELIVERY_DATE_IN_PAST,
         NULL);
+      return;
     }
   }
 
-  if (GNUNET_TIME_absolute_is_zero (pay_deadline.abs_time))
+  if (GNUNET_TIME_absolute_is_zero (oc->pay_deadline.abs_time))
   {
-    struct GNUNET_TIME_Timestamp t;
-
-    t = GNUNET_TIME_relative_to_timestamp (settings->default_pay_delay);
+    oc->pay_deadline = GNUNET_TIME_relative_to_timestamp (
+      settings->default_pay_delay);
     GNUNET_assert (0 ==
-                   json_object_set_new (order,
+                   json_object_set_new (oc->order,
                                         "pay_deadline",
-                                        GNUNET_JSON_from_timestamp (t)));
+                                        GNUNET_JSON_from_timestamp (
+                                          oc->pay_deadline)));
   }
-  else if (GNUNET_TIME_absolute_is_past (pay_deadline.abs_time))
+  else if (GNUNET_TIME_absolute_is_past (oc->pay_deadline.abs_time))
   {
     GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (
-      connection,
+    reply_with_error (
+      oc,
       MHD_HTTP_BAD_REQUEST,
       TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PAY_DEADLINE_IN_PAST,
       NULL);
+    return;
   }
   if ( (! GNUNET_TIME_absolute_is_zero (refund_deadline.abs_time)) &&
        (GNUNET_TIME_absolute_is_past (refund_deadline.abs_time)) )
   {
     GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (
-      connection,
+    reply_with_error (
+      oc,
       MHD_HTTP_BAD_REQUEST,
       TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_DEADLINE_IN_PAST,
       NULL);
+    return;
   }
 
   if (GNUNET_TIME_absolute_is_never (wire_deadline.abs_time))
@@ -995,22 +1084,21 @@ patch_order (struct MHD_Connection *connection,
 
     t = GNUNET_TIME_relative_to_timestamp (
       GNUNET_TIME_relative_max (settings->default_wire_transfer_delay,
-                                refund_delay));
+                                oc->refund_delay));
     wire_deadline = GNUNET_TIME_timestamp_max (refund_deadline,
                                                t);
     if (GNUNET_TIME_absolute_is_never (wire_deadline.abs_time))
     {
       GNUNET_break_op (0);
-      GNUNET_JSON_parse_free (spec);
-      return TALER_MHD_reply_with_error (
-        connection,
+      reply_with_error (
+        oc,
         MHD_HTTP_BAD_REQUEST,
         TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_WIRE_DEADLINE_IS_NEVER,
         "order:wire_transfer_deadline");
-
+      return;
     }
     GNUNET_assert (0 ==
-                   json_object_set_new (order,
+                   json_object_set_new (oc->order,
                                         "wire_transfer_deadline",
                                         GNUNET_JSON_from_timestamp (
                                           wire_deadline)));
@@ -1020,12 +1108,12 @@ patch_order (struct MHD_Connection *connection,
                                  refund_deadline))
   {
     GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (
-      connection,
+    reply_with_error (
+      oc,
       MHD_HTTP_BAD_REQUEST,
       TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_AFTER_WIRE_DEADLINE,
       "order:wire_transfer_deadline;order:refund_deadline");
+    return;
   }
 
   /* Note: total amount currency match checked
@@ -1035,7 +1123,7 @@ patch_order (struct MHD_Connection *connection,
   {
     GNUNET_assert (0 ==
                    json_object_set_new (
-                     order,
+                     oc->order,
                      "max_wire_fee",
                      TALER_JSON_from_amount 
(&settings->default_max_wire_fee)));
   }
@@ -1045,7 +1133,7 @@ patch_order (struct MHD_Connection *connection,
   {
     GNUNET_assert (0 ==
                    json_object_set_new (
-                     order,
+                     oc->order,
                      "max_fee",
                      TALER_JSON_from_amount (
                        &settings->default_max_deposit_fee)));
@@ -1054,7 +1142,7 @@ patch_order (struct MHD_Connection *connection,
   {
     GNUNET_assert (0 ==
                    json_object_set_new (
-                     order,
+                     oc->order,
                      "wire_fee_amortization",
                      json_integer (
                        (json_int_t) settings->default_wire_fee_amortization)));
@@ -1063,10 +1151,20 @@ patch_order (struct MHD_Connection *connection,
   {
     char *url;
 
-    url = make_merchant_base_url (connection,
+    url = make_merchant_base_url (oc->connection,
                                   settings->id);
+    if (NULL == url)
+    {
+      GNUNET_break_op (0);
+      reply_with_error (
+        oc,
+        MHD_HTTP_BAD_REQUEST,
+        TALER_EC_GENERIC_PARAMETER_MISSING,
+        "order:merchant_base_url");
+      return;
+    }
     GNUNET_assert (0 ==
-                   json_object_set_new (order,
+                   json_object_set_new (oc->order,
                                         "merchant_base_url",
                                         json_string (url)));
     GNUNET_free (url);
@@ -1075,24 +1173,24 @@ patch_order (struct MHD_Connection *connection,
            ('/' != merchant_base_url[strlen (merchant_base_url) - 1]))
   {
     GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (
-      connection,
+    reply_with_error (
+      oc,
       MHD_HTTP_BAD_REQUEST,
       TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR,
       "merchant_base_url is not valid");
+    return;
   }
 
-  /* Fill in merchant information if necessary */
+  /* Merchant information must not already be present */
   if (NULL != jmerchant)
   {
     GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (
-      connection,
+    reply_with_error (
+      oc,
       MHD_HTTP_BAD_REQUEST,
       TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR,
       "'merchant' field already set, but must be provided by backend");
+    return;
   }
 
   {
@@ -1140,91 +1238,39 @@ patch_order (struct MHD_Connection *connection,
       }
     }
     GNUNET_assert (0 ==
-                   json_object_set_new (order,
+                   json_object_set_new (oc->order,
                                         "merchant",
                                         jm));
   }
 
-  /* add fields to the contract that the backend should provide */
   GNUNET_assert (0 ==
-                 json_object_set (order,
-                                  "exchanges",
-                                  TMH_trusted_exchanges));
-  GNUNET_assert (0 ==
-                 json_object_set (order,
-                                  "auditors",
-                                  j_auditors));
-  GNUNET_assert (0 ==
-                 json_object_set_new (order,
+                 json_object_set_new (oc->order,
                                       "merchant_pub",
                                       GNUNET_JSON_from_data_auto (
-                                        &hc->instance->merchant_pub)));
+                                        &oc->hc->instance->merchant_pub)));
+
   if (GNUNET_OK !=
-      TALER_JSON_contract_seed_forgettable (order))
+      TALER_JSON_contract_seed_forgettable (oc->order))
   {
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (
-      connection,
+    reply_with_error (
+      oc,
       MHD_HTTP_BAD_REQUEST,
       TALER_EC_GENERIC_JSON_INVALID,
       "could not compute hash of order due to bogus forgettable fields");
+    return;
   }
 
   if ( (NULL != delivery_location) &&
        (! TMH_location_object_valid (delivery_location)) )
   {
     GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_BAD_REQUEST,
-                                       TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                       "delivery_location");
-  }
-
-  /* sanity check result */
-  {
-    struct TALER_PrivateContractHashP h_control;
-
-    switch (TALER_JSON_contract_hash (order,
-                                      &h_control))
-    {
-    case GNUNET_SYSERR:
-      GNUNET_break (0);
-      GNUNET_JSON_parse_free (spec);
-      return TALER_MHD_reply_with_error (
-        connection,
-        MHD_HTTP_INTERNAL_SERVER_ERROR,
-        TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
-        "could not compute hash of patched order");
-    case GNUNET_NO:
-      GNUNET_JSON_parse_free (spec);
-      GNUNET_break_op (0);
-      return TALER_MHD_reply_with_error (
-        connection,
-        MHD_HTTP_BAD_REQUEST,
-        TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
-        "order contained unallowed values");
-    case GNUNET_OK:
-      break;
-    }
-  }
-  {
-    MHD_RESULT mres;
-
-    mres = execute_order (connection,
-                          hc,
-                          h_post_data,
-                          order,
-                          claim_token,
-                          inventory_products_length,
-                          inventory_products,
-                          uuids_length,
-                          uuids,
-                          pos_key,
-                          pos_algorithm);
-    GNUNET_JSON_parse_free (spec);
-    return mres;
+    reply_with_error (oc,
+                      MHD_HTTP_BAD_REQUEST,
+                      TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                      "delivery_location");
+    return;
   }
+  oc->phase++;
 }
 
 
@@ -1233,77 +1279,44 @@ patch_order (struct MHD_Connection *connection,
  * order could be paid to @a order. On success, continue
  * processing with patch_order().
  *
- * @param connection connection to write the result or error to
- * @param hc handler context for the request
- * @param h_post_data hash of the client's POST request, for idempotency checks
- * @param[in,out] order order to process (can be modified)
- * @param claim_token token to use for access control
- * @param refund_delay refund delay
- * @param payment_target desired wire method, NULL for no preference
- * @param inventory_products_length length of the @a inventory_products array
- * @param inventory_products array of products to add to @a order from our 
inventory
- * @param uuids_length length of the @a uuids array
- * @param uuids array of UUIDs used to reserve products from @a 
inventory_products
- * @param pos_key encoded key for verification payment
- * @param pos_algorithm  algorithm to compute the payment verification
+ * @param[in,out] oc order context
  * @return MHD result code
  */
-static MHD_RESULT
-add_payment_details (struct MHD_Connection *connection,
-                     struct TMH_HandlerContext *hc,
-                     const struct TALER_MerchantPostDataHashP *h_post_data,
-                     json_t *order,
-                     const struct TALER_ClaimTokenP *claim_token,
-                     struct GNUNET_TIME_Relative refund_delay,
-                     const char *payment_target,
-                     unsigned int inventory_products_length,
-                     const struct InventoryProduct inventory_products[],
-                     unsigned int uuids_length,
-                     const struct GNUNET_Uuid uuids[],
-                     const char *pos_key,
-                     enum TALER_MerchantConfirmationAlgorithm pos_algorithm)
+static void
+add_payment_details (struct OrderContext *oc)
 {
   struct TMH_WireMethod *wm;
 
-  wm = hc->instance->wm_head;
+  wm = oc->hc->instance->wm_head;
   /* Locate wire method that has a matching payment target */
   while ( (NULL != wm) &&
           ( (! wm->active) ||
-            ( (NULL != payment_target) &&
-              (0 != strcasecmp (payment_target,
+            ( (NULL != oc->payment_target) &&
+              (0 != strcasecmp (oc->payment_target,
                                 wm->wire_method) ) ) ) )
     wm = wm->next;
   if (NULL == wm)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "No wire method available for instance '%s'\n",
-                hc->instance->settings.id);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_NOT_FOUND,
-                                       
TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_INSTANCE_CONFIGURATION_LACKS_WIRE,
-                                       payment_target);
+                oc->hc->instance->settings.id);
+    reply_with_error (oc,
+                      MHD_HTTP_NOT_FOUND,
+                      
TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_INSTANCE_CONFIGURATION_LACKS_WIRE,
+                      oc->payment_target);
+    return;
   }
+  oc->wm = wm;
   GNUNET_assert (0 ==
-                 json_object_set_new (order,
+                 json_object_set_new (oc->order,
                                       "h_wire",
                                       GNUNET_JSON_from_data_auto (
                                         &wm->h_wire)));
   GNUNET_assert (0 ==
-                 json_object_set_new (order,
+                 json_object_set_new (oc->order,
                                       "wire_method",
                                       json_string (wm->wire_method)));
-  return patch_order (connection,
-                      hc,
-                      h_post_data,
-                      order,
-                      claim_token,
-                      refund_delay,
-                      inventory_products_length,
-                      inventory_products,
-                      uuids_length,
-                      uuids,
-                      pos_key,
-                      pos_algorithm);
+  oc->phase++;
 }
 
 
@@ -1312,57 +1325,32 @@ add_payment_details (struct MHD_Connection *connection,
  * database about the details of those products. Upon success,
  * continue processing by calling add_payment_details().
  *
- * @param connection connection to write the result or error to
- * @param hc handler context for the request
- * @param h_post_data hash of the client's POST request, for idempotency checks
- * @param[in,out] order order to process (can be modified)
- * @param claim_token token to use for access control
- * @param refund_delay time window where it is possible to ask a refund
- * @param payment_target RFC8905 payment target type to find a matching 
merchant account
- * @param inventory_products_length length of the @a inventory_products array
- * @param inventory_products array of products to add to @a order from our 
inventory
- * @param uuids_length length of the @a uuids array
- * @param uuids array of UUIDs used to reserve products from @a 
inventory_products
- * @param pos_key encoded key for verification payment
- * @param pos_algorithm  algorithm to compute the payment verification
- * @return MHD result code
+ * @param[in,out] oc order context to process
  */
-static MHD_RESULT
-merge_inventory (struct MHD_Connection *connection,
-                 struct TMH_HandlerContext *hc,
-                 const struct TALER_MerchantPostDataHashP *h_post_data,
-                 json_t *order,
-                 const struct TALER_ClaimTokenP *claim_token,
-                 struct GNUNET_TIME_Relative refund_delay,
-                 const char *payment_target,
-                 unsigned int inventory_products_length,
-                 const struct InventoryProduct inventory_products[],
-                 unsigned int uuids_length,
-                 const struct GNUNET_Uuid uuids[],
-                 const char *pos_key,
-                 enum TALER_MerchantConfirmationAlgorithm pos_algorithm)
+static void
+merge_inventory (struct OrderContext *oc)
 {
   /**
    * inventory_products => instructions to add products to contract terms
    * order.products => contains products that are not from the backend-managed 
inventory.
    */
-  GNUNET_assert (NULL != order);
   {
-    json_t *jprod = json_object_get (order,
+    json_t *jprod = json_object_get (oc->order,
                                      "products");
     if (NULL == jprod)
     {
       GNUNET_assert (0 ==
-                     json_object_set_new (order,
+                     json_object_set_new (oc->order,
                                           "products",
                                           json_array ()));
     }
     else if (! TMH_products_array_valid (jprod))
     {
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_BAD_REQUEST,
-                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                         "order.products");
+      reply_with_error (oc,
+                        MHD_HTTP_BAD_REQUEST,
+                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                        "order.products");
+      return;
     }
   }
 
@@ -1370,14 +1358,15 @@ merge_inventory (struct MHD_Connection *connection,
   {
     json_t *np = json_array ();
 
-    for (unsigned int i = 0; i<inventory_products_length; i++)
+    GNUNET_assert (NULL != np);
+    for (unsigned int i = 0; i<oc->inventory_products_length; i++)
     {
       struct TALER_MERCHANTDB_ProductDetails pd;
       enum GNUNET_DB_QueryStatus qs;
 
       qs = TMH_db->lookup_product (TMH_db->cls,
-                                   hc->instance->settings.id,
-                                   inventory_products[i].product_id,
+                                   oc->hc->instance->settings.id,
+                                   oc->inventory_products[i].product_id,
                                    &pd);
       if (qs <= 0)
       {
@@ -1399,7 +1388,7 @@ merge_inventory (struct MHD_Connection *connection,
         case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                       "Product %s from order unknown\n",
-                      inventory_products[i].product_id);
+                      oc->inventory_products[i].product_id);
           http_status = MHD_HTTP_NOT_FOUND;
           ec = TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN;
           break;
@@ -1408,10 +1397,11 @@ merge_inventory (struct MHD_Connection *connection,
           GNUNET_assert (0);
         }
         json_decref (np);
-        return TALER_MHD_reply_with_error (connection,
-                                           http_status,
-                                           ec,
-                                           inventory_products[i].product_id);
+        reply_with_error (oc,
+                          http_status,
+                          ec,
+                          oc->inventory_products[i].product_id);
+        return;
       }
       {
         json_t *p;
@@ -1429,9 +1419,9 @@ merge_inventory (struct MHD_Connection *connection,
                                         pd.taxes),
           GNUNET_JSON_pack_string ("image",
                                    pd.image),
-          GNUNET_JSON_pack_uint64 ("quantity",
-                                   inventory_products[i].
-                                   quantity));
+          GNUNET_JSON_pack_uint64 (
+            "quantity",
+            oc->inventory_products[i].quantity));
         GNUNET_assert (NULL != p);
         GNUNET_assert (0 ==
                        json_array_append_new (np,
@@ -1446,66 +1436,41 @@ merge_inventory (struct MHD_Connection *connection,
     {
       json_t *xp;
 
-      xp = json_object_get (order,
+      xp = json_object_get (oc->order,
                             "products");
       GNUNET_assert (NULL != xp);
       json_array_extend (xp, np);
       json_decref (np);
     }
   }
-  return add_payment_details (connection,
-                              hc,
-                              h_post_data,
-                              order,
-                              claim_token,
-                              refund_delay,
-                              payment_target,
-                              inventory_products_length,
-                              inventory_products,
-                              uuids_length,
-                              uuids,
-                              pos_key,
-                              pos_algorithm);
+  oc->phase++;
 }
 
 
-MHD_RESULT
-TMH_private_post_orders_with_pos_secrets (
-  const struct TMH_RequestHandler *rh,
-  struct MHD_Connection *connection,
-  struct TMH_HandlerContext *hc,
-  const char *pos_key,
-  enum TALER_MerchantConfirmationAlgorithm pos_algorithm)
+static void
+parse_order_request (struct OrderContext *oc)
 {
-  json_t *order;
-  struct GNUNET_TIME_Relative refund_delay = GNUNET_TIME_UNIT_ZERO;
-  const char *payment_target = NULL;
-  json_t *ip = NULL;
-  unsigned int ips_len = 0;
-  struct InventoryProduct *ips = NULL;
-  unsigned int uuids_len = 0;
-  json_t *uuid;
-  struct GNUNET_Uuid *uuids = NULL;
-  struct TALER_ClaimTokenP claim_token;
+  const json_t *ip = NULL;
+  const json_t *uuid = NULL;
   bool create_token = true; /* default */
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_json ("order",
-                           &order),
+                           &oc->order),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_relative_time ("refund_delay",
-                                      &refund_delay),
+                                      &oc->refund_delay),
       NULL),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_string ("payment_target",
-                               &payment_target),
+                               &oc->payment_target),
       NULL),
     GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_json ("inventory_products",
-                             &ip),
+      GNUNET_JSON_spec_array_const ("inventory_products",
+                                    &ip),
       NULL),
     GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_json ("lock_uuids",
-                             &uuid),
+      GNUNET_JSON_spec_array_const ("lock_uuids",
+                                    &uuid),
       NULL),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_bool ("create_token",
@@ -1514,83 +1479,69 @@ TMH_private_post_orders_with_pos_secrets (
     GNUNET_JSON_spec_end ()
   };
   enum GNUNET_GenericReturnValue ret;
-  struct TALER_MerchantPostDataHashP h_post_data;
 
-  (void) rh;
-  ret = TALER_MHD_parse_json_data (connection,
-                                   hc->request_body,
+  ret = TALER_MHD_parse_json_data (oc->connection,
+                                   oc->hc->request_body,
                                    spec);
   if (GNUNET_OK != ret)
-    return (GNUNET_NO == ret)
-           ? MHD_YES
-           : MHD_NO;
-
+  {
+    GNUNET_break_op (0);
+    finalize_order2 (oc,
+                     ret);
+    return;
+  }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Refund delay is %s\n",
-              GNUNET_TIME_relative2s (refund_delay,
+              GNUNET_TIME_relative2s (oc->refund_delay,
                                       false));
-
   TMH_db->expire_locks (TMH_db->cls);
   if (create_token)
   {
     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
-                                &claim_token,
-                                sizeof (claim_token));
-  }
-  else
-  {
-    /* we use all-zeros for 'no token' */
-    memset (&claim_token,
-            0,
-            sizeof (claim_token));
+                                &oc->claim_token,
+                                sizeof (oc->claim_token));
   }
-
   /* Compute h_post_data (for idempotency check) */
   {
     char *req_body_enc;
 
     /* Dump normalized JSON to string. */
-    if (NULL == (req_body_enc = json_dumps (hc->request_body,
-                                            JSON_ENCODE_ANY
-                                            | JSON_COMPACT
-                                            | JSON_SORT_KEYS)))
+    if (NULL == (req_body_enc
+                   = json_dumps (oc->hc->request_body,
+                                 JSON_ENCODE_ANY
+                                 | JSON_COMPACT
+                                 | JSON_SORT_KEYS)))
     {
       GNUNET_break (0);
       GNUNET_JSON_parse_free (spec);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         TALER_EC_GENERIC_ALLOCATION_FAILURE,
-                                         "request body normalization for 
hashing");
+      reply_with_error (oc,
+                        MHD_HTTP_INTERNAL_SERVER_ERROR,
+                        TALER_EC_GENERIC_ALLOCATION_FAILURE,
+                        "request body normalization for hashing");
+      return;
     }
     GNUNET_CRYPTO_hash (req_body_enc,
                         strlen (req_body_enc),
-                        &h_post_data.hash);
+                        &oc->h_post_data.hash);
     GNUNET_free (req_body_enc);
   }
 
   /* parse the inventory_products (optionally given) */
   if (NULL != ip)
   {
-    if (! json_is_array (ip))
-    {
-      GNUNET_JSON_parse_free (spec);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_BAD_REQUEST,
-                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                         "inventory_products");
-    }
-    GNUNET_array_grow (ips,
-                       ips_len,
+    GNUNET_array_grow (oc->inventory_products,
+                       oc->inventory_products_length,
                        json_array_size (ip));
-    for (unsigned int i = 0; i<ips_len; i++)
+    for (unsigned int i = 0; i<oc->inventory_products_length; i++)
     {
+      struct InventoryProduct *ipr = &oc->inventory_products[i];
       const char *error_name;
       unsigned int error_line;
       struct GNUNET_JSON_Specification ispec[] = {
         GNUNET_JSON_spec_string ("product_id",
-                                 &ips[i].product_id),
+                                 &ipr->product_id),
         GNUNET_JSON_spec_uint32 ("quantity",
-                                 &ips[i].quantity),
+                                 &ipr->quantity),
         GNUNET_JSON_spec_end ()
       };
 
@@ -1602,19 +1553,16 @@ TMH_private_post_orders_with_pos_secrets (
       if (GNUNET_OK != ret)
       {
         GNUNET_break_op (0);
-        GNUNET_array_grow (ips,
-                           ips_len,
-                           0);
         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                     "Product parsing failed at #%u: %s:%u\n",
                     i,
                     error_name,
                     error_line);
-        GNUNET_JSON_parse_free (spec);
-        return TALER_MHD_reply_with_error (connection,
-                                           MHD_HTTP_BAD_REQUEST,
-                                           
TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                           "inventory_products");
+        reply_with_error (oc,
+                          MHD_HTTP_BAD_REQUEST,
+                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                          "inventory_products");
+        return;
       }
     }
   }
@@ -1622,21 +1570,10 @@ TMH_private_post_orders_with_pos_secrets (
   /* parse the lock_uuids (optionally given) */
   if (NULL != uuid)
   {
-    if (! json_is_array (uuid))
-    {
-      GNUNET_array_grow (ips,
-                         ips_len,
-                         0);
-      GNUNET_JSON_parse_free (spec);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_BAD_REQUEST,
-                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                         "lock_uuids");
-    }
-    GNUNET_array_grow (uuids,
-                       uuids_len,
+    GNUNET_array_grow (oc->uuids,
+                       oc->uuids_length,
                        json_array_size (uuid));
-    for (unsigned int i = 0; i<uuids_len; i++)
+    for (unsigned int i = 0; i<oc->uuids_length; i++)
     {
       json_t *ui = json_array_get (uuid,
                                    i);
@@ -1644,51 +1581,81 @@ TMH_private_post_orders_with_pos_secrets (
       if (! json_is_string (ui))
       {
         GNUNET_break_op (0);
-        GNUNET_array_grow (ips,
-                           ips_len,
-                           0);
-        GNUNET_array_grow (uuids,
-                           uuids_len,
-                           0);
-        GNUNET_JSON_parse_free (spec);
         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                     "UUID parsing failed at #%u\n",
                     i);
-        return TALER_MHD_reply_with_error (connection,
-                                           MHD_HTTP_BAD_REQUEST,
-                                           
TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                           "lock_uuids");
+        reply_with_error (oc,
+                          MHD_HTTP_BAD_REQUEST,
+                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                          "lock_uuids");
+        return;
       }
       TMH_uuid_from_string (json_string_value (ui),
-                            &uuids[i]);
+                            &oc->uuids[i]);
     }
   }
+  oc->phase++;
+}
 
-  /* Finally, start by completing the order */
+
+MHD_RESULT
+TMH_private_post_orders_with_pos_secrets (
+  const struct TMH_RequestHandler *rh,
+  struct MHD_Connection *connection,
+  struct TMH_HandlerContext *hc,
+  const char *pos_key,
+  enum TALER_MerchantConfirmationAlgorithm pos_algorithm)
+{
+  struct OrderContext *oc = hc->ctx;
+
+  if (NULL == oc)
   {
-    MHD_RESULT res;
-
-    res = merge_inventory (connection,
-                           hc,
-                           &h_post_data,
-                           order,
-                           &claim_token,
-                           refund_delay,
-                           payment_target,
-                           ips_len,
-                           ips,
-                           uuids_len,
-                           uuids,
-                           pos_key,
-                           pos_algorithm);
-    GNUNET_array_grow (ips,
-                       ips_len,
-                       0);
-    GNUNET_array_grow (uuids,
-                       uuids_len,
-                       0);
-    GNUNET_JSON_parse_free (spec);
-    return res;
+    oc = GNUNET_new (struct OrderContext);
+    hc->ctx = oc;
+    hc->cc = &clean_order;
+    oc->connection = connection;
+    oc->hc = hc;
+    if (NULL != pos_key)
+      oc->pos_key = GNUNET_strdup (pos_key);
+    oc->pos_algorithm = pos_algorithm;
+  }
+  while (1)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Processing order in phase %d\n",
+                oc->phase);
+    switch (oc->phase)
+    {
+    case ORDER_PHASE_INIT:
+      parse_order_request (oc);
+      break;
+    case ORDER_PHASE_MERGE_INVENTORY:
+      merge_inventory (oc);
+      break;
+    case ORDER_PHASE_ADD_PAYMENT_DETAILS:
+      add_payment_details (oc);
+      break;
+    case ORDER_PHASE_PATCH_ORDER:
+      patch_order (oc);
+      break;
+    case ORDER_PHASE_SET_EXCHANGES:
+      set_exchanges (oc);
+      break;
+    case ORDER_PHASE_CHECK_CONTRACT:
+      check_contract (oc);
+      break;
+    case ORDER_PHASE_EXECUTE_ORDER:
+      execute_order (oc);
+      break;
+    case ORDER_PHASE_FINISHED_MHD_YES:
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Finished processing order (1)\n");
+      return MHD_YES;
+    case ORDER_PHASE_FINISHED_MHD_NO:
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Finished processing order (0)\n");
+      return MHD_NO;
+    }
   }
 }
 
diff --git a/src/backend/taler-merchant-httpd_private-post-reserves.c 
b/src/backend/taler-merchant-httpd_private-post-reserves.c
index 4001badd..801cbf9c 100644
--- a/src/backend/taler-merchant-httpd_private-post-reserves.c
+++ b/src/backend/taler-merchant-httpd_private-post-reserves.c
@@ -210,17 +210,18 @@ reserve_context_cleanup (void *cls)
  * @param cls closure with our `struct PostReserveContext *`
  * @param hr HTTP response details
  * @param eh handle to the exchange context
- * @param exchange_trusted true if this exchange is trusted by config
+ * @param ih internal handle to the exchange
  */
 static void
 handle_exchange (void *cls,
                  const struct TALER_EXCHANGE_HttpResponse *hr,
                  struct TALER_EXCHANGE_Handle *eh,
-                 bool exchange_trusted)
+                 struct TMH_Exchange *ih)
 {
   struct PostReserveContext *rc = cls;
   const struct TALER_EXCHANGE_Keys *keys;
 
+  (void) ih;
   rc->fo = NULL;
   if (NULL != rc->timeout_task)
   {
diff --git a/src/backend/taler-merchant-httpd_private-post-transfers.c 
b/src/backend/taler-merchant-httpd_private-post-transfers.c
index a57c5e3b..4a324b6a 100644
--- a/src/backend/taler-merchant-httpd_private-post-transfers.c
+++ b/src/backend/taler-merchant-httpd_private-post-transfers.c
@@ -23,7 +23,6 @@
 #include <jansson.h>
 #include <taler/taler_signatures.h>
 #include <taler/taler_json_lib.h>
-#include "taler-merchant-httpd_auditors.h"
 #include "taler-merchant-httpd_exchanges.h"
 #include "taler-merchant-httpd_helper.h"
 #include "taler-merchant-httpd_private-post-transfers.h"
@@ -552,17 +551,17 @@ wire_transfer_cb (void *cls,
  * @param cls the `struct PostTransfersContext`
  * @param hr HTTP response details
  * @param eh NULL if exchange was not found to be acceptable
- * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config
+ * @param ih internal handle to the exchange
  */
 static void
 process_transfer_with_exchange (void *cls,
                                 const struct TALER_EXCHANGE_HttpResponse *hr,
                                 struct TALER_EXCHANGE_Handle *eh,
-                                bool exchange_trusted)
+                                struct TMH_Exchange *ih)
 {
   struct PostTransfersContext *ptc = cls;
 
-  (void) exchange_trusted;
+  (void) ih;
   ptc->fo = NULL;
   if (NULL == hr)
   {
diff --git a/src/backend/taler-merchant-httpd_reserves.c 
b/src/backend/taler-merchant-httpd_reserves.c
index fec18440..3d11a954 100644
--- a/src/backend/taler-merchant-httpd_reserves.c
+++ b/src/backend/taler-merchant-httpd_reserves.c
@@ -232,16 +232,17 @@ reserve_cb (void *cls,
  * @param cls closure
  * @param hr HTTP response details
  * @param eh handle to the exchange context
- * @param exchange_trusted true if this exchange is trusted by config
+ * @param ih internal handle to the exchange
  */
 static void
 find_cb (void *cls,
          const struct TALER_EXCHANGE_HttpResponse *hr,
          struct TALER_EXCHANGE_Handle *eh,
-         bool exchange_trusted)
+         struct TMH_Exchange *ih)
 {
   struct Reserve *r = cls;
 
+  (void) ih;
   r->fo = NULL;
   if (NULL == eh)
   {
diff --git a/src/testing/test_merchant_api.conf 
b/src/testing/test_merchant_api.conf
index e0451124..f270d670 100644
--- a/src/testing/test_merchant_api.conf
+++ b/src/testing/test_merchant_api.conf
@@ -64,28 +64,6 @@ CURRENCY = EUR
 BASE_URL = http://the.auditor/
 
 
-#######################################################
-# Configuration for ??? Is this used?
-#######################################################
-
-# Auditors must be in sections "auditor-", the rest of the section
-# name could be anything.
-[auditor-ezb]
-# Informal name of the auditor. Just for the user.
-NAME = European Central Bank
-
-# URL of the auditor (especially for in the future, when the
-# auditor offers an automated issue reporting system).
-# Not really used today.
-URL = http://taler.ezb.eu/
-
-# This is the important bit: the signing key of the auditor.
-PUBLIC_KEY = 9QXF7XY7E9VPV47B5Z806NDFSX2VJ79SVHHD29QEQ3BG31ANHZ60
-
-# Which currency is this auditor trusted for?
-CURRENCY = EUR
-
-
 ###################################################
 # Configuration for the exchange for the testcase #
 ###################################################
@@ -103,15 +81,12 @@ MASTER_PUBLIC_KEY = 
T1VVFQZZARQ1CMF4BN58EE7SKTW5AV2BS18S87ZEGYS4S29J6DNG
 # Base URL of the exchange.
 BASE_URL = "http://localhost:8081/";
 
-
 [exchangedb-postgres]
 CONFIG = "postgres:///talercheck"
 
-
 [auditordb-postgres]
 CONFIG = postgres:///talercheck
 
-
 # Account of the EXCHANGE
 [exchange-account-exchange]
 # What is the exchange's bank account (with the "Taler Bank" demo system)?

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



reply via email to

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