gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] 04/277: starting v1 protocol dispatching logic


From: gnunet
Subject: [taler-merchant] 04/277: starting v1 protocol dispatching logic
Date: Sun, 05 Jul 2020 20:48:37 +0200

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

grothoff pushed a commit to branch master
in repository merchant.

commit 0b8e550d09635f762033626c8c12b7b4a0d0faf7
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Fri Apr 17 14:07:06 2020 +0200

    starting v1 protocol dispatching logic
---
 src/backend/Makefile.am                            |    6 +-
 src/backend/legacy.c                               |  509 ++++++++
 src/backend/taler-merchant-httpd.c                 | 1335 +++++---------------
 src/backend/taler-merchant-httpd.h                 |  308 ++---
 src/backend/taler-merchant-httpd_config.c          |   95 +-
 src/backend/taler-merchant-httpd_config.h          |   12 +-
 src/backend/taler-merchant-httpd_exchanges.c       |   28 +-
 src/backend/taler-merchant-httpd_mhd.c             |   38 +-
 src/backend/taler-merchant-httpd_mhd.h             |   26 +-
 ...> taler-merchant-httpd_private-get-instances.c} |    2 -
 ...nfig.h => taler-merchant-httpd_transfers-get.h} |   30 +-
 src/backenddb/merchantdb_plugin.c                  |    4 +-
 src/backenddb/plugin_merchantdb_postgres.c         |    2 +-
 src/include/taler_merchantdb_lib.h                 |    2 +-
 14 files changed, 1026 insertions(+), 1371 deletions(-)

diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 082c35f..f47cf08 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -21,10 +21,12 @@ 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_check-payment.c taler-merchant-httpd_check-payment.h \
   taler-merchant-httpd_exchanges.c taler-merchant-httpd_exchanges.h \
+  taler-merchant-httpd_mhd.c taler-merchant-httpd_mhd.h
+
+DEAD = \
+  taler-merchant-httpd_check-payment.c taler-merchant-httpd_check-payment.h \
   taler-merchant-httpd_history.c taler-merchant-httpd_history.h \
-  taler-merchant-httpd_mhd.c taler-merchant-httpd_mhd.h \
   taler-merchant-httpd_order.c taler-merchant-httpd_order.h \
   taler-merchant-httpd_pay.c taler-merchant-httpd_pay.h \
   taler-merchant-httpd_poll-payment.c taler-merchant-httpd_poll-payment.h \
diff --git a/src/backend/legacy.c b/src/backend/legacy.c
new file mode 100644
index 0000000..c105ecb
--- /dev/null
+++ b/src/backend/legacy.c
@@ -0,0 +1,509 @@
+/**
+ * Create a taler://pay/ URI for the given @a con and @a order_id
+ * and @a session_id and @a instance_id.
+ *
+ * @param con HTTP connection
+ * @param order_id the order id
+ * @param session_id session, may be NULL
+ * @param instance_id instance, may be "default"
+ * @return corresponding taler://pay/ URI, or NULL on missing "host"
+ */
+char *
+TMH_make_taler_pay_uri (struct MHD_Connection *con,
+                        const char *order_id,
+                        const char *session_id,
+                        const char *instance_id)
+{
+  const char *host;
+  const char *forwarded_host;
+  const char *uri_path;
+  const char *uri_instance_id;
+  const char *query;
+  char *result;
+
+  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 == uri_path)
+    uri_path = "-";
+  if (NULL != forwarded_host)
+    host = forwarded_host;
+  if (0 == strcmp (instance_id,
+                   "default"))
+    uri_instance_id = "-";
+  else
+    uri_instance_id = instance_id;
+  if (NULL == host)
+  {
+    /* Should never happen, at least the host header should be defined */
+    GNUNET_break (0);
+    return NULL;
+  }
+
+  if (GNUNET_YES == TALER_mhd_is_https (con))
+    query = "";
+  else
+    query = "?insecure=1";
+  GNUNET_assert (NULL != order_id);
+  GNUNET_assert (0 < GNUNET_asprintf (&result,
+                                      "taler://pay/%s/%s/%s/%s%s%s%s",
+                                      host,
+                                      uri_path,
+                                      uri_instance_id,
+                                      order_id,
+                                      (NULL == session_id) ? "" : "/",
+                                      (NULL == session_id) ? "" : session_id,
+                                      query));
+  return result;
+}
+
+
+/**
+ * Closure for the #wireformat_iterator_cb().
+ */
+struct WireFormatIteratorContext
+{
+  /**
+   * The global iteration context.
+   */
+  struct IterateInstancesCls *iic;
+
+  /**
+   * The merchant instance we are currently building.
+   */
+  struct MerchantInstance *mi;
+
+  /**
+   * Set to #GNUNET_YES if the default instance was found.
+   */
+  int default_instance;
+};
+
+
+/**
+ * Callback that looks for 'merchant-account-*' sections,
+ * and populates our wire method according to the data
+ *
+ * @param cls closure with a `struct WireFormatIteratorContext *`
+ * @section section name this callback gets
+ */
+static void
+wireformat_iterator_cb (void *cls,
+                        const char *section)
+{
+  struct WireFormatIteratorContext *wfic = cls;
+  struct MerchantInstance *mi = wfic->mi;
+  struct IterateInstancesCls *iic = wfic->iic;
+  char *instance_option;
+  struct WireMethod *wm;
+  char *payto;
+  char *fn;
+  json_t *j;
+  struct GNUNET_HashCode jh_wire;
+  char *wire_file_mode;
+
+  if (0 != strncasecmp (section,
+                        "merchant-account-",
+                        strlen ("merchant-account-")))
+    return;
+  GNUNET_asprintf (&instance_option,
+                   "HONOR_%s",
+                   mi->id);
+  if (GNUNET_YES !=
+      GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                            section,
+                                            instance_option))
+  {
+    GNUNET_free (instance_option);
+    return;
+  }
+  GNUNET_free (instance_option);
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             section,
+                                             "PAYTO_URI",
+                                             &payto))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               section,
+                               "PAYTO_URI");
+    iic->ret = GNUNET_SYSERR;
+    return;
+  }
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                               section,
+                                               "WIRE_RESPONSE",
+                                               &fn))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               section,
+                               "WIRE_RESPONSE");
+    GNUNET_free (payto);
+    iic->ret = GNUNET_SYSERR;
+    return;
+  }
+
+  /* Try loading existing JSON from file */
+  if (GNUNET_YES ==
+      GNUNET_DISK_file_test (fn))
+  {
+    json_error_t err;
+    char *url;
+
+    if (NULL ==
+        (j = json_load_file (fn,
+                             JSON_REJECT_DUPLICATES,
+                             &err)))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Failed to load JSON from `%s': %s at %d:%d\n",
+                  fn,
+                  err.text,
+                  err.line,
+                  err.column);
+      GNUNET_free (fn);
+      GNUNET_free (payto);
+      iic->ret = GNUNET_SYSERR;
+      return;
+    }
+    url = TALER_JSON_wire_to_payto (j);
+    if (NULL == url)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "URL missing in `%s', disabling account `%s'\n",
+                  fn,
+                  section);
+      GNUNET_free (fn);
+      GNUNET_free (payto);
+      iic->ret = GNUNET_SYSERR;
+      return;
+    }
+    if (0 != strcasecmp (url,
+                         payto))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "URL `%s' does not match configuration `%s', disabling 
account `%s'\n",
+                  url,
+                  payto,
+                  section);
+      GNUNET_free (fn);
+      GNUNET_free (payto);
+      GNUNET_free (url);
+      iic->ret = GNUNET_SYSERR;
+      return;
+    }
+    GNUNET_free (url);
+  }
+  else /* need to generate JSON */
+  {
+    struct GNUNET_HashCode salt;
+    char *salt_str;
+
+    GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
+                                &salt,
+                                sizeof (salt));
+    salt_str = GNUNET_STRINGS_data_to_string_alloc (&salt,
+                                                    sizeof (salt));
+    j = json_pack ("{s:s, s:s}",
+                   "payto_uri", payto,
+                   "salt", salt_str);
+    GNUNET_free (salt_str);
+
+    /* Make sure every path component exists.  */
+    if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (fn))
+    {
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                                "mkdir",
+                                fn);
+      GNUNET_free (fn);
+      GNUNET_free (payto);
+      json_decref (j);
+      iic->ret = GNUNET_SYSERR;
+      return;
+    }
+
+    if (0 != json_dump_file (j,
+                             fn,
+                             JSON_COMPACT | JSON_SORT_KEYS))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Failed to write hashed wire details to `%s'\n",
+                  fn);
+      GNUNET_free (fn);
+      GNUNET_free (payto);
+      json_decref (j);
+      iic->ret = GNUNET_SYSERR;
+      return;
+    }
+
+    if (GNUNET_OK ==
+        GNUNET_CONFIGURATION_get_value_string (cfg,
+                                               section,
+                                               "WIRE_FILE_MODE",
+                                               &wire_file_mode))
+    {
+      errno = 0;
+      mode_t mode = (mode_t) strtoul (wire_file_mode,
+                                      NULL,
+                                      8);
+      if (0 != errno)
+      {
+        GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                                   section,
+                                   "WIRE_FILE_MODE",
+                                   "Must be octal number\n");
+        iic->ret = GNUNET_SYSERR;
+        GNUNET_free (fn);
+        return;
+      }
+      if (0 != chmod (fn, mode))
+      {
+        TALER_LOG_ERROR ("chmod failed on %s\n", fn);
+        iic->ret = GNUNET_SYSERR;
+        GNUNET_free (fn);
+        return;
+      }
+    }
+  }
+
+  GNUNET_free (fn);
+
+  if (GNUNET_OK !=
+      TALER_JSON_merchant_wire_signature_hash (j,
+                                               &jh_wire))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to hash wire input\n");
+    GNUNET_free (fn);
+    GNUNET_free (payto);
+    json_decref (j);
+    iic->ret = GNUNET_SYSERR;
+    return;
+  }
+
+  wm = GNUNET_new (struct WireMethod);
+  wm->wire_method = TALER_payto_get_method (payto);
+  GNUNET_free (payto);
+  GNUNET_asprintf (&instance_option,
+                   "ACTIVE_%s",
+                   mi->id);
+  wm->active = GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                                     section,
+                                                     instance_option);
+  GNUNET_free (instance_option);
+  if (GNUNET_YES == wm->active)
+    GNUNET_CONTAINER_DLL_insert (mi->wm_head,
+                                 mi->wm_tail,
+                                 wm);
+  else
+    GNUNET_CONTAINER_DLL_insert_tail (mi->wm_head,
+                                      mi->wm_tail,
+                                      wm);
+  wm->j_wire = j;
+  wm->h_wire = jh_wire;
+}
+
+
+/**
+ * Callback that looks for 'instance-*' sections,
+ * and populates accordingly each instance's data
+ *
+ * @param cls closure of type `struct IterateInstancesCls`
+ * @section section name this callback gets
+ */
+static void
+instances_iterator_cb (void *cls,
+                       const char *section)
+{
+  struct IterateInstancesCls *iic = cls;
+  char *token;
+  struct MerchantInstance *mi;
+  /* used as hashmap keys */
+  struct GNUNET_HashCode h_pk;
+  struct GNUNET_HashCode h_id;
+
+  if (0 != strncasecmp (section,
+                        "instance-",
+                        strlen ("instance-")))
+    return;
+  /** Get id **/
+  token = strrchr (section, '-');
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Extracted token: %s\n",
+              token + 1);
+  mi = GNUNET_new (struct MerchantInstance);
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             section,
+                                             "NAME",
+                                             &mi->name))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               section,
+                               "NAME");
+    GNUNET_free (mi);
+    iic->ret = GNUNET_SYSERR;
+    return;
+  }
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                               section,
+                                               "KEYFILE",
+                                               &mi->keyfile))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               section,
+                               "KEYFILE");
+    GNUNET_free (mi->name);
+    GNUNET_free (mi);
+    iic->ret = GNUNET_SYSERR;
+    return;
+  }
+  if (GNUNET_OK ==
+      GNUNET_CONFIGURATION_get_value_string (cfg,
+                                             section,
+                                             "TIP_EXCHANGE",
+                                             &mi->tip_exchange))
+  {
+    char *tip_reserves;
+
+    if (GNUNET_OK !=
+        GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                                 section,
+                                                 "TIP_RESERVE_PRIV_FILENAME",
+                                                 &tip_reserves))
+    {
+      GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                                 section,
+                                 "TIP_RESERVE_PRIV_FILENAME");
+      GNUNET_free (mi->keyfile);
+      GNUNET_free (mi->name);
+      GNUNET_free (mi);
+      iic->ret = GNUNET_SYSERR;
+      return;
+    }
+    if (GNUNET_OK !=
+        GNUNET_CRYPTO_eddsa_key_from_file (tip_reserves,
+                                           GNUNET_NO,
+                                           &mi->tip_reserve.eddsa_priv))
+    {
+      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                                 section,
+                                 "TIP_RESERVE_PRIV_FILENAME",
+                                 "Failed to read private key");
+      GNUNET_free (tip_reserves);
+      GNUNET_free (mi->keyfile);
+      GNUNET_free (mi->name);
+      GNUNET_free (mi);
+      iic->ret = GNUNET_SYSERR;
+      return;
+    }
+    GNUNET_free (tip_reserves);
+  }
+
+  if (GNUNET_SYSERR ==
+      GNUNET_CRYPTO_eddsa_key_from_file (mi->keyfile,
+                                         GNUNET_YES,
+                                         &mi->privkey.eddsa_priv))
+  {
+    GNUNET_break (0);
+    GNUNET_free (mi->keyfile);
+    GNUNET_free (mi->name);
+    GNUNET_free (mi);
+    iic->ret = GNUNET_SYSERR;
+    return;
+  }
+  GNUNET_CRYPTO_eddsa_key_get_public (&mi->privkey.eddsa_priv,
+                                      &mi->pubkey.eddsa_pub);
+
+  mi->id = GNUNET_strdup (token + 1);
+  if (0 == strcasecmp ("default",
+                       mi->id))
+    iic->default_instance = GNUNET_YES;
+
+  GNUNET_CRYPTO_hash (mi->id,
+                      strlen (mi->id),
+                      &h_id);
+  if (GNUNET_OK !=
+      GNUNET_CONTAINER_multihashmap_put (by_id_map,
+                                         &h_id,
+                                         mi,
+                                         
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to put an entry into the 'by_id' hashmap\n");
+    iic->ret = GNUNET_SYSERR;
+    GNUNET_free (mi->keyfile);
+    GNUNET_free (mi->name);
+    GNUNET_free (mi);
+    return;
+  }
+  GNUNET_CRYPTO_hash (&mi->pubkey.eddsa_pub,
+                      sizeof (struct GNUNET_CRYPTO_EddsaPublicKey),
+                      &h_pk);
+
+
+  /* Initialize wireformats */
+  {
+    struct WireFormatIteratorContext wfic = {
+      .iic = iic,
+      .mi = mi
+    };
+
+    GNUNET_CONFIGURATION_iterate_sections (cfg,
+                                           &wireformat_iterator_cb,
+                                           &wfic);
+  }
+  if (NULL == mi->wm_head)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to load wire formats for instance `%s'\n",
+                mi->id);
+    iic->ret = GNUNET_SYSERR;
+  }
+
+}
+
+
+/**
+ * Iterate over each merchant instance, in order to populate
+ * each instance's own data
+ *
+ * @return #GNUNET_OK if successful, #GNUNET_SYSERR upon errors
+ *          (for example, if no "default" instance is defined)
+ */
+static int
+iterate_instances (void)
+{
+  struct IterateInstancesCls iic;
+
+  iic.default_instance = GNUNET_NO;
+  iic.ret = GNUNET_OK;
+  GNUNET_CONFIGURATION_iterate_sections (cfg,
+                                         &instances_iterator_cb,
+                                         &iic);
+
+  if (GNUNET_NO == iic.default_instance)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "No default merchant instance found\n");
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_OK != iic.ret)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "At least one instance was not successfully parsed\n");
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index 86b3630..60979d2 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  (C) 2014-2018 Taler Systems SA
+  (C) 2014-2020 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
@@ -32,23 +32,8 @@
 #include "taler_merchantdb_lib.h"
 #include "taler-merchant-httpd.h"
 #include "taler-merchant-httpd_auditors.h"
-#include "taler-merchant-httpd_check-payment.h"
 #include "taler-merchant-httpd_exchanges.h"
-#include "taler-merchant-httpd_history.h"
 #include "taler-merchant-httpd_mhd.h"
-#include "taler-merchant-httpd_order.h"
-#include "taler-merchant-httpd_pay.h"
-#include "taler-merchant-httpd_poll-payment.h"
-#include "taler-merchant-httpd_proposal.h"
-#include "taler-merchant-httpd_refund.h"
-#include "taler-merchant-httpd_refund_increase.h"
-#include "taler-merchant-httpd_refund_lookup.h"
-#include "taler-merchant-httpd_track-transaction.h"
-#include "taler-merchant-httpd_track-transfer.h"
-#include "taler-merchant-httpd_tip-authorize.h"
-#include "taler-merchant-httpd_tip-pickup.h"
-#include "taler-merchant-httpd_tip-query.h"
-#include "taler-merchant-httpd_tip-reserve-helper.h"
 #include "taler-merchant-httpd_config.h"
 
 /**
@@ -58,101 +43,36 @@
 
 
 /**
- * Used by the iterator of the various merchant's instances given
- * in configuration
+ * Which currency do we use?
  */
-struct IterateInstancesCls
-{
-
-  /**
-   * Current index in the global array of #MerchantInstance
-   * types. Used by the callback in order to know which index
-   * is associated to the element being processed.
-   */
-  unsigned int current_index;
-
-  /**
-   * Flag indicating whether config contains a default instance
-   */
-  unsigned int default_instance;
+char *TMH_currency;
 
-  /**
-   * Tells if the parsing encountered any error. We need this
-   * field since the iterator must return void
-   */
-  unsigned int ret;
-};
+/**
+ * Inform the auditor for all deposit confirmations (global option)
+ */
+int TMH_force_audit;
 
+/**
+ * Connection handle to the our database
+ */
+struct TALER_MERCHANTDB_Plugin *TMH_db;
 
 /**
  * Hashmap pointing at merchant instances by 'id'. An 'id' is
  * just a string that identifies a merchant instance. When a frontend
  * needs to specify an instance to the backend, it does so by 'id'
  */
-struct GNUNET_CONTAINER_MultiHashMap *by_id_map;
-
-/**
- * Hashmap pointing at merchant instances by public key. This map
- * is mainly used to check whether there is more than one instance
- * using the same key
- */
-struct GNUNET_CONTAINER_MultiHashMap *by_kpub_map;
+static struct GNUNET_CONTAINER_MultiHashMap *by_id_map;
 
 /**
  * The port we are running on
  */
 static uint16_t port;
 
-/**
- * This value tells the exchange by which date this merchant would like
- * to receive the funds for a deposited payment
- */
-struct GNUNET_TIME_Relative default_wire_transfer_delay;
-
-/**
- * Locations from the configuration.  Mapping from
- * label to location data.
- */
-json_t *default_locations;
-
-/**
- * If the frontend does NOT specify a payment deadline, how long should
- * offers we make be valid by default?
- */
-struct GNUNET_TIME_Relative default_pay_deadline;
-
-/**
- * Default maximum wire fee to assume, unless stated differently in the 
proposal
- * already.
- */
-struct TALER_Amount default_max_wire_fee;
-
-/**
- * Default max deposit fee that the merchant is willing to
- * pay; if deposit costs more, then the customer will cover
- * the difference.
- */
-struct TALER_Amount default_max_deposit_fee;
-
-/**
- * Default factor for wire fee amortization.
- */
-unsigned long long default_wire_fee_amortization;
-
 /**
  * Should a "Connection: close" header be added to each HTTP response?
  */
-static int TMH_merchant_connection_close;
-
-/**
- * Which currency do we use?
- */
-char *TMH_currency;
-
-/**
- * Inform the auditor for all deposit confirmations (global option)
- */
-int TMH_force_audit;
+static int merchant_connection_close;
 
 /**
  * Task running the HTTP server.
@@ -164,11 +84,6 @@ static struct GNUNET_SCHEDULER_Task *mhd_task;
  */
 static int result;
 
-/**
- * Connection handle to the our database
- */
-struct TALER_MERCHANTDB_Plugin *db;
-
 /**
  * The MHD Daemon
  */
@@ -178,39 +93,39 @@ static struct MHD_Daemon *mhd;
  * MIN-Heap of suspended connections to resume when the timeout expires,
  * ordered by timeout. Values are of type `struct MHD_Connection`
  */
-struct GNUNET_CONTAINER_Heap *resume_timeout_heap;
+static struct GNUNET_CONTAINER_Heap *resume_timeout_heap;
 
 /**
  * Hash map from H(order_id,merchant_pub) to `struct MHD_Connection`
  * entries to resume when a payment is made for the given order.
  */
-struct GNUNET_CONTAINER_MultiHashMap *payment_trigger_map;
+static struct GNUNET_CONTAINER_MultiHashMap *payment_trigger_map;
 
 /**
  * Task responsible for timeouts in the #resume_timeout_heap.
  */
-struct GNUNET_SCHEDULER_Task *resume_timeout_task;
+static struct GNUNET_SCHEDULER_Task *resume_timeout_task;
 
 /**
  * Our configuration.
  */
-static struct GNUNET_CONFIGURATION_Handle *cfg;
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
 
 
 /**
- * Callback that frees all the elements in the hashmap
+ * Callback that frees all the instances in the hashmap
  *
  * @param cls closure, NULL
  * @param key current key
- * @param value a `struct MerchantInstance`
+ * @param value a `struct TMH_MerchantInstance`
  */
 static int
-hashmap_free (void *cls,
-              const struct GNUNET_HashCode *key,
-              void *value)
+instance_free (void *cls,
+               const struct GNUNET_HashCode *key,
+               void *value)
 {
-  struct MerchantInstance *mi = value;
-  struct WireMethod *wm;
+  struct TMH_MerchantInstance *mi = value;
+  struct TMH_WireMethod *wm;
 
   (void) cls;
   (void) key;
@@ -266,10 +181,10 @@ payment_trigger_free (void *cls,
  * @param mpub an instance public key
  * @param key[out] set to the hash map key to use
  */
-void
-TMH_compute_pay_key (const char *order_id,
-                     const struct TALER_MerchantPublicKeyP *mpub,
-                     struct GNUNET_HashCode *key)
+static void
+compute_pay_key (const char *order_id,
+                 const struct TALER_MerchantPublicKeyP *mpub,
+                 struct GNUNET_HashCode *key)
 {
   size_t olen = strlen (order_id);
   char buf[sizeof (*mpub) + olen];
@@ -287,7 +202,6 @@ TMH_compute_pay_key (const char *order_id,
               "Pay key for %s is %s\n",
               order_id,
               GNUNET_h2s (key));
-
 }
 
 
@@ -332,14 +246,21 @@ do_resume (void *cls)
 /**
  * Suspend connection from @a sc until payment has been received.
  *
+ * @param order_id the order that we are waiting on
+ * @param mi the merchant instance we are waiting on
  * @param sc connection to suspend
  * @param min_refund refund amount we are waiting on to be exceeded before 
resuming,
  *                   NULL if we are not waiting for refunds
  */
 void
-TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc,
+TMH_long_poll_suspend (const char *order_id,
+                       const struct TMH_MerchantInstance *mi,
+                       struct TMH_SuspendedConnection *sc,
                        const struct TALER_Amount *min_refund)
 {
+  compute_pay_key (order_id,
+                   &mi->pubkey,
+                   &sc->key);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Suspending operation on key %s\n",
               GNUNET_h2s (&sc->key));
@@ -350,7 +271,7 @@ TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc,
                                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
   if (NULL != min_refund)
   {
-    sc->awaiting_refund = GNUNET_YES;
+    sc->awaiting_refund = true;
     sc->refund_expected = *min_refund;
   }
   sc->hn = GNUNET_CONTAINER_heap_insert (resume_timeout_heap,
@@ -385,7 +306,7 @@ resume_operation (void *cls,
   const struct TALER_Amount *have_refund = cls;
   struct TMH_SuspendedConnection *sc = value;
 
-  if ( (GNUNET_YES == sc->awaiting_refund) &&
+  if ( (sc->awaiting_refund) &&
        ( (NULL == have_refund) ||
          (1 != TALER_amount_cmp (have_refund,
                                  &sc->refund_expected)) ) )
@@ -415,20 +336,20 @@ resume_operation (void *cls,
  * Find out if we have any clients long-polling for @a order_id to be
  * confirmed at merchant @a mpub, and if so, tell them to resume.
  *
- * @param order_id the order that was paid
- * @param mpub the merchant's public key of the instance where the payment 
happened
+ * @param order_id the order that was paid or refunded
+ * @param mi the merchant instance where the payment or refund happened
  * @param have_refund refunded amount, NULL if there was no refund
  */
 void
 TMH_long_poll_resume (const char *order_id,
-                      const struct TALER_MerchantPublicKeyP *mpub,
+                      const struct TMH_MerchantInstance *mi,
                       const struct TALER_Amount *have_refund)
 {
   struct GNUNET_HashCode key;
 
-  TMH_compute_pay_key (order_id,
-                       mpub,
-                       &key);
+  compute_pay_key (order_id,
+                   &mi->pubkey,
+                   &key);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Resuming operations suspended pending payment on key %s\n",
               GNUNET_h2s (&key));
@@ -442,73 +363,6 @@ TMH_long_poll_resume (const char *order_id,
 }
 
 
-/**
- * Create a taler://pay/ URI for the given @a con and @a order_id
- * and @a session_id and @a instance_id.
- *
- * @param con HTTP connection
- * @param order_id the order id
- * @param session_id session, may be NULL
- * @param instance_id instance, may be "default"
- * @return corresponding taler://pay/ URI, or NULL on missing "host"
- */
-char *
-TMH_make_taler_pay_uri (struct MHD_Connection *con,
-                        const char *order_id,
-                        const char *session_id,
-                        const char *instance_id)
-{
-  const char *host;
-  const char *forwarded_host;
-  const char *uri_path;
-  const char *uri_instance_id;
-  const char *query;
-  char *result;
-
-  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 == uri_path)
-    uri_path = "-";
-  if (NULL != forwarded_host)
-    host = forwarded_host;
-  if (0 == strcmp (instance_id,
-                   "default"))
-    uri_instance_id = "-";
-  else
-    uri_instance_id = instance_id;
-  if (NULL == host)
-  {
-    /* Should never happen, at least the host header should be defined */
-    GNUNET_break (0);
-    return NULL;
-  }
-
-  if (GNUNET_YES == TALER_mhd_is_https (con))
-    query = "";
-  else
-    query = "?insecure=1";
-  GNUNET_assert (NULL != order_id);
-  GNUNET_assert (0 < GNUNET_asprintf (&result,
-                                      "taler://pay/%s/%s/%s/%s%s%s%s",
-                                      host,
-                                      uri_path,
-                                      uri_instance_id,
-                                      order_id,
-                                      (NULL == session_id) ? "" : "/",
-                                      (NULL == session_id) ? "" : session_id,
-                                      query));
-  return result;
-}
-
-
 /**
  * Shutdown task (magically invoked when the application is being
  * quit)
@@ -521,10 +375,12 @@ do_shutdown (void *cls)
   struct TMH_SuspendedConnection *sc;
 
   (void) cls;
-  MH_force_pc_resume ();
-  MH_force_trh_resume ();
-  MH_force_refund_resume ();
-  MH_force_tip_pickup_resume ();
+#if 0
+  TMH_force_pc_resume ();
+  TMH_force_trh_resume ();
+  TMH_force_refund_resume ();
+  TMH_force_tip_pickup_resume ();
+#endif
   if (NULL != mhd_task)
   {
     GNUNET_SCHEDULER_cancel (mhd_task);
@@ -556,10 +412,10 @@ do_shutdown (void *cls)
     MHD_stop_daemon (mhd);
     mhd = NULL;
   }
-  if (NULL != db)
+  if (NULL != TMH_db)
   {
-    TALER_MERCHANTDB_plugin_unload (db);
-    db = NULL;
+    TALER_MERCHANTDB_plugin_unload (TMH_db);
+    TMH_db = NULL;
   }
   TMH_EXCHANGES_done ();
   TMH_AUDITORS_done ();
@@ -574,16 +430,11 @@ do_shutdown (void *cls)
   if (NULL != by_id_map)
   {
     GNUNET_CONTAINER_multihashmap_iterate (by_id_map,
-                                           &hashmap_free,
+                                           &instance_free,
                                            NULL);
     GNUNET_CONTAINER_multihashmap_destroy (by_id_map);
     by_id_map = NULL;
   }
-  if (NULL != by_kpub_map)
-  {
-    GNUNET_CONTAINER_multihashmap_destroy (by_kpub_map);
-    by_kpub_map = NULL;
-  }
 }
 
 
@@ -607,15 +458,21 @@ handle_mhd_completion_callback (void *cls,
                                 void **con_cls,
                                 enum MHD_RequestTerminationCode toe)
 {
-  struct TM_HandlerContext *hc = *con_cls;
+  struct TMH_HandlerContext *hc = *con_cls;
 
   if (NULL == hc)
     return;
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Finished handling request for `%s' with status %d\n",
-              hc->rh->url,
+              hc->url,
               (int) toe);
-  hc->cc (hc);
+  if (NULL != hc->cc)
+    hc->cc (hc->ctx);
+  TALER_MHD_parse_post_cleanup_callback (hc->json_parse_context);
+  GNUNET_free_non_null (hc->infix);
+  if (NULL != hc->json)
+    json_decref (hc->json);
+  GNUNET_free (hc);
   *con_cls = NULL;
 }
 
@@ -625,7 +482,6 @@ handle_mhd_completion_callback (void *cls,
  * starts the task waiting for them.
  */
 static struct GNUNET_SCHEDULER_Task *
-
 prepare_daemon (void);
 
 
@@ -728,505 +584,19 @@ prepare_daemon (void)
 }
 
 
-/**
- * Callback that looks for 'merchant-location-*' sections,
- * and populates @a default_locations.
- *
- * @param cls closure
- * @section section name this callback gets
- */
-static void
-locations_iterator_cb (void *cls,
-                       const char *section)
-{
-  static const char *keys[] = {
-    "country",
-    "city",
-    "state",
-    "region",
-    "province",
-    "zip_code",
-    "street",
-    "street_number",
-    NULL,
-  };
-  const char *prefix = "merchant-location-";
-  const char *substr = strstr (section, prefix);
-  const char *locname;
-  json_t *loc;
-
-  (void) cls;
-  if ( (NULL == substr) || (substr != section) )
-    return;
-  locname = section + strlen (prefix);
-  if (0 == strlen (locname))
-    return;
-  GNUNET_assert (json_is_object (default_locations));
-
-  loc = json_object ();
-  json_object_set_new (default_locations,
-                       locname,
-                       loc);
-  for (unsigned int pos = 0; NULL != keys[pos]; pos++)
-  {
-    char *val;
-
-    if (GNUNET_OK ==
-        GNUNET_CONFIGURATION_get_value_string (cfg,
-                                               section,
-                                               keys[pos],
-                                               &val))
-    {
-      json_object_set_new (loc,
-                           keys[pos],
-                           json_string (val));
-      GNUNET_free (val);
-    }
-  }
-}
-
-
-/**
- * Closure for the #wireformat_iterator_cb().
- */
-struct WireFormatIteratorContext
-{
-  /**
-   * The global iteration context.
-   */
-  struct IterateInstancesCls *iic;
-
-  /**
-   * The merchant instance we are currently building.
-   */
-  struct MerchantInstance *mi;
-
-  /**
-   * Set to #GNUNET_YES if the default instance was found.
-   */
-  int default_instance;
-};
-
-
-/**
- * Callback that looks for 'merchant-account-*' sections,
- * and populates our wire method according to the data
- *
- * @param cls closure with a `struct WireFormatIteratorContext *`
- * @section section name this callback gets
- */
-static void
-wireformat_iterator_cb (void *cls,
-                        const char *section)
-{
-  struct WireFormatIteratorContext *wfic = cls;
-  struct MerchantInstance *mi = wfic->mi;
-  struct IterateInstancesCls *iic = wfic->iic;
-  char *instance_option;
-  struct WireMethod *wm;
-  char *payto;
-  char *fn;
-  json_t *j;
-  struct GNUNET_HashCode jh_wire;
-  char *wire_file_mode;
-
-  if (0 != strncasecmp (section,
-                        "merchant-account-",
-                        strlen ("merchant-account-")))
-    return;
-  GNUNET_asprintf (&instance_option,
-                   "HONOR_%s",
-                   mi->id);
-  if (GNUNET_YES !=
-      GNUNET_CONFIGURATION_get_value_yesno (cfg,
-                                            section,
-                                            instance_option))
-  {
-    GNUNET_free (instance_option);
-    return;
-  }
-  GNUNET_free (instance_option);
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (cfg,
-                                             section,
-                                             "PAYTO_URI",
-                                             &payto))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               section,
-                               "PAYTO_URI");
-    iic->ret = GNUNET_SYSERR;
-    return;
-  }
-
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (cfg,
-                                               section,
-                                               "WIRE_RESPONSE",
-                                               &fn))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               section,
-                               "WIRE_RESPONSE");
-    GNUNET_free (payto);
-    iic->ret = GNUNET_SYSERR;
-    return;
-  }
-
-  /* Try loading existing JSON from file */
-  if (GNUNET_YES ==
-      GNUNET_DISK_file_test (fn))
-  {
-    json_error_t err;
-    char *url;
-
-    if (NULL ==
-        (j = json_load_file (fn,
-                             JSON_REJECT_DUPLICATES,
-                             &err)))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Failed to load JSON from `%s': %s at %d:%d\n",
-                  fn,
-                  err.text,
-                  err.line,
-                  err.column);
-      GNUNET_free (fn);
-      GNUNET_free (payto);
-      iic->ret = GNUNET_SYSERR;
-      return;
-    }
-    url = TALER_JSON_wire_to_payto (j);
-    if (NULL == url)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "URL missing in `%s', disabling account `%s'\n",
-                  fn,
-                  section);
-      GNUNET_free (fn);
-      GNUNET_free (payto);
-      iic->ret = GNUNET_SYSERR;
-      return;
-    }
-    if (0 != strcasecmp (url,
-                         payto))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "URL `%s' does not match configuration `%s', disabling 
account `%s'\n",
-                  url,
-                  payto,
-                  section);
-      GNUNET_free (fn);
-      GNUNET_free (payto);
-      GNUNET_free (url);
-      iic->ret = GNUNET_SYSERR;
-      return;
-    }
-    GNUNET_free (url);
-  }
-  else /* need to generate JSON */
-  {
-    struct GNUNET_HashCode salt;
-    char *salt_str;
-
-    GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
-                                &salt,
-                                sizeof (salt));
-    salt_str = GNUNET_STRINGS_data_to_string_alloc (&salt,
-                                                    sizeof (salt));
-    j = json_pack ("{s:s, s:s}",
-                   "payto_uri", payto,
-                   "salt", salt_str);
-    GNUNET_free (salt_str);
-
-    /* Make sure every path component exists.  */
-    if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (fn))
-    {
-      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
-                                "mkdir",
-                                fn);
-      GNUNET_free (fn);
-      GNUNET_free (payto);
-      json_decref (j);
-      iic->ret = GNUNET_SYSERR;
-      return;
-    }
-
-    if (0 != json_dump_file (j,
-                             fn,
-                             JSON_COMPACT | JSON_SORT_KEYS))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Failed to write hashed wire details to `%s'\n",
-                  fn);
-      GNUNET_free (fn);
-      GNUNET_free (payto);
-      json_decref (j);
-      iic->ret = GNUNET_SYSERR;
-      return;
-    }
-
-    if (GNUNET_OK ==
-        GNUNET_CONFIGURATION_get_value_string (cfg,
-                                               section,
-                                               "WIRE_FILE_MODE",
-                                               &wire_file_mode))
-    {
-      errno = 0;
-      mode_t mode = (mode_t) strtoul (wire_file_mode,
-                                      NULL,
-                                      8);
-      if (0 != errno)
-      {
-        GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                                   section,
-                                   "WIRE_FILE_MODE",
-                                   "Must be octal number\n");
-        iic->ret = GNUNET_SYSERR;
-        GNUNET_free (fn);
-        return;
-      }
-      if (0 != chmod (fn, mode))
-      {
-        TALER_LOG_ERROR ("chmod failed on %s\n", fn);
-        iic->ret = GNUNET_SYSERR;
-        GNUNET_free (fn);
-        return;
-      }
-    }
-  }
-
-  GNUNET_free (fn);
-
-  if (GNUNET_OK !=
-      TALER_JSON_merchant_wire_signature_hash (j,
-                                               &jh_wire))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to hash wire input\n");
-    GNUNET_free (fn);
-    GNUNET_free (payto);
-    json_decref (j);
-    iic->ret = GNUNET_SYSERR;
-    return;
-  }
-
-  wm = GNUNET_new (struct WireMethod);
-  wm->wire_method = TALER_payto_get_method (payto);
-  GNUNET_free (payto);
-  GNUNET_asprintf (&instance_option,
-                   "ACTIVE_%s",
-                   mi->id);
-  wm->active = GNUNET_CONFIGURATION_get_value_yesno (cfg,
-                                                     section,
-                                                     instance_option);
-  GNUNET_free (instance_option);
-  if (GNUNET_YES == wm->active)
-    GNUNET_CONTAINER_DLL_insert (mi->wm_head,
-                                 mi->wm_tail,
-                                 wm);
-  else
-    GNUNET_CONTAINER_DLL_insert_tail (mi->wm_head,
-                                      mi->wm_tail,
-                                      wm);
-  wm->j_wire = j;
-  wm->h_wire = jh_wire;
-}
-
-
-/**
- * Callback that looks for 'instance-*' sections,
- * and populates accordingly each instance's data
- *
- * @param cls closure of type `struct IterateInstancesCls`
- * @section section name this callback gets
- */
-static void
-instances_iterator_cb (void *cls,
-                       const char *section)
-{
-  struct IterateInstancesCls *iic = cls;
-  char *token;
-  struct MerchantInstance *mi;
-  /* used as hashmap keys */
-  struct GNUNET_HashCode h_pk;
-  struct GNUNET_HashCode h_id;
-
-  if (0 != strncasecmp (section,
-                        "instance-",
-                        strlen ("instance-")))
-    return;
-  /** Get id **/
-  token = strrchr (section, '-');
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Extracted token: %s\n",
-              token + 1);
-  mi = GNUNET_new (struct MerchantInstance);
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (cfg,
-                                             section,
-                                             "NAME",
-                                             &mi->name))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               section,
-                               "NAME");
-    GNUNET_free (mi);
-    iic->ret = GNUNET_SYSERR;
-    return;
-  }
-
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (cfg,
-                                               section,
-                                               "KEYFILE",
-                                               &mi->keyfile))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               section,
-                               "KEYFILE");
-    GNUNET_free (mi->name);
-    GNUNET_free (mi);
-    iic->ret = GNUNET_SYSERR;
-    return;
-  }
-  if (GNUNET_OK ==
-      GNUNET_CONFIGURATION_get_value_string (cfg,
-                                             section,
-                                             "TIP_EXCHANGE",
-                                             &mi->tip_exchange))
-  {
-    char *tip_reserves;
-
-    if (GNUNET_OK !=
-        GNUNET_CONFIGURATION_get_value_filename (cfg,
-                                                 section,
-                                                 "TIP_RESERVE_PRIV_FILENAME",
-                                                 &tip_reserves))
-    {
-      GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                                 section,
-                                 "TIP_RESERVE_PRIV_FILENAME");
-      GNUNET_free (mi->keyfile);
-      GNUNET_free (mi->name);
-      GNUNET_free (mi);
-      iic->ret = GNUNET_SYSERR;
-      return;
-    }
-    if (GNUNET_OK !=
-        GNUNET_CRYPTO_eddsa_key_from_file (tip_reserves,
-                                           GNUNET_NO,
-                                           &mi->tip_reserve.eddsa_priv))
-    {
-      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                                 section,
-                                 "TIP_RESERVE_PRIV_FILENAME",
-                                 "Failed to read private key");
-      GNUNET_free (tip_reserves);
-      GNUNET_free (mi->keyfile);
-      GNUNET_free (mi->name);
-      GNUNET_free (mi);
-      iic->ret = GNUNET_SYSERR;
-      return;
-    }
-    GNUNET_free (tip_reserves);
-  }
-
-  if (GNUNET_SYSERR ==
-      GNUNET_CRYPTO_eddsa_key_from_file (mi->keyfile,
-                                         GNUNET_YES,
-                                         &mi->privkey.eddsa_priv))
-  {
-    GNUNET_break (0);
-    GNUNET_free (mi->keyfile);
-    GNUNET_free (mi->name);
-    GNUNET_free (mi);
-    iic->ret = GNUNET_SYSERR;
-    return;
-  }
-  GNUNET_CRYPTO_eddsa_key_get_public (&mi->privkey.eddsa_priv,
-                                      &mi->pubkey.eddsa_pub);
-
-  mi->id = GNUNET_strdup (token + 1);
-  if (0 == strcasecmp ("default",
-                       mi->id))
-    iic->default_instance = GNUNET_YES;
-
-  GNUNET_CRYPTO_hash (mi->id,
-                      strlen (mi->id),
-                      &h_id);
-  if (GNUNET_OK !=
-      GNUNET_CONTAINER_multihashmap_put (by_id_map,
-                                         &h_id,
-                                         mi,
-                                         
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to put an entry into the 'by_id' hashmap\n");
-    iic->ret = GNUNET_SYSERR;
-    GNUNET_free (mi->keyfile);
-    GNUNET_free (mi->name);
-    GNUNET_free (mi);
-    return;
-  }
-  GNUNET_CRYPTO_hash (&mi->pubkey.eddsa_pub,
-                      sizeof (struct GNUNET_CRYPTO_EddsaPublicKey),
-                      &h_pk);
-  if (GNUNET_OK !=
-      GNUNET_CONTAINER_multihashmap_put (by_kpub_map,
-                                         &h_pk,
-                                         mi,
-                                         
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to put an entry into the 'by_kpub_map' hashmap\n");
-    GNUNET_assert (GNUNET_OK ==
-                   GNUNET_CONTAINER_multihashmap_remove (by_id_map,
-                                                         &h_id,
-                                                         mi));
-    iic->ret = GNUNET_SYSERR;
-    GNUNET_free (mi->keyfile);
-    GNUNET_free (mi->name);
-    GNUNET_free (mi);
-    return;
-  }
-
-  /* Initialize wireformats */
-  {
-    struct WireFormatIteratorContext wfic = {
-      .iic = iic,
-      .mi = mi
-    };
-
-    GNUNET_CONFIGURATION_iterate_sections (cfg,
-                                           &wireformat_iterator_cb,
-                                           &wfic);
-  }
-  if (NULL == mi->wm_head)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to load wire formats for instance `%s'\n",
-                mi->id);
-    iic->ret = GNUNET_SYSERR;
-  }
-
-}
-
-
 /**
  * Lookup a merchant instance by its instance ID.
  *
  * @param instance_id identifier of the instance to resolve
  * @return NULL if that instance is unknown to us
  */
-static struct MerchantInstance *
+static struct TMH_MerchantInstance *
 lookup_instance (const char *instance_id)
 {
   struct GNUNET_HashCode h_instance;
 
   if (NULL == instance_id)
     instance_id = "default";
-
   GNUNET_CRYPTO_hash (instance_id,
                       strlen (instance_id),
                       &h_instance);
@@ -1241,57 +611,6 @@ lookup_instance (const char *instance_id)
 }
 
 
-/**
- * Iterate over locations in config in order to populate
- * the location data.
- *
- * @return #GNUNET_OK if successful, #GNUNET_SYSERR upon errors
- */
-static void
-iterate_locations (void)
-{
-  GNUNET_assert (NULL == default_locations);
-  default_locations = json_object ();
-  GNUNET_CONFIGURATION_iterate_sections (cfg,
-                                         &locations_iterator_cb,
-                                         NULL);
-}
-
-
-/**
- * Iterate over each merchant instance, in order to populate
- * each instance's own data
- *
- * @return #GNUNET_OK if successful, #GNUNET_SYSERR upon errors
- *          (for example, if no "default" instance is defined)
- */
-static int
-iterate_instances (void)
-{
-  struct IterateInstancesCls iic;
-
-  iic.default_instance = GNUNET_NO;
-  iic.ret = GNUNET_OK;
-  GNUNET_CONFIGURATION_iterate_sections (cfg,
-                                         &instances_iterator_cb,
-                                         &iic);
-
-  if (GNUNET_NO == iic.default_instance)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "No default merchant instance found\n");
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_OK != iic.ret)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "At least one instance was not successfully parsed\n");
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
 /**
  * A client has requested the given url using the given method
  * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
@@ -1341,158 +660,143 @@ url_handler (void *cls,
              size_t *upload_data_size,
              void **con_cls)
 {
-  static struct TMH_RequestHandler handlers[] = {
-    /* Landing page, tell humans to go away. */
-    { "/", MHD_HTTP_METHOD_GET, "text/plain",
-      "Hello, I'm a merchant's Taler backend. This HTTP server is not for 
humans.\n",
-      0,
-      &TMH_MHD_handler_static_response, MHD_HTTP_OK },
-    { "/agpl", MHD_HTTP_METHOD_GET, "text/plain",
-      NULL, 0,
-      &TMH_MHD_handler_agpl_redirect, MHD_HTTP_FOUND },
-    { "/track/transfer", MHD_HTTP_METHOD_GET, "application/json",
-      NULL, 0,
-      &MH_handler_track_transfer, MHD_HTTP_OK},
-    { "/track/transfer", NULL, "text/plain",
-      "Only GET is allowed", 0,
-      &TMH_MHD_handler_static_response, MHD_HTTP_OK},
-    { "/track/transaction", MHD_HTTP_METHOD_GET, "application/json",
-      NULL, 0,
-      &MH_handler_track_transaction, MHD_HTTP_OK},
-    { "/track/transaction", NULL, "text/plain",
-      "Only GET is allowed", 0,
-      &TMH_MHD_handler_static_response, MHD_HTTP_OK},
-    { "/history", MHD_HTTP_METHOD_GET, "text/plain",
-      "Only GET is allowed", 0,
-      &MH_handler_history, MHD_HTTP_OK},
-    { "/order", MHD_HTTP_METHOD_POST, "application/json",
-      NULL, 0,
-      &MH_handler_order_post, MHD_HTTP_OK },
-    { "/refund", MHD_HTTP_METHOD_POST, "application/json",
-      NULL, 0,
-      &MH_handler_refund_increase, MHD_HTTP_OK},
-    { "/tip-authorize", MHD_HTTP_METHOD_POST, "text/plain",
-      NULL, 0,
-      &MH_handler_tip_authorize, MHD_HTTP_OK},
-    { "/tip-query", MHD_HTTP_METHOD_GET, "text/plain",
-      NULL, 0,
-      &MH_handler_tip_query, MHD_HTTP_OK},
-    { "/check-payment", MHD_HTTP_METHOD_GET, "text/plain",
-      NULL, 0,
-      &MH_handler_check_payment, MHD_HTTP_OK},
-    { "/config", MHD_HTTP_METHOD_GET, "text/plain",
-      NULL, 0,
-      &MH_handler_config, MHD_HTTP_OK},
-    {NULL, NULL, NULL, NULL, 0, 0 }
+  static struct TMH_RequestHandler private_handlers[] = {
+    {
+      .url_prefix = "/",
+      .method = MHD_HTTP_METHOD_GET,
+      .mime_type = "text/plain",
+      .skip_instance = true,
+      .data = "This is a GNU Taler merchant backend. See 
https://taler.net/.\n";,
+      .data_size = strlen (
+        "This is a GNU Taler merchant backend. See https://taler.net/.\n";),
+      .handler = &TMH_MHD_handler_static_response,
+      .response_code = MHD_HTTP_OK
+    },
+    {
+      .url_prefix = "/agpl",
+      .method = MHD_HTTP_METHOD_GET,
+      .skip_instance = true,
+      .handler = &TMH_MHD_handler_agpl_redirect
+    },
+    {
+      NULL
+    }
   };
   static struct TMH_RequestHandler public_handlers[] = {
-    { "/pay", MHD_HTTP_METHOD_POST, "application/json",
-      NULL, 0,
-      &MH_handler_pay, MHD_HTTP_OK },
-    { "/proposal", MHD_HTTP_METHOD_GET, "text/plain",
-      NULL, 0,
-      &MH_handler_proposal_lookup, MHD_HTTP_OK },
-    { "/tip-pickup", MHD_HTTP_METHOD_POST, "text/plain",
-      NULL, 0,
-      &MH_handler_tip_pickup, MHD_HTTP_OK },
-    { "/refund", MHD_HTTP_METHOD_GET, "text/plain",
-      NULL, 0,
-      &MH_handler_refund_lookup, MHD_HTTP_OK },
-    { "/tip-pickup", MHD_HTTP_METHOD_GET, "text/plain",
-      NULL, 0,
-      &MH_handler_tip_pickup_get, MHD_HTTP_OK },
-    { "/poll-payment", MHD_HTTP_METHOD_GET, "text/plain",
-      NULL, 0,
-      &MH_handler_poll_payment, MHD_HTTP_OK},
-    { "/config", MHD_HTTP_METHOD_GET, "text/plain",
-      NULL, 0,
-      &MH_handler_config, MHD_HTTP_OK},
-    {NULL, NULL, NULL, NULL, 0, 0 }
+    {
+      .url_prefix = "/",
+      .method = MHD_HTTP_METHOD_GET,
+      .mime_type = "text/plain",
+      .skip_instance = true,
+      .data = "This is a GNU Taler merchant backend. See 
https://taler.net/.\n";,
+      .data_size = strlen (
+        "This is a GNU Taler merchant backend. See https://taler.net/.\n";),
+      .handler = &TMH_MHD_handler_static_response,
+      .response_code = MHD_HTTP_OK
+    },
+    {
+      .url_prefix = "/agpl",
+      .method = MHD_HTTP_METHOD_GET,
+      .skip_instance = true,
+      .handler = &TMH_MHD_handler_agpl_redirect
+    },
+    {
+      .url_prefix = "/config",
+      .method = MHD_HTTP_METHOD_GET,
+      .skip_instance = true,
+      .handler = &MH_handler_config
+    },
+    {
+      NULL
+    }
   };
   static struct TMH_RequestHandler h404 = {
-    "", NULL, "text/html",
-    "<html><title>404: not found</title><body>404: not found</body></html>", 0,
-    &TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
+    .mime_type = "text/html",
+    .data = "<html><title>404: not found</title>"
+            "<body>404: not found</body></html>",
+    .data_size = strlen ("<html><title>404: not found</title>"
+                         "<body>404: not found</body></html>"),
+    .handler = &TMH_MHD_handler_static_response,
+    .response_code = MHD_HTTP_NOT_FOUND
   };
-
-  struct TM_HandlerContext *hc = *con_cls;
-  struct GNUNET_AsyncScopeId aid;
-  const char *correlation_id = NULL;
-  struct MerchantInstance *instance;
-  const char *effective_url;
-  /* Is a publicly facing endpoint being requested? */
-  int is_public;
-  /* Matching URL found, but maybe method doesn't match */
-  int url_found = GNUNET_NO;
-  MHD_RESULT ret;
-  struct TMH_RequestHandler *selected_handler = NULL;
+  struct TMH_HandlerContext *hc = *con_cls;
+  struct TMH_RequestHandler *handlers;
 
   (void) cls;
   (void) version;
-  if (NULL == hc)
+  if (NULL != hc)
   {
-    GNUNET_async_scope_fresh (&aid);
-    /* We only read the correlation ID on the first callback for every client 
*/
+    GNUNET_assert (NULL != hc->rh);
+    GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
+    if ( (hc->is_post) &&
+         (NULL == hc->json) )
+    {
+      int res;
+
+      res = TALER_MHD_parse_post_json (connection,
+                                       &hc->json_parse_context,
+                                       upload_data,
+                                       upload_data_size,
+                                       &hc->json);
+      if (GNUNET_SYSERR == res)
+        return MHD_NO;
+      /* A error response was already generated */
+      if ( (GNUNET_NO == res) ||
+           /* or, need more data to accomplish parsing */
+           (NULL == hc->json) )
+        return MHD_YES;
+    }
+    return hc->rh->handler (hc->rh,
+                            connection,
+                            hc);
+  }
+  hc = GNUNET_new (struct TMH_HandlerContext);
+  *con_cls = hc;
+  GNUNET_async_scope_fresh (&hc->async_scope_id);
+  GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
+  hc->url = url;
+  {
+    const char *correlation_id;
+
     correlation_id = MHD_lookup_connection_value (connection,
                                                   MHD_HEADER_KIND,
                                                   "Taler-Correlation-Id");
-    if ((NULL != correlation_id) &&
-        (GNUNET_YES != GNUNET_CURL_is_valid_scope_id (correlation_id)))
+    if ( (NULL != correlation_id) &&
+         (GNUNET_YES != GNUNET_CURL_is_valid_scope_id (correlation_id)) )
     {
       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                   "illegal incoming correlation ID\n");
       correlation_id = NULL;
     }
-  }
-  else
-  {
-    aid = hc->async_scope_id;
-  }
-
-  GNUNET_SCHEDULER_begin_async_scope (&aid);
-
-  if (NULL != correlation_id)
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Handling request for (%s) URL '%s', correlation_id=%s\n",
-                method,
-                url,
-                correlation_id);
-  else
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Handling request (%s) for URL '%s'\n",
-                method,
-                url);
-
-  effective_url = url;
-
-  {
-    const char *public_prefix = "/public/";
-
-    if (0 == strncmp (effective_url,
-                      public_prefix,
-                      strlen (public_prefix)))
-    {
-      is_public = GNUNET_YES;
-      effective_url = effective_url + strlen (public_prefix) - 1;
-    }
+    if (NULL != correlation_id)
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Handling request for (%s) URL '%s', correlation_id=%s\n",
+                  method,
+                  url,
+                  correlation_id);
     else
-    {
-      is_public = GNUNET_NO;
-    }
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Handling request (%s) for URL '%s'\n",
+                  method,
+                  url);
   }
 
+  if (0 == strcasecmp (method,
+                       MHD_HTTP_METHOD_HEAD))
+    method = MHD_HTTP_METHOD_GET; /* MHD will deal with the rest */
+
   /* Find out the merchant backend instance for the request.
    * If there is an instance, remove the instance specification
    * from the beginning of the request URL. */
   {
     const char *instance_prefix = "/instances/";
 
-    if (0 == strncmp (effective_url,
+    if (0 == strncmp (url,
                       instance_prefix,
                       strlen (instance_prefix)))
     {
       /* url starts with "/instances/" */
-      const char *istart = effective_url + strlen (instance_prefix);
+      const char *istart = url + strlen (instance_prefix);
       const char *slash = strchr (istart, '/');
       char *instance_id;
 
@@ -1500,114 +804,149 @@ url_handler (void *cls,
       {
         return TMH_MHD_handler_static_response (&h404,
                                                 connection,
-                                                con_cls,
-                                                upload_data,
-                                                upload_data_size,
-                                                NULL);
+                                                hc);
       }
       instance_id = GNUNET_strndup (istart,
                                     slash - istart);
-      instance = lookup_instance (instance_id);
+      hc->instance = lookup_instance (instance_id);
       GNUNET_free (instance_id);
-      effective_url = slash;
+      url = slash;
     }
     else
     {
-      instance = lookup_instance (NULL);
+      /* use 'default' */
+      hc->instance = lookup_instance (NULL);
     }
   }
-  if (NULL == instance)
-    return TALER_MHD_reply_json_pack (connection,
-                                      MHD_HTTP_NOT_FOUND,
-                                      "{s:I, s:s}",
-                                      "code",
-                                      (json_int_t) TALER_EC_INSTANCE_UNKNOWN,
-                                      "error",
-                                      "merchant instance unknown");
 
-  if (GNUNET_NO == is_public)
   {
-    for (unsigned int i = 0; NULL != handlers[i].url; i++)
-    {
-      struct TMH_RequestHandler *rh = &handlers[i];
+    const char *private_prefix = "/private/";
 
-      if ( (0 != strcasecmp (effective_url, rh->url)) )
-        continue;
-      url_found = GNUNET_YES;
-      if (0 == strcasecmp (method,
-                           MHD_HTTP_METHOD_OPTIONS))
-      {
-        return TALER_MHD_reply_cors_preflight (connection);
-      }
-      if ( (rh->method != NULL) &&
-           (0 != strcasecmp (method, rh->method)) )
-        continue;
-      selected_handler = rh;
-      break;
+    if (0 == strncmp (url,
+                      private_prefix,
+                      strlen (private_prefix)))
+    {
+      handlers = private_handlers;
+      url += strlen (private_prefix) - 1;
+    }
+    else
+    {
+      handlers = public_handlers;
     }
   }
+  if (strcmp (url,
+              ""))
+    url = "/"; /* code below does not like empty string */
 
-  if (NULL == selected_handler)
   {
-    for (unsigned int i = 0; NULL != public_handlers[i].url; i++)
+    /* Matching URL found, but maybe method doesn't match */
+    size_t prefix_strlen; /* i.e. 8 for "/orders/", or 7 for "/config" */
+    const char *infix_url = NULL; /* i.e. "$ORDER_ID", no '/'-es */
+    size_t infix_strlen = 0; /* number of characters in infix_url */
+    const char *suffix_url = NULL; /* i.e. "/refund", includes '/' at the 
beginning */
+    size_t suffix_strlen = 0; /* number of characters in suffix_url */
+
     {
-      struct TMH_RequestHandler *rh = &public_handlers[i];
+      const char *slash;
 
-      if ( (0 != strcasecmp (effective_url, rh->url)) )
-        continue;
-      url_found = GNUNET_YES;
-      if (0 == strcasecmp (method,
-                           MHD_HTTP_METHOD_OPTIONS))
+      slash = strchr (&url[1], '/');
+      if (NULL == slash)
       {
-        return TALER_MHD_reply_cors_preflight (connection);
+        prefix_strlen = strlen (url);
+      }
+      else
+      {
+        prefix_strlen = slash - url + 1; /* includes both '/'-es if present! */
+        infix_url = slash + 1;
+        slash = strchr (&infix_url[1], '/');
+        if (NULL == slash)
+        {
+          infix_strlen = strlen (infix_url);
+        }
+        else
+        {
+          infix_strlen = slash - infix_url;
+          suffix_url = slash;
+          suffix_strlen = strlen (suffix_url);
+        }
+        hc->infix = GNUNET_strndup (infix_url,
+                                    infix_strlen);
       }
-      if ( (rh->method != NULL) && (0 != strcasecmp (method, rh->method)) )
-        continue;
-      selected_handler = rh;
-      break;
     }
-  }
 
-  if (NULL == selected_handler)
-  {
-    if (GNUNET_YES == url_found)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "invalid request: method '%s' for '%s' not allowed\n",
-                  method,
-                  url);
-      return TALER_MHD_reply_json_pack (connection,
-                                        MHD_HTTP_METHOD_NOT_ALLOWED,
-                                        "{s:s}",
-                                        "error",
-                                        "method not allowed");
+      bool url_found = false;
+
+      for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
+      {
+        struct TMH_RequestHandler *rh = &handlers[i];
+
+        if ( (prefix_strlen != strlen (rh->url_prefix)) ||
+             (0 != memcmp (url,
+                           rh->url_prefix,
+                           prefix_strlen)) )
+          continue;
+        if ( (NULL == infix_url)
+             ^ (GNUNET_NO == rh->have_id_segment) )
+          continue; /* infix existence missmatch */
+        if ( (NULL == suffix_url)
+             ^ (NULL != rh->url_suffix) )
+          continue; /* suffix existence missmatch */
+        if ( (NULL != suffix_url) &&
+             ( (suffix_strlen != strlen (rh->url_suffix)) ||
+               (0 != memcmp (suffix_url,
+                             rh->url_suffix,
+                             suffix_strlen)) ) )
+          continue; /* suffix content missmatch */
+        url_found = true;
+        if (0 == strcasecmp (method,
+                             MHD_HTTP_METHOD_OPTIONS))
+        {
+          return TALER_MHD_reply_cors_preflight (connection);
+        }
+        if ( (rh->method != NULL) &&
+             (0 != strcasecmp (method, rh->method)) )
+          continue;
+        hc->rh = rh;
+        break;
+      }
+      if ( (NULL == hc->rh) &&
+           (url_found) )
+        return TALER_MHD_reply_json_pack (connection,
+                                          MHD_HTTP_METHOD_NOT_ALLOWED,
+                                          "{s:s}",
+                                          "error",
+                                          "method not allowed");
+      if (NULL == hc->rh)
+        return TMH_MHD_handler_static_response (&h404,
+                                                connection,
+                                                hc);
     }
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "invalid request: URL '%s' not handled\n",
-                url);
-    return TMH_MHD_handler_static_response (&h404,
-                                            connection,
-                                            con_cls,
-                                            upload_data,
-                                            upload_data_size,
-                                            instance);
   }
-
-  ret = selected_handler->handler (selected_handler,
-                                   connection,
-                                   con_cls,
-                                   upload_data,
-                                   upload_data_size,
-                                   instance);
-  hc = *con_cls;
-  if (NULL != hc)
+  /* At this point, we must have found a handler */
+  GNUNET_assert (NULL != hc->rh);
+  if ( (NULL == hc->instance) &&
+       (GNUNET_YES != hc->rh->skip_instance) )
+    return TALER_MHD_reply_json_pack (connection,
+                                      MHD_HTTP_NOT_FOUND,
+                                      "{s:I, s:s}",
+                                      "code",
+                                      (json_int_t) TALER_EC_INSTANCE_UNKNOWN,
+                                      "error",
+                                      "merchant instance unknown");
+  hc->is_post = (0 == strcasecmp (method,
+                                  MHD_HTTP_METHOD_POST));
+  if (hc->is_post)
   {
-    hc->rh = selected_handler;
-    /* Store the async context ID, so we can restore it if
-     * we get another callback for this request. */
-    hc->async_scope_id = aid;
+    /* FIXME: Maybe check for maximum upload size here
+       and refuse if it is too big? */
+
+    GNUNET_break (NULL == hc->json); /* can't have it already */
+    return MHD_YES; /* proceed with upload */
   }
-  return ret;
+  return hc->rh->handler (hc->rh,
+                          connection,
+                          hc);
 }
 
 
@@ -1632,10 +971,11 @@ run (void *cls,
   (void) cls;
   (void) args;
   (void) cfgfile;
+  cfg = config;
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Starting taler-merchant-httpd\n");
   go = TALER_MHD_GO_NONE;
-  if (TMH_merchant_connection_close)
+  if (merchant_connection_close)
     go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
   TALER_MHD_setup (go);
 
@@ -1643,14 +983,14 @@ run (void *cls,
   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
                                  NULL);
   if (GNUNET_OK !=
-      TALER_config_get_currency (config,
+      TALER_config_get_currency (cfg,
                                  &TMH_currency))
   {
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
   if (GNUNET_YES ==
-      GNUNET_CONFIGURATION_get_value_yesno (config,
+      GNUNET_CONFIGURATION_get_value_yesno (cfg,
                                             "merchant",
                                             "FORCE_AUDIT"))
     TMH_force_audit = GNUNET_YES;
@@ -1666,7 +1006,6 @@ run (void *cls,
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
-
   if (NULL ==
       (by_id_map = GNUNET_CONTAINER_multihashmap_create (1,
                                                          GNUNET_NO)))
@@ -1674,97 +1013,14 @@ run (void *cls,
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
-
   if (NULL ==
-      (by_kpub_map = GNUNET_CONTAINER_multihashmap_create (1,
-                                                           GNUNET_NO)))
-  {
-    GNUNET_SCHEDULER_shutdown ();
-    return;
-  }
-
-  if (GNUNET_SYSERR ==
-      GNUNET_CONFIGURATION_get_value_time (config,
-                                           "merchant",
-                                           "WIRE_TRANSFER_DELAY",
-                                           &default_wire_transfer_delay))
+      (TMH_db = TALER_MERCHANTDB_plugin_load (cfg)))
   {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "merchant",
-                               "WIRE_TRANSFER_DELAY");
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
 
-  if (GNUNET_SYSERR ==
-      GNUNET_CONFIGURATION_get_value_time (config,
-                                           "merchant",
-                                           "DEFAULT_PAY_DEADLINE",
-                                           &default_pay_deadline))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "merchant",
-                               "DEFAULT_PAY_DEADLINE");
-    GNUNET_SCHEDULER_shutdown ();
-    return;
-  }
-
-  if (GNUNET_OK !=
-      TALER_config_get_amount (config,
-                               "merchant",
-                               "DEFAULT_MAX_WIRE_FEE",
-                               &default_max_wire_fee))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "merchant",
-                               "DEFAULT_MAX_WIRE_FEE");
-    GNUNET_SCHEDULER_shutdown ();
-    return;
-  }
-
-  if (GNUNET_OK !=
-      TALER_config_get_amount (config,
-                               "merchant",
-                               "DEFAULT_MAX_DEPOSIT_FEE",
-                               &default_max_deposit_fee))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "merchant",
-                               "DEFAULT_MAX_DEPOSIT_FEE");
-    GNUNET_SCHEDULER_shutdown ();
-    return;
-  }
-
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_number (config,
-                                             "merchant",
-                                             "DEFAULT_WIRE_FEE_AMORTIZATION",
-                                             &default_wire_fee_amortization))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "merchant",
-                               "DEFAULT_WIRE_FEE_AMORTIZATION");
-    GNUNET_SCHEDULER_shutdown ();
-    return;
-  }
-
-  cfg = GNUNET_CONFIGURATION_dup (config);
-  if (GNUNET_OK !=
-      iterate_instances ())
-  {
-    GNUNET_SCHEDULER_shutdown ();
-    return;
-  }
-  iterate_locations ();
-
-  if (NULL ==
-      (db = TALER_MERCHANTDB_plugin_load (cfg)))
-  {
-    GNUNET_SCHEDULER_shutdown ();
-    return;
-  }
-
-  fh = TALER_MHD_bind (config,
+  fh = TALER_MHD_bind (cfg,
                        "merchant",
                        &port);
   if ( (0 == port) &&
@@ -1805,7 +1061,7 @@ run (void *cls,
  *
  * @param argc number of arguments from the command line
  * @param argv command line arguments
- * @return 0 ok, 1 on error
+ * @return 0 ok, non-zero on error
  */
 int
 main (int argc,
@@ -1815,12 +1071,11 @@ main (int argc,
     GNUNET_GETOPT_option_flag ('C',
                                "connection-close",
                                "force HTTP connections to be closed after each 
request",
-                               &TMH_merchant_connection_close),
+                               &merchant_connection_close),
     GNUNET_GETOPT_option_timetravel ('T',
                                      "timetravel"),
     GNUNET_GETOPT_OPTION_END
   };
-
   if (GNUNET_OK !=
       GNUNET_PROGRAM_run (argc, argv,
                           "taler-merchant-httpd",
diff --git a/src/backend/taler-merchant-httpd.h 
b/src/backend/taler-merchant-httpd.h
index 28a0f2e..b2e1a59 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -39,17 +39,17 @@
 /**
  * Supported wire method.  Kept in a DLL.
  */
-struct WireMethod
+struct TMH_WireMethod
 {
   /**
    * Next entry in DLL.
    */
-  struct WireMethod *next;
+  struct TMH_WireMethod *next;
 
   /**
    * Previous entry in DLL.
    */
-  struct WireMethod *prev;
+  struct TMH_WireMethod *prev;
 
   /**
    * Which wire method / payment target identifier is @e j_wire using?
@@ -69,7 +69,7 @@ struct WireMethod
   /**
    * Is this wire method active (should it be included in new contracts)?
    */
-  int active;
+  bool active;
 
 };
 
@@ -79,7 +79,7 @@ struct WireMethod
  * backend can account for several merchants, as used to do in donation
  * shops
  */
-struct MerchantInstance
+struct TMH_MerchantInstance
 {
 
   /**
@@ -102,12 +102,12 @@ struct MerchantInstance
   /**
    * Next entry in DLL.
    */
-  struct WireMethod *wm_head;
+  struct TMH_WireMethod *wm_head;
 
   /**
    * Previous entry in DLL.
    */
-  struct WireMethod *wm_tail;
+  struct TMH_WireMethod *wm_tail;
 
   /**
    * Merchant's private key
@@ -119,12 +119,50 @@ struct MerchantInstance
    */
   struct TALER_MerchantPublicKeyP pubkey;
 
+  /**
+   * Default maximum wire fee to assume, unless stated differently in the 
proposal
+   * already.
+   */
+  struct TALER_Amount default_max_wire_fee;
+
+  /**
+   * Default max deposit fee that the merchant is willing to
+   * pay; if deposit costs more, then the customer will cover
+   * the difference.
+   */
+  struct TALER_Amount default_max_deposit_fee;
+
+  /**
+   * Default factor for wire fee amortization.
+   */
+  unsigned long long default_wire_fee_amortization;
+
+  /**
+   * If the frontend does NOT specify an execution date, how long should
+   * we tell the exchange to wait to aggregate transactions before
+   * executing the wire transfer?  This delay is added to the current
+   * time when we generate the advisory execution time for the exchange.
+   */
+  struct GNUNET_TIME_Relative default_wire_transfer_delay;
+
+  /**
+   * If the frontend does NOT specify a payment deadline, how long should
+   * offers we make be valid by default?
+   */
+  struct GNUNET_TIME_Relative default_pay_deadline;
+
   /**
    * Exchange this instance uses for tipping, NULL if tipping
    * is not supported.
    */
   char *tip_exchange;
 
+  /**
+   * Locations from the configuration.  Mapping from
+   * label to location data.
+   */
+  json_t *default_locations;
+
   /**
    * What is the private key of the reserve used for signing tips by this 
exchange?
    * Only valid if @e tip_exchange is non-null.
@@ -135,14 +173,56 @@ struct MerchantInstance
 
 /**
  * @brief Struct describing an URL and the handler for it.
+ *
+ * The overall URL is always @e url_prefix, optionally followed by the
+ * id_segment, which is optionally followed by the @e url_suffix. It is NOT
+ * allowed for the @e url_prefix to be directly followed by the @e url_suffix.
+ * A @e url_suffix SHOULD only be used with a @e method of 
#MHD_HTTP_METHOD_POST.
+ */
+struct TMH_RequestHandler;
+
+/**
+ * This information is stored in the "connection_cls" of MHD for
+ * every request that we process.
+ * Individual handlers can evaluate tis memebers and
+ * are allowed to update @e cc and @e ctx to store and clean up
+ * handler-specific data.
+ */
+struct TMH_HandlerContext;
+
+
+/**
+ * @brief Struct describing an URL and the handler for it.
+ *
+ * The overall URL is always @e url_prefix, optionally followed by the
+ * id_segment, which is optionally followed by the @e url_suffix. It is NOT
+ * allowed for the @e url_prefix to be directly followed by the @e url_suffix.
+ * A @e url_suffix SHOULD only be used with a @e method of 
#MHD_HTTP_METHOD_POST.
  */
 struct TMH_RequestHandler
 {
 
   /**
-   * URL the handler is for.
+   * URL prefix the handler is for.
    */
-  const char *url;
+  const char *url_prefix;
+
+  /**
+   * Does this request include an identifier segment
+   * (product_id, reserve_pub, order_id, tip_id) in the
+   * second segment?
+   */
+  bool have_id_segment;
+
+  /**
+   * Does this request handler expect an instance?
+   */
+  bool skip_instance;
+
+  /**
+   * URL suffix the handler is for.
+   */
+  const char *url_suffix;
 
   /**
    * Method the handler is for, NULL for "all".
@@ -155,36 +235,30 @@ struct TMH_RequestHandler
   const char *mime_type;
 
   /**
-   * Raw data for the @e handler
+   * Raw data for the @e handler (can be NULL).
    */
   const void *data;
 
   /**
-   * Number of bytes in @e data, 0 for 0-terminated.
+   * Number of bytes in @e data.
    */
   size_t data_size;
 
   /**
-   * Function to call to handle the request.
+   * Handler to be called for this URL/METHOD combination.
    *
    * @param rh this struct
-   * @param mime_type the @e mime_type for the reply (hint, can be NULL)
    * @param connection the MHD connection to handle
-   * @param[in,out] connection_cls the connection's closure (can be updated)
-   * @param upload_data upload data
-   * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
-   * @param mi merchant backend instance, never NULL
+   * @param[in,out] hc context with further information about the request
    * @return MHD result code
    */
-  MHD_RESULT (*handler)(struct TMH_RequestHandler *rh,
-                        struct MHD_Connection *connection,
-                        void **connection_cls,
-                        const char *upload_data,
-                        size_t *upload_data_size,
-                        struct MerchantInstance *mi);
+  MHD_RESULT
+  (*handler)(const struct TMH_RequestHandler *rh,
+             struct MHD_Connection *connection,
+             struct TMH_HandlerContext *hc);
 
   /**
-   * Default response code.
+   * Default response code to use.
    */
   unsigned int response_code;
 };
@@ -196,43 +270,80 @@ struct TMH_RequestHandler
  * member.  This struct contains a single callback, which will be
  * invoked to clean up the memory when the contection is completed.
  */
-struct TM_HandlerContext;
+struct TMH_HandlerContext;
 
 /**
  * Signature of a function used to clean up the context
  * we keep in the "connection_cls" of MHD when handling
  * a request.
  *
- * @param hc header of the context to clean up.
+ * @param ctxt the context to clean up.
  */
 typedef void
-(*TM_ContextCleanup)(struct TM_HandlerContext *hc);
+(*TMH_ContextCleanup)(void *ctx);
 
 
 /**
- * Each MHD response handler that sets the "connection_cls" to a
- * non-NULL value must use a struct that has this struct as its first
- * member.  This struct contains a single callback, which will be
- * invoked to clean up the memory when the connection is completed.
+ * This information is stored in the "connection_cls" of MHD for
+ * every request that we process.
+ * Individual handlers can evaluate tis memebers and
+ * are allowed to update @e cc and @e ctx to store and clean up
+ * handler-specific data.
  */
-struct TM_HandlerContext
+struct TMH_HandlerContext
 {
 
   /**
    * Function to execute the handler-specific cleanup of the
-   * (typically larger) context.
+   * (request-specific) context in @e ctx.
+   */
+  TMH_ContextCleanup cc;
+
+  /**
+   * Client-specific context we keep. Passed to @e cc.
    */
-  TM_ContextCleanup cc;
+  void *ctx;
 
   /**
    * Which request handler is handling this request?
    */
   const struct TMH_RequestHandler *rh;
 
+  /**
+   * Which instance is handling this request?
+   */
+  struct TMH_MerchantInstance *instance;
+
   /**
    * Asynchronous request context id.
    */
   struct GNUNET_AsyncScopeId async_scope_id;
+
+  /**
+   * Our original URL, for logging.
+   */
+  const char *url;
+
+  /**
+   * Infix part of @a url.
+   */
+  char *infix;
+
+  /**
+   * JSON body that was uploaded, NULL if @e is_post is false.
+   */
+  json_t *json;
+
+  /**
+   * Placeholder for #TALER_MHD_parse_post_json() to keep its internal state.
+   * Used when we parse the POSTed data.
+   */
+  void *json_parse_context;
+
+  /**
+   * Set to true if this is an #MHD_HTTP_METHOD_POST request.
+   */
+  bool is_post;
 };
 
 
@@ -247,12 +358,14 @@ struct TMH_SuspendedConnection
   struct MHD_Connection *con;
 
   /**
-   * Associated heap node.
+   * Associated heap node.  Used internally by #TMH_long_poll_suspend()
+   * and TMH_long_poll_resume().
    */
   struct GNUNET_CONTAINER_HeapNode *hn;
 
   /**
-   * Key of this entry in the #payment_trigger_map
+   * Key of this entry in the #payment_trigger_map.  Used internally by
+   * #TMH_long_poll_suspend() and TMH_long_poll_resume().
    */
   struct GNUNET_HashCode key;
 
@@ -270,52 +383,11 @@ struct TMH_SuspendedConnection
   /**
    * #GNUNET_YES if we are waiting for a refund.
    */
-  int awaiting_refund;
+  bool awaiting_refund;
 
 };
 
 
-/**
- * Locations from the configuration.  Mapping from
- * label to location data.
- */
-extern json_t *default_locations;
-
-/**
- * Default maximum wire fee to assume, unless stated differently in the 
proposal
- * already.
- */
-extern struct TALER_Amount default_max_wire_fee;
-
-/**
- * Default max deposit fee that the merchant is willing to
- * pay; if deposit costs more, then the customer will cover
- * the difference.
- */
-extern struct TALER_Amount default_max_deposit_fee;
-
-/**
- * Default factor for wire fee amortization.
- */
-extern unsigned long long default_wire_fee_amortization;
-
-/**
- * MIN-Heap of suspended connections to resume when the timeout expires,
- * ordered by timeout. Values are of type `struct TMH_SuspendedConnection`
- */
-extern struct GNUNET_CONTAINER_Heap *resume_timeout_heap;
-
-/**
- * Task responsible for timeouts in the #resume_timeout_heap.
- */
-extern struct GNUNET_SCHEDULER_Task *resume_timeout_task;
-
-/**
- * Hash map from H(order_id,merchant_pub) to `struct TMH_SuspendedConnection`
- * entries to resume when a payment is made for the given order.
- */
-extern struct GNUNET_CONTAINER_MultiHashMap *payment_trigger_map;
-
 /**
  * Which currency do we use?
  */
@@ -326,46 +398,11 @@ extern char *TMH_currency;
  */
 extern int TMH_force_audit;
 
-/**
- * Hash of our wire format details as given in #j_wire.
- */
-extern struct GNUNET_HashCode h_wire;
-
-/**
- * Our private key, corresponds to #pubkey.
- */
-extern struct TALER_MerchantPrivateKeyP privkey;
-
-/**
- * Our public key, corresponds to #privkey.
- */
-extern struct TALER_MerchantPublicKeyP pubkey;
-
-/**
- * Hashmap pointing at merchant instances by 'id'. An 'id' is
- * just a string that identifies a merchant instance. When a frontend
- * needs to specify an instance to the backend, it does so by 'id'
- */
-extern struct GNUNET_CONTAINER_MultiHashMap *by_id_map;
-
 /**
  * Handle to the database backend.
  */
-extern struct TALER_MERCHANTDB_Plugin *db;
-
-/**
- * If the frontend does NOT specify an execution date, how long should
- * we tell the exchange to wait to aggregate transactions before
- * executing the wire transfer?  This delay is added to the current
- * time when we generate the advisory execution time for the exchange.
- */
-extern struct GNUNET_TIME_Relative default_wire_transfer_delay;
+extern struct TALER_MERCHANTDB_Plugin *TMH_db;
 
-/**
- * If the frontend does NOT specify a payment deadline, how long should
- * offers we make be valid by default?
- */
-extern struct GNUNET_TIME_Relative default_pay_deadline;
 
 /**
  * Kick MHD to run now, to be called after MHD_resume_connection().
@@ -377,29 +414,19 @@ void
 TMH_trigger_daemon (void);
 
 
-/**
- * Compute @a key to use for @a order_id and @a mpub in our
- * #payment_trigger_map.
- *
- * @param order_id an order ID
- * @param mpub an instance public key
- * @param key[out] set to the hash map key to use
- */
-void
-TMH_compute_pay_key (const char *order_id,
-                     const struct TALER_MerchantPublicKeyP *mpub,
-                     struct GNUNET_HashCode *key);
-
-
 /**
  * Suspend connection from @a sc until payment has been received.
  *
+ * @param order_id the order that we are waiting on
+ * @param mi the merchant instance we are waiting on
  * @param sc connection to suspend
  * @param min_refund refund amount we are waiting on to be exceeded before 
resuming,
  *                   NULL if we are not waiting for refunds
  */
 void
-TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc,
+TMH_long_poll_suspend (const char *order_id,
+                       const struct TMH_MerchantInstance *mi,
+                       struct TMH_SuspendedConnection *sc,
                        const struct TALER_Amount *min_refund);
 
 
@@ -407,31 +434,14 @@ TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc,
  * Find out if we have any clients long-polling for @a order_id to be
  * confirmed at merchant @a mpub, and if so, tell them to resume.
  *
- * @param order_id the order that was paid
- * @param mpub the merchant's public key of the instance where the payment 
happened
+ * @param order_id the order that was paid or refunded
+ * @param mi the merchant instance where the payment or refund happened
  * @param refund_amount refunded amount, if the trigger was a refund, 
otherwise NULL
  */
 void
 TMH_long_poll_resume (const char *order_id,
-                      const struct TALER_MerchantPublicKeyP *mpub,
+                      const struct TMH_MerchantInstance *mi,
                       const struct TALER_Amount *refund_amount);
 
 
-/**
- * Create a taler://pay/ URI for the given @a con and @a order_id
- * and @a session_id and @a instance_id.
- *
- * @param con HTTP connection
- * @param order_id the order id
- * @param session_id session, may be NULL
- * @param instance_id instance, may be "default"
- * @return corresponding taler://pay/ URI, or NULL on missing "host"
- */
-char *
-TMH_make_taler_pay_uri (struct MHD_Connection *con,
-                        const char *order_id,
-                        const char *session_id,
-                        const char *instance_id);
-
-
 #endif
diff --git a/src/backend/taler-merchant-httpd_config.c 
b/src/backend/taler-merchant-httpd_config.c
index d6f6c92..c4fed58 100644
--- a/src/backend/taler-merchant-httpd_config.c
+++ b/src/backend/taler-merchant-httpd_config.c
@@ -25,8 +25,6 @@
 #include "taler-merchant-httpd.h"
 #include "taler-merchant-httpd_mhd.h"
 #include "taler-merchant-httpd_exchanges.h"
-#include "taler-merchant-httpd_tip-query.h"
-#include "taler-merchant-httpd_tip-reserve-helper.h"
 
 
 /**
@@ -47,111 +45,28 @@
 #define MERCHANT_PROTOCOL_VERSION "0:0:0"
 
 
-static int
-add_instance (void *cls,
-              const struct GNUNET_HashCode *key,
-              void *value)
-{
-  json_t *ja = cls;
-  struct MerchantInstance *mi = value;
-  char *url;
-  json_t *pta;
-
-  /* Compile array of all unique wire methods supported by this
-     instance */
-  pta = json_array ();
-  GNUNET_assert (NULL != pta);
-  for (struct WireMethod *wm = mi->wm_head;
-       NULL != wm;
-       wm = wm->next)
-  {
-    int duplicate = GNUNET_NO;
-
-    if (! wm->active)
-      break;
-    /* Yes, O(n^2), but really how many bank accounts can an
-       instance realistically have for this to matter? */
-    for (struct WireMethod *pm = mi->wm_head;
-         pm != wm;
-         pm = pm->next)
-      if (0 == strcasecmp (pm->wire_method,
-                           wm->wire_method))
-      {
-        duplicate = GNUNET_YES;
-        break;
-      }
-    if (duplicate)
-      continue;
-    GNUNET_assert (0 ==
-                   json_array_append_new (pta,
-                                          json_string (wm->wire_method)));
-  }
-  GNUNET_asprintf (&url,
-                   "/%s/",
-                   mi->id);
-  GNUNET_assert (0 ==
-                 json_array_append_new (
-                   ja,
-                   json_pack (
-                     (NULL != mi->tip_exchange)
-                     ? "{s:s, s:s, s:o, s:o, s:s}"
-                     : "{s:s, s:s, s:o, s:o}",
-                     "name",
-                     mi->name,
-                     "backend_base_url",
-                     url,
-                     "merchant_pub",
-                     GNUNET_JSON_from_data_auto (&mi->pubkey),
-                     "payment_targets",
-                     pta,
-                     /* optional: */
-                     "tipping_exchange_baseurl",
-                     mi->tip_exchange)));
-  GNUNET_free (url);
-  return GNUNET_OK;
-}
-
-
 /**
  * Handle a "/config" request.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL
+ * @param[in,out] hc handler context (can be updated)
  * @return MHD result code
  */
 MHD_RESULT
 MH_handler_config (struct TMH_RequestHandler *rh,
                    struct MHD_Connection *connection,
-                   void **connection_cls,
-                   const char *upload_data,
-                   size_t *upload_data_size,
-                   struct MerchantInstance *mi)
+                   struct TMH_HandlerContext *hc)
 {
   static struct MHD_Response *response;
 
   (void) rh;
-  (void) connection_cls;
-  (void) upload_data;
-  (void) upload_data_size;
-  (void) mi;
+  (void) hc;
   if (NULL == response)
   {
-    json_t *ia;
-
-    ia = json_array ();
-    GNUNET_assert (NULL != ia);
-    GNUNET_CONTAINER_multihashmap_iterate (by_id_map,
-                                           &add_instance,
-                                           ia);
-    response = TALER_MHD_make_json_pack ("{s:s, s:s, s:o}",
+    response = TALER_MHD_make_json_pack ("{s:s, s:s }",
                                          "currency", TMH_currency,
-                                         "version", MERCHANT_PROTOCOL_VERSION,
-                                         "instances", ia);
-
+                                         "version", MERCHANT_PROTOCOL_VERSION);
   }
   return MHD_queue_response (connection,
                              MHD_HTTP_OK,
diff --git a/src/backend/taler-merchant-httpd_config.h 
b/src/backend/taler-merchant-httpd_config.h
index 1a5dfd6..8d21e47 100644
--- a/src/backend/taler-merchant-httpd_config.h
+++ b/src/backend/taler-merchant-httpd_config.h
@@ -28,18 +28,12 @@
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL
+ * @param[in,out] hc handler context (can be updated)
  * @return MHD result code
  */
 MHD_RESULT
-MH_handler_config (struct TMH_RequestHandler *rh,
+MH_handler_config (const struct TMH_RequestHandler *rh,
                    struct MHD_Connection *connection,
-                   void **connection_cls,
-                   const char *upload_data,
-                   size_t *upload_data_size,
-                   struct MerchantInstance *mi);
+                   struct TMH_HandlerContext *hc);
 
 #endif
diff --git a/src/backend/taler-merchant-httpd_exchanges.c 
b/src/backend/taler-merchant-httpd_exchanges.c
index 190f5db..11f845f 100644
--- a/src/backend/taler-merchant-httpd_exchanges.c
+++ b/src/backend/taler-merchant-httpd_exchanges.c
@@ -380,10 +380,10 @@ process_wire_fees (struct Exchange *exchange,
                 wire_method,
                 GNUNET_STRINGS_absolute_time_to_string (af->start_date),
                 TALER_amount2s (&af->wire_fee));
-    db->preflight (db->cls);
+    TMH_db->preflight (TMH_db->cls);
     if (GNUNET_OK !=
-        db->start (db->cls,
-                   "store wire fee"))
+        TMH_db->start (TMH_db->cls,
+                       "store wire fee"))
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Failed to start database transaction to store exchange wire 
fees (will try to continue anyway)!\n");
@@ -391,21 +391,21 @@ process_wire_fees (struct Exchange *exchange,
       fees = fees->next;
       continue;
     }
-    qs = db->store_wire_fee_by_exchange (db->cls,
-                                         master_pub,
-                                         &h_wire_method,
-                                         &af->wire_fee,
-                                         &af->closing_fee,
-                                         af->start_date,
-                                         af->end_date,
-                                         &af->master_sig);
+    qs = TMH_db->store_wire_fee_by_exchange (TMH_db->cls,
+                                             master_pub,
+                                             &h_wire_method,
+                                             &af->wire_fee,
+                                             &af->closing_fee,
+                                             af->start_date,
+                                             af->end_date,
+                                             &af->master_sig);
     if (0 > qs)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Failed to persist exchange wire fees in merchant DB (will 
try to continue anyway)!\n");
       GNUNET_free (af);
       fees = fees->next;
-      db->rollback (db->cls);
+      TMH_db->rollback (TMH_db->cls);
       continue;
     }
     if (0 == qs)
@@ -413,12 +413,12 @@ process_wire_fees (struct Exchange *exchange,
       /* Entry was already in DB, fine, continue as if we had succeeded */
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   "Fees already in DB, rolling back transaction attempt!\n");
-      db->rollback (db->cls);
+      TMH_db->rollback (TMH_db->cls);
     }
     if (0 < qs)
     {
       /* Inserted into DB, make sure transaction completes */
-      qs = db->commit (db->cls);
+      qs = TMH_db->commit (TMH_db->cls);
       if (0 > qs)
       {
         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
diff --git a/src/backend/taler-merchant-httpd_mhd.c 
b/src/backend/taler-merchant-httpd_mhd.c
index 0860860..79f4071 100644
--- a/src/backend/taler-merchant-httpd_mhd.c
+++ b/src/backend/taler-merchant-httpd_mhd.c
@@ -33,27 +33,15 @@
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, could be NULL in this specific case!
+ * @param[in,out] hc handler context (can be updated)
  * @return MHD result code
  */
 MHD_RESULT
-TMH_MHD_handler_static_response (struct TMH_RequestHandler *rh,
+TMH_MHD_handler_static_response (const struct TMH_RequestHandler *rh,
                                  struct MHD_Connection *connection,
-                                 void **connection_cls,
-                                 const char *upload_data,
-                                 size_t *upload_data_size,
-                                 struct MerchantInstance *instance)
+                                 struct TMH_HandlerContext *hc)
 {
-  (void) instance;
-  (void) connection_cls;
-  (void) upload_data;
-  (void) upload_data_size;
-  (void) instance;
-  if (0 == rh->data_size)
-    rh->data_size = strlen ((const char *) rh->data);
+  (void) hc;
   return TALER_MHD_reply_static (connection,
                                  rh->response_code,
                                  rh->mime_type,
@@ -68,24 +56,16 @@ TMH_MHD_handler_static_response (struct TMH_RequestHandler 
*rh,
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL (but unused)
+ * @param[in,out] hc handler context (can be updated)
  * @return MHD result code
  */
 MHD_RESULT
-TMH_MHD_handler_agpl_redirect (struct TMH_RequestHandler *rh,
+TMH_MHD_handler_agpl_redirect (const struct TMH_RequestHandler *rh,
                                struct MHD_Connection *connection,
-                               void **connection_cls,
-                               const char *upload_data,
-                               size_t *upload_data_size,
-                               struct MerchantInstance *mi)
+                               struct TMH_HandlerContext *hc)
 {
-  (void) mi;
-  (void) connection_cls;
-  (void) upload_data;
-  (void) upload_data_size;
+  (void) rh;
+  (void) hc;
   return TALER_MHD_reply_agpl (connection,
                                "http://www.git.taler.net/?p=merchant.git";);
 }
diff --git a/src/backend/taler-merchant-httpd_mhd.h 
b/src/backend/taler-merchant-httpd_mhd.h
index 8fc78a2..cbf83ad 100644
--- a/src/backend/taler-merchant-httpd_mhd.h
+++ b/src/backend/taler-merchant-httpd_mhd.h
@@ -34,19 +34,13 @@
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, NULL is allowed in this case!
+ * @param[in,out] hc handler context (can be updated)
  * @return MHD result code
  */
 MHD_RESULT
-TMH_MHD_handler_static_response (struct TMH_RequestHandler *rh,
+TMH_MHD_handler_static_response (const struct TMH_RequestHandler *rh,
                                  struct MHD_Connection *connection,
-                                 void **connection_cls,
-                                 const char *upload_data,
-                                 size_t *upload_data_size,
-                                 struct MerchantInstance *mi);
+                                 struct TMH_HandlerContext *hc);
 
 
 /**
@@ -55,19 +49,13 @@ TMH_MHD_handler_static_response (struct TMH_RequestHandler 
*rh,
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL
+ * @param[in,out] hc handler context (can be updated)
  * @return MHD result code
  */
 MHD_RESULT
-TMH_MHD_handler_agpl_redirect (struct TMH_RequestHandler *rh,
+TMH_MHD_handler_agpl_redirect (const struct TMH_RequestHandler *rh,
                                struct MHD_Connection *connection,
-                               void **connection_cls,
-                               const char *upload_data,
-                               size_t *upload_data_size,
-                               struct MerchantInstance *mi);
+                               struct TMH_HandlerContext *hc);
 
 
 /**
@@ -111,7 +99,7 @@ TMH_MHD_handler_send_json_pack_error (struct 
TMH_RequestHandler *rh,
                                       void **connection_cls,
                                       const char *upload_data,
                                       size_t *upload_data_size,
-                                      struct MerchantInstance *mi);
+                                      struct TMH_MerchantInstance *mi);
 
 
 #endif
diff --git a/src/backend/taler-merchant-httpd_config.c 
b/src/backend/taler-merchant-httpd_private-get-instances.c
similarity index 98%
copy from src/backend/taler-merchant-httpd_config.c
copy to src/backend/taler-merchant-httpd_private-get-instances.c
index d6f6c92..8a08ab1 100644
--- a/src/backend/taler-merchant-httpd_config.c
+++ b/src/backend/taler-merchant-httpd_private-get-instances.c
@@ -25,8 +25,6 @@
 #include "taler-merchant-httpd.h"
 #include "taler-merchant-httpd_mhd.h"
 #include "taler-merchant-httpd_exchanges.h"
-#include "taler-merchant-httpd_tip-query.h"
-#include "taler-merchant-httpd_tip-reserve-helper.h"
 
 
 /**
diff --git a/src/backend/taler-merchant-httpd_config.h 
b/src/backend/taler-merchant-httpd_transfers-get.h
similarity index 57%
copy from src/backend/taler-merchant-httpd_config.h
copy to src/backend/taler-merchant-httpd_transfers-get.h
index 1a5dfd6..0463295 100644
--- a/src/backend/taler-merchant-httpd_config.h
+++ b/src/backend/taler-merchant-httpd_transfers-get.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  (C) 2019 Taler Systems SA
+  (C) 2014-2020 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
@@ -14,17 +14,20 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file backend/taler-merchant-httpd_config.h
- * @brief headers for /config handler
- * @author Florian Dold
+ * @file backend/taler-merchant-httpd_track-transfer.h
+ * @brief headers for /track/transfer handler
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
  */
-#ifndef TALER_MERCHANT_HTTPD_CONFIG_H
-#define TALER_MERCHANT_HTTPD_CONFIG_H
+#ifndef TALER_MERCHANT_HTTPD_TRACK_TRANSFER_H
+#define TALER_MERCHANT_HTTPD_TRACK_TRANSFER_H
 #include <microhttpd.h>
 #include "taler-merchant-httpd.h"
 
 /**
- * Manages a /config call.
+ * Manages a /track/transfer call, thus it calls the /wire/transfer
+ * offered by the exchange in order to return the set of transfers
+ * (of coins) associated with a given wire transfer
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
@@ -35,11 +38,12 @@
  * @return MHD result code
  */
 MHD_RESULT
-MH_handler_config (struct TMH_RequestHandler *rh,
-                   struct MHD_Connection *connection,
-                   void **connection_cls,
-                   const char *upload_data,
-                   size_t *upload_data_size,
-                   struct MerchantInstance *mi);
+MH_handler_track_transfer (struct TMH_RequestHandler *rh,
+                           struct MHD_Connection *connection,
+                           void **connection_cls,
+                           const char *upload_data,
+                           size_t *upload_data_size,
+                           struct MerchantInstance *mi);
+
 
 #endif
diff --git a/src/backenddb/merchantdb_plugin.c 
b/src/backenddb/merchantdb_plugin.c
index 473c659..a8e046e 100644
--- a/src/backenddb/merchantdb_plugin.c
+++ b/src/backenddb/merchantdb_plugin.c
@@ -32,7 +32,7 @@
  * @return #GNUNET_OK on success
  */
 struct TALER_MERCHANTDB_Plugin *
-TALER_MERCHANTDB_plugin_load (struct GNUNET_CONFIGURATION_Handle *cfg)
+TALER_MERCHANTDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
   char *plugin_name;
   char *lib_name;
@@ -54,7 +54,7 @@ TALER_MERCHANTDB_plugin_load (struct 
GNUNET_CONFIGURATION_Handle *cfg)
                           plugin_name);
   GNUNET_free (plugin_name);
   plugin = GNUNET_PLUGIN_load (lib_name,
-                               cfg);
+                               (void *) cfg);
   if (NULL != plugin)
     plugin->library_name = lib_name;
   else
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index 697732d..417fb12 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -3121,7 +3121,7 @@ RETRY:
 void *
 libtaler_plugin_merchantdb_postgres_init (void *cls)
 {
-  struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+  const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
   struct PostgresClosure *pg;
   struct TALER_MERCHANTDB_Plugin *plugin;
   struct GNUNET_PQ_PreparedStatement ps[] = {
diff --git a/src/include/taler_merchantdb_lib.h 
b/src/include/taler_merchantdb_lib.h
index eba702b..5ebbf73 100644
--- a/src/include/taler_merchantdb_lib.h
+++ b/src/include/taler_merchantdb_lib.h
@@ -37,7 +37,7 @@ struct TALER_MERCHANTDB_Plugin;
  * @return connection to the database; NULL upon error
  */
 struct TALER_MERCHANTDB_Plugin *
-TALER_MERCHANTDB_plugin_load (struct GNUNET_CONFIGURATION_Handle *cfg);
+TALER_MERCHANTDB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg);
 
 
 /**

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