gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-merchant] branch stable updated (806ec0f -> 5ed6f6f)


From: gnunet
Subject: [GNUnet-SVN] [taler-merchant] branch stable updated (806ec0f -> 5ed6f6f)
Date: Fri, 24 May 2019 18:18:49 +0200

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

dold pushed a change to branch stable
in repository merchant.

    from 806ec0f  remove instance from order after looking up merchant info
     add df31a86  fix jansson format string
     add 80c84ec  Create async scopes.
     add 821109d  replace if with AS_IF (fixes #5709 for merchant)
     add ca0f567  Resolve #5719
     add 514a101  Compressing/decompressing /proposal.
     add 69e616d  rename
     add d565780  indent
     add 2ec30cc  compressing lib pay
     add 08c149b  more compression
     add 55b6620  completing lib compression
     add 5ed6f6f  rename

No new revisions were added by this update.

Summary of changes:
 configure.ac                                 |  22 +-
 src/backend/taler-merchant-httpd.c           |  64 +++-
 src/backend/taler-merchant-httpd.h           |   4 +
 src/backend/taler-merchant-httpd_exchanges.c |   1 +
 src/backend/taler-merchant-httpd_parsing.c   | 209 +++--------
 src/backend/taler-merchant-httpd_pay.c       |   4 +-
 src/backend/taler-merchant-httpd_proposal.c  |   1 +
 src/lib/merchant_api_pay.c                   | 242 +++++++------
 src/lib/merchant_api_pay_abort.c             | 498 ---------------------------
 src/lib/merchant_api_proposal.c              | 115 ++++---
 src/lib/merchant_api_refund.c                |  46 ++-
 src/lib/merchant_api_tip_authorize.c         |  51 ++-
 src/lib/merchant_api_tip_pickup.c            |  50 ++-
 src/lib/merchant_api_tip_query.c             |   3 +
 src/lib/test_merchant_api.c                  |   1 +
 src/lib/test_merchant_api_twisted.c          |  76 ++++
 16 files changed, 473 insertions(+), 914 deletions(-)
 delete mode 100644 src/lib/merchant_api_pay_abort.c

diff --git a/configure.ac b/configure.ac
index 4f9945b..01ff7ad 100644
--- a/configure.ac
+++ b/configure.ac
@@ -32,8 +32,9 @@ AC_ARG_ENABLE([only-doc],
 AC_MSG_RESULT($doc_only)
 AM_CONDITIONAL([DOC_ONLY], [test "$doc_only" = "yes"])
 
-if test "x$doc_only" != xyes
-then
+
+# Not indented as it covers most of the file...
+AS_IF([test "x$doc_only" != xyes],[
 
 
 # Checks for programs.
@@ -74,10 +75,7 @@ AS_IF([test $libgnunetutil != 1],
 
 # test for postgres
 AX_LIB_POSTGRESQL([9.3])
-if test "x$found_postgresql" = "xyes"
-then
-  postgres=true
-fi
+AS_IF([test "x$found_postgresql" = "xyes"],[postgres=true])
 AM_CONDITIONAL(HAVE_POSTGRESQL, test x$postgres = xtrue)
 
 # Check for Taler's libtalerpq
@@ -137,13 +135,12 @@ PKG_CHECK_MODULES([JANSSON], [jansson >= 2.3],
 # check for libgnurl
 # libgnurl
 LIBGNURL_CHECK_CONFIG(,7.34.0,gnurl=1,gnurl=0)
-if test "x$gnurl" = x1
-then
+AS_IF([test "x$gnurl" = x1],[
        AM_CONDITIONAL(HAVE_LIBGNURL, true)
        AC_DEFINE([HAVE_LIBGNURL],[1],[Have libgnurl])
-else
+],[
        AM_CONDITIONAL(HAVE_LIBGNURL, false)
-fi
+])
 
 # libcurl-gnutls
 LIBCURL_CHECK_CONFIG(,7.34.0,[curl=true],[curl=false])
@@ -305,7 +302,8 @@ AC_ARG_ENABLE([[doc]],
 test "x$enable_doc" = "xno" || enable_doc=yes
 AM_CONDITIONAL([ENABLE_DOC], [test "x$enable_doc" = "xyes"])
 
-else
+
+],[  # this is about the doc-only if on top of the file
 
 # logic if doc_only is set, make sure conditionals are still defined
 AM_CONDITIONAL([HAVE_GNUNETPQ], [false])
@@ -320,7 +318,7 @@ AM_CONDITIONAL([HAVE_TWISTER], [true])
 
 
 # end of 'doc_only'
-fi
+])
 
 
 # should experimental code be compiled (code that may not yet compile / have 
passing test cases)?
diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index 77dd744..b92aad4 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -152,6 +152,24 @@ static mode_t unixpath_mode;
 
 
 /**
+ * Return GNUNET_YES if given a valid correlation ID and
+ * GNUNET_NO otherwise.
+ *
+ * @returns GNUNET_YES iff given a valid correlation ID
+ */
+static int
+is_valid_correlation_id (const char *correlation_id)
+{
+  if (strlen (correlation_id) >= 64)
+    return GNUNET_NO;
+  for (int i = 0; i < strlen (correlation_id); i++)
+    if (!(isalnum (correlation_id[i]) || correlation_id[i] == '-'))
+      return GNUNET_NO;
+  return GNUNET_YES;
+}
+
+
+/**
  * A client has requested the given url using the given method
  * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
  * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc).  The callback
@@ -282,6 +300,46 @@ url_handler (void *cls,
               "Handling request (%s) for URL `%s'\n",
               method,
               url);
+
+  struct TM_HandlerContext *hc;
+  struct GNUNET_AsyncScopeId aid;
+  const char *correlation_id = NULL;
+
+  hc = *con_cls;
+
+  if (NULL == hc)
+  {
+    GNUNET_async_scope_fresh (&aid);
+    /* We only read the correlation ID on the first callback for every client 
*/
+    correlation_id = MHD_lookup_connection_value (connection,
+                                                  MHD_HEADER_KIND,
+                                                  "Taler-Correlation-Id");
+    if ((NULL != correlation_id) &&
+        (GNUNET_YES != is_valid_correlation_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);
+
   for (unsigned int i=0;NULL != handlers[i].url;i++)
   {
     struct TMH_RequestHandler *rh = &handlers[i];
@@ -292,7 +350,6 @@ url_handler (void *cls,
            (0 == strcasecmp (method,
                              rh->method)) ) )
     {
-      struct TM_HandlerContext *hc;
       int ret;
 
       ret = rh->handler (rh,
@@ -302,7 +359,12 @@ url_handler (void *cls,
                         upload_data_size);
       hc = *con_cls;
       if (NULL != hc)
+      {
         hc->rh = rh;
+        /* Store the async context ID, so we can restore it if
+         * we get another callack for this request. */
+        hc->async_scope_id = aid;
+      }
       return ret;
     }
   }
diff --git a/src/backend/taler-merchant-httpd.h 
b/src/backend/taler-merchant-httpd.h
index 9d494db..ee3d88f 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -257,6 +257,10 @@ struct TM_HandlerContext
    */
   const struct TMH_RequestHandler *rh;
 
+  /**
+   * Asynchronous request context id.
+   */
+  struct GNUNET_AsyncScopeId async_scope_id;
 };
 
 
diff --git a/src/backend/taler-merchant-httpd_exchanges.c 
b/src/backend/taler-merchant-httpd_exchanges.c
index b8c9be0..ed6ece9 100644
--- a/src/backend/taler-merchant-httpd_exchanges.c
+++ b/src/backend/taler-merchant-httpd_exchanges.c
@@ -1056,6 +1056,7 @@ TMH_EXCHANGES_init (const struct 
GNUNET_CONFIGURATION_Handle *cfg)
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
+  GNUNET_CURL_enable_async_scope_header (merchant_curl_ctx, 
"Taler-Correlation-Id");
   merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (merchant_curl_ctx);
   /* get exchanges from the merchant configuration and try to connect to them 
*/
   GNUNET_CONFIGURATION_iterate_sections (cfg,
diff --git a/src/backend/taler-merchant-httpd_parsing.c 
b/src/backend/taler-merchant-httpd_parsing.c
index 3f368b9..743999d 100644
--- a/src/backend/taler-merchant-httpd_parsing.c
+++ b/src/backend/taler-merchant-httpd_parsing.c
@@ -2,21 +2,25 @@
   This file is part of TALER
   Copyright (C) 2014, 2015, 2016 GNUnet e.V.
 
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU Affero General Public License as published by the Free 
Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
-
-  You should have received a copy of the GNU Affero General Public License 
along with
-  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Affero General Public License as
+  published by the Free Software Foundation; either version 3,
+  or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Affero General Public License for more details.
+
+  You should have received a copy of the GNU Affero General Public
+  License along with TALER; see the file COPYING.  If not,
+  see <http://www.gnu.org/licenses/>
 */
 
 /**
  * @file taler-exchange-httpd_parsing.c
- * @brief functions to parse incoming requests (MHD arguments and JSON 
snippets)
+ * @brief functions to parse incoming requests
+ *        (MHD arguments and JSON snippets)
  * @author Florian Dold
  * @author Benedikt Mueller
  * @author Christian Grothoff
@@ -63,38 +67,6 @@ struct Buffer
 };
 
 
-/**
- * Initialize a buffer and copy first chunk of data in it.
- *
- * @param buf the buffer to initialize
- * @param data the initial data
- * @param data_size size of the initial data
- * @param alloc_size size of the buffer
- * @param max_size maximum size that the buffer can grow to
- * @return a GNUnet result code
- */
-static int
-buffer_init (struct Buffer *buf,
-             const void *data,
-             size_t data_size,
-             size_t alloc_size,
-             size_t max_size)
-{
-  if (data_size > max_size || alloc_size > max_size)
-    return GNUNET_SYSERR;
-  if (data_size > alloc_size)
-    alloc_size = data_size;
-  if (0 == alloc_size)
-  {
-    buf->data = NULL;
-    return GNUNET_OK;
-  }
-  buf->data = GNUNET_malloc (alloc_size);
-  GNUNET_memcpy (buf->data,
-                 data,
-                 data_size);
-  return GNUNET_OK;
-}
 
 
 /**
@@ -111,46 +83,6 @@ buffer_deinit (struct Buffer *buf)
 }
 
 
-/**
- * Append data to a buffer, growing the buffer if necessary.
- *
- * @param buf the buffer to append to
- * @param data the data to append
- * @param data_size the size of @a data
- * @param max_size maximum size that the buffer can grow to
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO if the buffer can't accomodate for the new data
- */
-static int
-buffer_append (struct Buffer *buf,
-               const void *data,
-               size_t data_size,
-               size_t max_size)
-{
-  if (buf->fill + data_size > max_size)
-    return GNUNET_NO;
-  if (data_size + buf->fill > buf->alloc)
-  {
-    char *new_buf;
-    size_t new_size = buf->alloc ? buf->alloc : 1;
-
-    while (new_size < buf->fill + data_size)
-      new_size *= 2;
-    if (new_size > max_size)
-      return GNUNET_NO;
-    new_buf = GNUNET_malloc (new_size);
-    memcpy (new_buf, buf->data, buf->fill);
-    GNUNET_free (buf->data);
-    buf->data = new_buf;
-    buf->alloc = new_size;
-  }
-  memcpy (buf->data + buf->fill,
-         data,
-         data_size);
-  buf->fill += data_size;
-  return GNUNET_OK;
-}
-
 
 /**
  * Function called whenever we are done with a request
@@ -175,9 +107,9 @@ TMH_PARSE_post_cleanup_callback (void *con_cls)
 /**
  * Process a POST request containing a JSON object.  This function
  * realizes an MHD POST processor that will (incrementally) process
- * JSON data uploaded to the HTTP server.  It will store the required
- * state in the @a con_cls, which must be cleaned up using
- * #TMH_PARSE_post_cleanup_callback().
+ * JSON data uploaded to the HTTP server.  It will store the
+ * required state in the @a con_cls, which must be cleaned up
+ * using #TMH_PARSE_post_cleanup_callback().
  *
  * @param connection the MHD connection
  * @param con_cls the closure (points to a `struct Buffer *`)
@@ -202,86 +134,41 @@ TMH_PARSE_post_json (struct MHD_Connection *connection,
                      size_t *upload_data_size,
                      json_t **json)
 {
-  struct Buffer *r = *con_cls;
-
-  TALER_LOG_DEBUG ("Will parse: %.*s\n",
-                   (int) *upload_data_size,
-                   upload_data);
-  *json = NULL;
-  if (NULL == *con_cls)
+  enum GNUNET_JSON_PostResult pr;
+
+  pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+                                connection,
+                                con_cls,
+                                upload_data,
+                                upload_data_size,
+                                json);
+  switch (pr)
   {
-    /* We are seeing a fresh POST request. */
-    r = GNUNET_new (struct Buffer);
-    if (GNUNET_OK !=
-        buffer_init (r,
-                     upload_data,
-                     *upload_data_size,
-                     REQUEST_BUFFER_INITIAL,
-                     REQUEST_BUFFER_MAX))
-    {
-      *con_cls = NULL;
-      buffer_deinit (r);
-      GNUNET_free (r);
-      /* return GNUNET_SYSERR if this isn't even
-       * able to generate proper error response.  */
-      return (MHD_NO == TMH_RESPONSE_reply_internal_error
-        (connection,
-         TALER_EC_PARSER_OUT_OF_MEMORY,
-         "out of memory")) ? GNUNET_SYSERR : GNUNET_NO;
-    }
-    /* everything OK, wait for more POST data */
-    *upload_data_size = 0;
-    *con_cls = r;
-    return GNUNET_YES;
-  }
 
-  /* When zero, upload is over.  */
-  if (0 != *upload_data_size)
-  {
-    TALER_LOG_INFO ("Parser asking for more data"
-                    ", current data size is %zu\n",
-                    *upload_data_size);
-
-    /* We are seeing an old request with more data available. */
-    if (GNUNET_OK !=
-        buffer_append (r,
-                       upload_data,
-                       *upload_data_size,
-                       REQUEST_BUFFER_MAX))
-    {
-      /* Request too long */
-      *con_cls = NULL;
-      buffer_deinit (r);
-      GNUNET_free (r);
-      return (MHD_NO == TMH_RESPONSE_reply_request_too_large
-        (connection)) ? GNUNET_SYSERR : GNUNET_NO;
-    }
-
-    /* everything OK, wait for more POST data */
-    *upload_data_size = 0;
+  case GNUNET_JSON_PR_OUT_OF_MEMORY:
+    return (MHD_NO == TMH_RESPONSE_reply_internal_error
+      (connection,
+       TALER_EC_PARSER_OUT_OF_MEMORY,
+       "out of memory")) ? GNUNET_SYSERR : GNUNET_NO;
+
+  case GNUNET_JSON_PR_CONTINUE:
     return GNUNET_YES;
-  }
 
-  TALER_LOG_DEBUG ("About to parse: %.*s\n",
-                   (int) r->fill,
-                   r->data);
-  /* We have seen the whole request. */
-  *json = json_loadb (r->data,
-                      r->fill,
-                      0,
-                      NULL);
-  if (NULL == *json)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Failed to parse JSON request body\n");
-    return (MHD_YES == TMH_RESPONSE_reply_invalid_json
-      (connection)) ? GNUNET_NO : GNUNET_SYSERR;
-  }
-  buffer_deinit (r);
-  GNUNET_free (r);
-  *con_cls = NULL;
+  case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+    return (MHD_NO == TMH_RESPONSE_reply_request_too_large
+      (connection)) ? GNUNET_SYSERR : GNUNET_NO;
 
-  return GNUNET_YES;
+  case GNUNET_JSON_PR_JSON_INVALID:
+    return (MHD_YES ==
+            TMH_RESPONSE_reply_invalid_json (connection))
+      ? GNUNET_NO : GNUNET_SYSERR;
+  case GNUNET_JSON_PR_SUCCESS:
+    GNUNET_break (NULL != *json);
+    return GNUNET_YES;
+  }
+  /* this should never happen */
+  GNUNET_break (0);
+  return GNUNET_SYSERR;
 }
 
 
diff --git a/src/backend/taler-merchant-httpd_pay.c 
b/src/backend/taler-merchant-httpd_pay.c
index ec52ff2..6f407e3 100644
--- a/src/backend/taler-merchant-httpd_pay.c
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -935,7 +935,7 @@ deposit_cb (void *cls,
       /* We can't do anything meaningful here, the exchange did something 
wrong */
       resume_pay_with_response (pc,
                                 MHD_HTTP_SERVICE_UNAVAILABLE,
-                                TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:I, 
s:s}",
+                                TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:I, 
s:I, s:s}",
                                                              "error", 
"exchange failed",
                                                              "code", 
(json_int_t) TALER_EC_PAY_EXCHANGE_FAILED,
                                                              "exchange-code", 
(json_int_t) ec,
@@ -2024,7 +2024,7 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
   json_t *root;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "In handler for /pay.\n");
+              "In handler for /pay.\n");
   if (NULL == *connection_cls)
   {
     pc = GNUNET_new (struct PayContext);
diff --git a/src/backend/taler-merchant-httpd_proposal.c 
b/src/backend/taler-merchant-httpd_proposal.c
index be4359b..a65bf46 100644
--- a/src/backend/taler-merchant-httpd_proposal.c
+++ b/src/backend/taler-merchant-httpd_proposal.c
@@ -651,6 +651,7 @@ MH_handler_proposal_put (struct TMH_RequestHandler *rh,
                              upload_data,
                              upload_data_size,
                              &root);
+
   if (GNUNET_SYSERR == res)
     return MHD_NO;
 
diff --git a/src/lib/merchant_api_pay.c b/src/lib/merchant_api_pay.c
index be70f6d..e583601 100644
--- a/src/lib/merchant_api_pay.c
+++ b/src/lib/merchant_api_pay.c
@@ -2,22 +2,26 @@
   This file is part of TALER
   Copyright (C) 2014, 2015, 2016, 2017 GNUnet e.V. and INRIA
 
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU Lesser General Public License as published by the Free 
Software
-  Foundation; either version 2.1, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
details.
-
-  You should have received a copy of the GNU Lesser General Public License 
along with
-  TALER; see the file COPYING.LGPL.  If not, see
-  <http://www.gnu.org/licenses/>
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1,
+  or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General
+  Public License along with TALER; see the file COPYING.LGPL.
+  If not, see <http://www.gnu.org/licenses/>
 */
 /**
  * @file lib/merchant_api_pay.c
- * @brief Implementation of the /pay request of the merchant's HTTP API
+ * @brief Implementation of the /pay request
+ *        of the merchant's HTTP API
  * @author Christian Grothoff
+ * @author Marcello Stanisci
  */
 #include "platform.h"
 #include <curl/curl.h>
@@ -29,6 +33,7 @@
 #include <taler/taler_json_lib.h>
 #include <taler/taler_signatures.h>
 #include <taler/taler_exchange_service.h>
+#include <taler/taler_curl_lib.h>
 
 
 /**
@@ -43,11 +48,6 @@ struct TALER_MERCHANT_Pay
   char *url;
 
   /**
-   * JSON encoding of the request to POST.
-   */
-  char *json_enc;
-
-  /**
    * Handle for the request.
    */
   struct GNUNET_CURL_Job *job;
@@ -83,6 +83,11 @@ struct TALER_MERCHANT_Pay
   struct GNUNET_CURL_Context *ctx;
 
   /**
+   * Minor context that holds body and headers.
+   */
+  struct TEAH_PostContext post_ctx;
+
+  /**
    * The coins we are paying with.
    */
   struct TALER_MERCHANT_PaidCoin *coins;
@@ -160,8 +165,10 @@ check_abort_refund (struct TALER_MERCHANT_Pay *ph,
         return GNUNET_SYSERR;
       }
 
-      rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
-      rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
+      rr.purpose.purpose = htonl
+        (TALER_SIGNATURE_MERCHANT_REFUND);
+      rr.purpose.size = htonl
+        (sizeof (struct TALER_RefundRequestPS));
       rr.h_contract_terms = ph->h_contract_terms;
       rr.coin_pub = res[i].coin_pub;
       rr.merchant = merchant_pub;
@@ -171,7 +178,8 @@ check_abort_refund (struct TALER_MERCHANT_Pay *ph,
       {
        if (0 == memcmp (&ph->coins[j].coin_pub,
                         &res[i].coin_pub,
-                        sizeof (struct TALER_CoinSpendPublicKeyP)))
+                        sizeof
+                           (struct TALER_CoinSpendPublicKeyP)))
        {
          TALER_amount_hton (&rr.refund_amount,
                             &ph->coins[j].amount_with_fee);
@@ -187,10 +195,11 @@ check_abort_refund (struct TALER_MERCHANT_Pay *ph,
       }
 
       if (GNUNET_OK !=
-         GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
-                                     &rr.purpose,
-                                     &sig->eddsa_sig,
-                                     &merchant_pub.eddsa_pub))
+         GNUNET_CRYPTO_eddsa_verify
+            (TALER_SIGNATURE_MERCHANT_REFUND,
+            &rr.purpose,
+            &sig->eddsa_sig,
+            &merchant_pub.eddsa_pub))
       {
        GNUNET_break_op (0);
        return GNUNET_SYSERR;
@@ -217,8 +226,8 @@ check_abort_refund (struct TALER_MERCHANT_Pay *ph,
  * coin was actually already spent!
  *
  * @param pc handle of the original coin we paid with
- * @param json cryptographic proof of coin's transaction history as
- *        was returned by the exchange/merchant
+ * @param json cryptographic proof of coin's transaction
+ *        history as was returned by the exchange/merchant
  * @return #GNUNET_OK if proof checks out
  */
 static int
@@ -229,10 +238,11 @@ check_coin_history (const struct TALER_MERCHANT_PaidCoin 
*pc,
   struct TALER_Amount spent_plus_contrib;
 
   if (GNUNET_OK !=
-      TALER_EXCHANGE_verify_coin_history (pc->amount_with_fee.currency,
-                                          &pc->coin_pub,
-                                          json,
-                                          &spent))
+      TALER_EXCHANGE_verify_coin_history
+        (pc->amount_with_fee.currency,
+         &pc->coin_pub,
+         json,
+         &spent))
   {
     /* Exchange's history fails to verify */
     GNUNET_break_op (0);
@@ -267,7 +277,8 @@ check_coin_history (const struct TALER_MERCHANT_PaidCoin 
*pc,
  * coin was actually already spent!
  *
  * @param ph handle of the original pay operation
- * @param json cryptographic proof returned by the exchange/merchant
+ * @param json cryptographic proof returned by the
+ *        exchange/merchant
  * @return #GNUNET_OK if proof checks out
  */
 static int
@@ -344,8 +355,9 @@ handle_pay_finished (void *cls,
     case MHD_HTTP_NOT_ACCEPTABLE:
       break;
     case MHD_HTTP_BAD_REQUEST:
-      /* This should never happen, either us or the merchant is buggy
-        (or API version conflict); just pass JSON reply to the application */
+      /* This should never happen, either us
+       * or the merchant is buggy (or API version conflict);
+       * just pass JSON reply to the application */
       break;
     case MHD_HTTP_FORBIDDEN:
       if (GNUNET_OK != check_forbidden (ph,
@@ -356,17 +368,23 @@ handle_pay_finished (void *cls,
       }
       break;
     case MHD_HTTP_UNAUTHORIZED:
-      /* Nothing really to verify, merchant says one of the signatures is
-        invalid; as we checked them, this should never happen, we
-        should pass the JSON reply to the application */
+      /* Nothing really to verify, merchant says one of the
+       * signatures is invalid; as we checked them, this
+       * should never happen, we should pass the JSON reply
+       * to the application */
       break;
     case MHD_HTTP_NOT_FOUND:
       /* Nothing really to verify, this should never
-        happen, we should pass the JSON reply to the application */
+        happen, we should pass the JSON reply to the
+         application */
       break;
     case MHD_HTTP_INTERNAL_SERVER_ERROR:
-      /* Server had an internal issue; we should retry, but this API
-        leaves this to the application */
+      /* Server had an internal issue; we should retry,
+         but this API leaves this to the application */
+      break;
+    case MHD_HTTP_SERVICE_UNAVAILABLE:
+      /* Exchange couldn't respond properly; the retry is
+         left to the application */
       break;
     default:
       /* unexpected response code */
@@ -402,23 +420,26 @@ handle_pay_finished (void *cls,
       response_code = 0;
       break;
     case MHD_HTTP_BAD_REQUEST:
-      /* This should never happen, either us or the merchant is buggy
-        (or API version conflict); just pass JSON reply to the application */
+      /* This should never happen, either us or the
+         merchant is buggy (or API version conflict); just
+         pass JSON reply to the application */
       break;
     case MHD_HTTP_FORBIDDEN:
       break;
     case MHD_HTTP_UNAUTHORIZED:
-      /* Nothing really to verify, merchant says one of the signatures is
-        invalid; as we checked them, this should never happen, we
-        should pass the JSON reply to the application */
+      /* Nothing really to verify, merchant says one of
+         the signatures is invalid; as we checked them,
+         this should never happen, we should pass the JSON
+         reply to the application */
       break;
     case MHD_HTTP_NOT_FOUND:
       /* Nothing really to verify, this should never
-        happen, we should pass the JSON reply to the application */
+        happen, we should pass the JSON reply to the
+         application */
       break;
     case MHD_HTTP_INTERNAL_SERVER_ERROR:
-      /* Server had an internal issue; we should retry, but this API
-        leaves this to the application */
+      /* Server had an internal issue; we should retry,
+         but this API leaves this to the application */
       break;
     default:
       /* unexpected response code */
@@ -453,24 +474,26 @@ handle_pay_finished (void *cls,
  * @param num_coins number of coins used to pay
  * @param coins array of coins we use to pay
  * @param mode mode string to use ("pay" or "abort-refund").
- * @param pay_cb the callback to call when a reply for this request is 
available
+ * @param pay_cb the callback to call when a reply for this
+ *        request is available
  * @param pay_cb_cls closure for @a pay_cb
  * @param abort_cb callback to call for the abort-refund variant
  * @param abort_cb_cls closure for @a abort_cb
  * @return a handle for this request
  */
 static struct TALER_MERCHANT_Pay *
-request_pay_generic (struct GNUNET_CURL_Context *ctx,
-                    const char *merchant_url,
-                    const struct TALER_MerchantPublicKeyP *merchant_pub,
-                    const char *order_id,
-                    unsigned int num_coins,
-                    const struct TALER_MERCHANT_PaidCoin *coins,
-                    const char *mode,
-                    TALER_MERCHANT_PayCallback pay_cb,
-                    void *pay_cb_cls,
-                    TALER_MERCHANT_PayRefundCallback abort_cb,
-                    void *abort_cb_cls)
+request_pay_generic
+  (struct GNUNET_CURL_Context *ctx,
+   const char *merchant_url,
+   const struct TALER_MerchantPublicKeyP *merchant_pub,
+   const char *order_id,
+   unsigned int num_coins,
+   const struct TALER_MERCHANT_PaidCoin *coins,
+   const char *mode,
+   TALER_MERCHANT_PayCallback pay_cb,
+   void *pay_cb_cls,
+   TALER_MERCHANT_PayRefundCallback abort_cb,
+   void *abort_cb_cls)
 {
   struct TALER_MERCHANT_Pay *ph;
   json_t *pay_obj;
@@ -526,16 +549,20 @@ request_pay_generic (struct GNUNET_CURL_Context *ctx,
     }
 
     /* create JSON for this coin */
-    j_coin = json_pack ("{s:o, s:o," /* contribution/coin_pub */
-                       " s:s, s:o," /* exchange_url / denom_pub */
-                       " s:o, s:o}", /* ub_sig / coin_sig */
-                       "contribution", TALER_JSON_from_amount 
(&pc->amount_with_fee),
-                       "coin_pub", GNUNET_JSON_from_data_auto (&pc->coin_pub),
-                       "exchange_url", pc->exchange_url,
-                       "denom_pub", GNUNET_JSON_from_rsa_public_key 
(pc->denom_pub.rsa_public_key),
-                       "ub_sig", GNUNET_JSON_from_rsa_signature 
(pc->denom_sig.rsa_signature),
-                       "coin_sig", GNUNET_JSON_from_data_auto (&pc->coin_sig)
-                       );
+    j_coin = json_pack
+      ("{s:o, s:o," /* contribution/coin_pub */
+       " s:s, s:o," /* exchange_url / denom_pub */
+       " s:o, s:o}", /* ub_sig / coin_sig */
+       "contribution", TALER_JSON_from_amount
+         (&pc->amount_with_fee),
+       "coin_pub", GNUNET_JSON_from_data_auto
+         (&pc->coin_pub),
+       "exchange_url", pc->exchange_url,
+       "denom_pub", GNUNET_JSON_from_rsa_public_key
+         (pc->denom_pub.rsa_public_key),
+       "ub_sig", GNUNET_JSON_from_rsa_signature
+         (pc->denom_sig.rsa_signature),
+       "coin_sig", GNUNET_JSON_from_data_auto (&pc->coin_sig));
     if (0 !=
         json_array_append_new (j_coins,
                                j_coin))
@@ -555,7 +582,8 @@ request_pay_generic (struct GNUNET_CURL_Context *ctx,
                       "mode", mode,
                       "coins", j_coins,
                        "order_id", order_id,
-                       "merchant_pub", GNUNET_JSON_from_data_auto 
(merchant_pub));
+                       "merchant_pub", GNUNET_JSON_from_data_auto
+                         (merchant_pub));
   if (NULL == pay_obj)
   {
     GNUNET_break (0);
@@ -577,53 +605,61 @@ request_pay_generic (struct GNUNET_CURL_Context *ctx,
           num_coins * sizeof (struct TALER_MERCHANT_PaidCoin));
 
   eh = curl_easy_init ();
-  GNUNET_assert (NULL != (ph->json_enc =
-                          json_dumps (pay_obj,
-                                      JSON_COMPACT)));
+
+  if (GNUNET_OK != TALER_curl_easy_post (&ph->post_ctx,
+                                         eh,
+                                         pay_obj))
+  {
+    GNUNET_break (0);
+    GNUNET_free (ph);
+    return NULL;
+  }
+
   json_decref (pay_obj);
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_URL,
-                                   ph->url));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDS,
-                                   ph->json_enc));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDSIZE,
-                                   strlen (ph->json_enc)));
-  ph->job = GNUNET_CURL_job_add (ctx,
-                                 eh,
-                                 GNUNET_YES,
-                                 &handle_pay_finished,
-                                 ph);
+  GNUNET_assert (CURLE_OK == curl_easy_setopt (eh,
+                                               CURLOPT_URL,
+                                               ph->url));
+  ph->job = GNUNET_CURL_job_add2 (ctx,
+                                  eh,
+                                  ph->post_ctx.headers,
+                                  &handle_pay_finished,
+                                  ph);
   return ph;
 }
 
 
 /**
- * Pay a merchant.  API for wallets that have the coin's private keys.
- * _NOTE_: this function does NOT calculate each coin amount in order
- * to match the contract total price.  This calculation is to be made
- * by the logic using this library.
+ * Pay a merchant.  API for wallets that have the coin's private
+ * keys.
+ * _NOTE_: this function does NOT calculate each coin amount in
+ * order to match the contract total price.  This calculation is
+ * to be made by the logic using this library.
  *
  * @param ctx the execution loop context
  * @param merchant_url base URL of the merchant's backend
- * @param instance which merchant instance will receive this payment
+ * @param instance which merchant instance will receive this
+ *        payment
  * @param h_contract_terms hashcode of the proposal being paid
- * @param amount total value of the contract to be paid to the merchant
- * @param max_fee maximum fee covered by the merchant (according to the 
contract)
- * @param merchant_pub the public key of the merchant (used to identify the 
merchant for refund requests)
- * @param merchant_sig signature from the merchant over the original contract
- * @param timestamp timestamp when the contract was finalized, must match 
approximately the current time of the merchant
- * @param refund_deadline date until which the merchant can issue a refund to 
the customer via the merchant (can be zero if refunds are not allowed)
+ * @param amount total value of the contract to be paid to the
+ *        merchant
+ * @param max_fee maximum fee covered by the merchant
+ *        (according to the contract)
+ * @param merchant_pub the public key of the merchant
+ *        (used to identify the merchant for refund requests)
+ * @param merchant_sig signature from the merchant over the
+ *        original contract
+ * @param timestamp timestamp when the contract was finalized,
+ *        must match approximately the current time of the merchant
+ * @param refund_deadline date until which the merchant can issue
+ *        a refund to the customer via the merchant (can be zero
+ *        if refunds are not allowed)
  * @param pay_deadline maximum time limit to pay for this contract
  * @param h_wire hash of the merchant’s account details
  * @param order_id order id of the proposal being paid
  * @param num_coins number of coins used to pay
  * @param coins array of coins we use to pay
- * @param pay_cb the callback to call when a reply for this request is 
available
+ * @param pay_cb the callback to call when a reply for this
+ *        request is available
  * @param pay_cb_cls closure for @a pay_cb
  * @return a handle for this request
  */
@@ -920,7 +956,7 @@ TALER_MERCHANT_pay_cancel (struct TALER_MERCHANT_Pay *pay)
   }
   GNUNET_free (pay->coins);
   GNUNET_free (pay->url);
-  GNUNET_free (pay->json_enc);
+  GNUNET_free (pay->post_ctx.json_enc);
   GNUNET_free (pay);
 }
 
diff --git a/src/lib/merchant_api_pay_abort.c b/src/lib/merchant_api_pay_abort.c
deleted file mode 100644
index eb7d219..0000000
--- a/src/lib/merchant_api_pay_abort.c
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 GNUnet e.V. and INRIA
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU Lesser General Public License as published by the Free 
Software
-  Foundation; either version 2.1, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
details.
-
-  You should have received a copy of the GNU Lesser General Public License 
along with
-  TALER; see the file COPYING.LGPL.  If not, see
-  <http://www.gnu.org/licenses/>
-*/
-/**
- * @file lib/merchant_api_pay.c
- * @brief Implementation of the /pay request of the merchant's HTTP API
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler_merchant_service.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include <taler/taler_exchange_service.h>
-#include "merchant_api_common.h"
-
-
-/**
- * @brief A Pay Handle
- */
-struct TALER_MERCHANT_Pay
-{
-
-  /**
-   * The url for this request.
-   */
-  char *url;
-
-  /**
-   * JSON encoding of the request to POST.
-   */
-  char *json_enc;
-
-  /**
-   * Handle for the request.
-   */
-  struct GNUNET_CURL_Job *job;
-
-  /**
-   * Function to call with the result.
-   */
-  TALER_MERCHANT_PayCallback cb;
-
-  /**
-   * Closure for @a cb.
-   */
-  void *cb_cls;
-
-  /**
-   * Reference to the execution context.
-   */
-  struct GNUNET_CURL_Context *ctx;
-
-  /**
-   * Number of @e coins we are paying with.
-   */
-  unsigned int num_coins;
-
-  /**
-   * The coins we are paying with.
-   */
-  struct TALER_MERCHANT_PaidCoin *coins;
-
-};
-
-
-/**
- * We got a 403 response back from the exchange (or the merchant).
- * Now we need to check the provided cryptographic proof that the
- * coin was actually already spent!
- *
- * @param pc handle of the original coin we paid with
- * @param json cryptographic proof of coin's transaction history as
- *        was returned by the exchange/merchant
- * @return #GNUNET_OK if proof checks out
- */
-static int
-check_coin_history (const struct TALER_MERCHANT_PaidCoin *pc,
-                    json_t *json)
-{
-  struct TALER_Amount spent;
-  struct TALER_Amount spent_plus_contrib;
-
-  if (GNUNET_OK !=
-      TALER_EXCHANGE_verify_coin_history (pc->amount_with_fee.currency,
-                                          &pc->coin_pub,
-                                          json,
-                                          &spent))
-  {
-    /* Exchange's history fails to verify */
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_OK !=
-      TALER_amount_add (&spent_plus_contrib,
-                        &spent,
-                        &pc->amount_with_fee))
-  {
-    /* We got an integer overflow? Bad application! */
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (-1 != TALER_amount_cmp (&pc->denom_value,
-                              &spent_plus_contrib))
-  {
-    /* according to our calculations, the transaction should
-       have still worked, exchange error! */
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Accepting proof of double-spending\n");
-  return GNUNET_OK;
-}
-
-
-/**
- * We got a 403 response back from the exchange (or the merchant).
- * Now we need to check the provided cryptographic proof that the
- * coin was actually already spent!
- *
- * @param ph handle of the original pay operation
- * @param json cryptographic proof returned by the exchange/merchant
- * @return #GNUNET_OK if proof checks out
- */
-static int
-check_forbidden (struct TALER_MERCHANT_Pay *ph,
-                 const json_t *json)
-{
-  json_t *history;
-  struct TALER_CoinSpendPublicKeyP coin_pub;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_json ("history", &history),
-    GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub),
-    GNUNET_JSON_spec_end()
-  };
-  int ret;
-
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (json,
-                         spec,
-                         NULL, NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  for (unsigned int i=0;i<ph->num_coins;i++)
-  {
-    if (0 == memcmp (&ph->coins[i].coin_pub,
-                     &coin_pub,
-                     sizeof (struct TALER_CoinSpendPublicKeyP)))
-    {
-      ret = check_coin_history (&ph->coins[i],
-                                history);
-      GNUNET_JSON_parse_free (spec);
-      return ret;
-    }
-  }
-  GNUNET_break_op (0); /* complaint is not about any of the coins
-                          that we actually paid with... */
-  GNUNET_JSON_parse_free (spec);
-  return GNUNET_SYSERR;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /pay request.
- *
- * @param cls the `struct TALER_MERCHANT_Pay`
- * @param response_code HTTP response code, 0 on error
- * @param json response body, NULL if not in JSON
- */
-static void
-handle_pay_finished (void *cls,
-                     long response_code,
-                     const json_t *json)
-{
-  struct TALER_MERCHANT_Pay *ph = cls;
-
-  ph->job = NULL;
-  switch (response_code)
-  {
-  case 0:
-    break;
-  case MHD_HTTP_OK:
-    break;
-  case MHD_HTTP_BAD_REQUEST:
-    /* This should never happen, either us or the merchant is buggy
-       (or API version conflict); just pass JSON reply to the application */
-    break;
-  case MHD_HTTP_FORBIDDEN:
-    if (GNUNET_OK != check_forbidden (ph,
-                                      json))
-    {
-      GNUNET_break_op (0);
-      response_code = 0;
-    }
-    break;
-  case MHD_HTTP_UNAUTHORIZED:
-    /* Nothing really to verify, merchant says one of the signatures is
-       invalid; as we checked them, this should never happen, we
-       should pass the JSON reply to the application */
-    break;
-  case MHD_HTTP_NOT_FOUND:
-    /* Nothing really to verify, this should never
-       happen, we should pass the JSON reply to the application */
-    break;
-  case MHD_HTTP_INTERNAL_SERVER_ERROR:
-    /* Server had an internal issue; we should retry, but this API
-       leaves this to the application */
-    break;
-  default:
-    /* unexpected response code */
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u\n",
-                (unsigned int) response_code);
-    GNUNET_break (0);
-    response_code = 0;
-    break;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "/pay completed with response code %u\n",
-              (unsigned int) response_code);
-  ph->cb (ph->cb_cls,
-          response_code,
-         TALER_JSON_get_error_code (json),
-          json);
-  TALER_MERCHANT_pay_cancel (ph);
-}
-
-
-/**
- * Pay a merchant.  API for wallets that have the coin's private keys.
- * _NOTE_: this function does NOT calculate each coin amount in order
- * to match the contract total price.  This calculation is to be made
- * by the logic using this library.
- *
- * @param ctx the execution loop context
- * @param merchant_url base URL of the merchant's backend
- * @param instance which merchant instance will receive this payment
- * @param h_contract_terms hashcode of the proposal being paid
- * @param amount total value of the contract to be paid to the merchant
- * @param max_fee maximum fee covered by the merchant (according to the 
contract)
- * @param merchant_pub the public key of the merchant (used to identify the 
merchant for refund requests)
- * @param merchant_sig signature from the merchant over the original contract
- * @param timestamp timestamp when the contract was finalized, must match 
approximately the current time of the merchant
- * @param refund_deadline date until which the merchant can issue a refund to 
the customer via the merchant (can be zero if refunds are not allowed)
- * @param pay_deadline maximum time limit to pay for this contract
- * @param h_wire hash of the merchant’s account details
- * @param order_id order id of the proposal being paid
- * @param num_coins number of coins used to pay
- * @param coins array of coins we use to pay
- * @param pay_cb the callback to call when a reply for this request is 
available
- * @param pay_cb_cls closure for @a pay_cb
- * @return a handle for this request
- */
-struct TALER_MERCHANT_Pay *
-TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx,
-                          const char *merchant_url,
-                          const char *instance,
-                           const struct GNUNET_HashCode *h_contract_terms,
-                          const struct TALER_Amount *amount,
-                          const struct TALER_Amount *max_fee,
-                           const struct TALER_MerchantPublicKeyP *merchant_pub,
-                           const struct TALER_MerchantSignatureP *merchant_sig,
-                           struct GNUNET_TIME_Absolute timestamp,
-                           struct GNUNET_TIME_Absolute refund_deadline,
-                           struct GNUNET_TIME_Absolute pay_deadline,
-                           const struct GNUNET_HashCode *h_wire,
-                           const char *order_id,
-                           unsigned int num_coins,
-                           const struct TALER_MERCHANT_PayCoin *coins,
-                           TALER_MERCHANT_PayCallback pay_cb,
-                           void *pay_cb_cls)
-{
-  struct TALER_DepositRequestPS dr;
-  struct TALER_MERCHANT_PaidCoin pc[num_coins];
-  struct TALER_MERCHANT_Pay *ph;
-  json_t *pay_obj;
-  json_t *j_coins;
-  CURL *eh;
-  struct TALER_Amount total_fee;
-  struct TALER_Amount total_amount;
-
-  (void) GNUNET_TIME_round_abs (&timestamp);
-  (void) GNUNET_TIME_round_abs (&pay_deadline);
-  (void) GNUNET_TIME_round_abs (&refund_deadline);
-
-  if (GNUNET_YES !=
-      TALER_amount_cmp_currency (amount,
-                                 max_fee))
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-
-  dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
-  dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
-  dr.h_contract_terms = *h_contract_terms;
-  dr.h_wire = *h_wire;
-  dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
-  dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
-  dr.merchant = *merchant_pub;
-  for (unsigned int i=0;i<num_coins;i++)
-  {
-    const struct TALER_MERCHANT_PayCoin *coin = &coins[i];
-    struct TALER_MERCHANT_PaidCoin *p = &pc[i];
-    struct TALER_Amount fee;
-
-    /* prepare 'dr' for this coin to generate coin signature */
-    GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv,
-                                       &dr.coin_pub.eddsa_pub);
-    TALER_amount_hton (&dr.amount_with_fee,
-                      &coin->amount_with_fee);
-    if (GNUNET_SYSERR ==
-       TALER_amount_subtract (&fee,
-                              &coin->amount_with_fee,
-                              &coin->amount_without_fee))
-    {
-      /* Integer underflow, fee larger than total amount?
-        This should not happen (client violated API!) */
-      GNUNET_break (0);
-      return NULL;
-    }
-    TALER_amount_hton (&dr.deposit_fee,
-                      &fee);
-    {
-      TALER_LOG_DEBUG ("... amount_with_fee was %s\n",
-                       TALER_amount2s (&coin->amount_with_fee));
-      TALER_LOG_DEBUG ("... fee was %s\n",
-                       TALER_amount2s (&fee));
-    }
-
-    GNUNET_CRYPTO_eddsa_sign (&coin->coin_priv.eddsa_priv,
-                             &dr.purpose,
-                             &p->coin_sig.eddsa_signature);
-    p->denom_pub = coin->denom_pub;
-    p->denom_sig = coin->denom_sig;
-    p->denom_value = coin->denom_value;
-    p->coin_pub = dr.coin_pub;
-    p->amount_with_fee = coin->amount_with_fee;
-    p->amount_without_fee = coin->amount_without_fee;
-    p->exchange_url = coin->exchange_url;
-  }
-
-  j_coins = json_array ();
-  for (unsigned int i=0;i<num_coins;i++)
-  {
-    json_t *j_coin;
-    const struct TALER_MERCHANT_PaidCoin *pc = &coins[i];
-    struct TALER_Amount fee;
-
-    if (GNUNET_SYSERR ==
-       TALER_amount_subtract (&fee,
-                              &pc->amount_with_fee,
-                              &pc->amount_without_fee))
-    {
-      /* Integer underflow, fee larger than total amount?
-        This should not happen (client violated API!) */
-      GNUNET_break (0);
-      json_decref (j_coins);
-      return NULL;
-    }
-    if (0 == i)
-    {
-      total_fee = fee;
-      total_amount = pc->amount_with_fee;
-    }
-    else
-    {
-      if ( (GNUNET_OK !=
-           TALER_amount_add (&total_fee,
-                             &total_fee,
-                             &fee)) ||
-          (GNUNET_OK !=
-           TALER_amount_add (&total_amount,
-                             &total_amount,
-                             &pc->amount_with_fee)) )
-      {
-       /* integer overflow */
-       GNUNET_break (0);
-       json_decref (j_coins);
-       return NULL;
-      }
-    }
-
-    /* create JSON for this coin */
-    j_coin = json_pack ("{s:o, s:o," /* contribution/coin_pub */
-                       " s:s, s:o," /* exchange_url / denom_pub */
-                       " s:o, s:o}", /* ub_sig / coin_sig */
-                       "contribution", TALER_JSON_from_amount 
(&pc->amount_with_fee),
-                       "coin_pub", GNUNET_JSON_from_data_auto (&pc->coin_pub),
-                       "exchange_url", pc->exchange_url,
-                       "denom_pub", GNUNET_JSON_from_rsa_public_key 
(pc->denom_pub.rsa_public_key),
-                       "ub_sig", GNUNET_JSON_from_rsa_signature 
(pc->denom_sig.rsa_signature),
-                       "coin_sig", GNUNET_JSON_from_data_auto (&pc->coin_sig)
-                       );
-    if (0 !=
-        json_array_append_new (j_coins,
-                               j_coin))
-    {
-      GNUNET_break (0);
-      json_decref (j_coins);
-      return NULL;
-    }
-  }
-
-  pay_obj = json_pack ("{"
-                       " s:o," /* coins */
-                       " s:s," /* order_id */
-                       " s:o," /* merchant_pub */
-                       "}",
-                      "coins", j_coins,
-                       "order_id", order_id,
-                       "merchant_pub", GNUNET_JSON_from_data_auto 
(merchant_pub));
-  if (NULL == pay_obj)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  ph = GNUNET_new (struct TALER_MERCHANT_Pay);
-  ph->ctx = ctx;
-  ph->cb = pay_cb;
-  ph->cb_cls = pay_cb_cls;
-  ph->url = TALER_url_join (merchant_url, "/pay", NULL);
-  ph->num_coins = num_coins;
-  ph->coins = GNUNET_new_array (num_coins,
-                                struct TALER_MERCHANT_PaidCoin);
-  memcpy (ph->coins,
-          coins,
-          num_coins * sizeof (struct TALER_MERCHANT_PaidCoin));
-
-  eh = curl_easy_init ();
-  GNUNET_assert (NULL != (ph->json_enc =
-                          json_dumps (pay_obj,
-                                      JSON_COMPACT)));
-  json_decref (pay_obj);
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_URL,
-                                   ph->url));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDS,
-                                   ph->json_enc));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDSIZE,
-                                   strlen (ph->json_enc)));
-  ph->job = GNUNET_CURL_job_add (ctx,
-                                 eh,
-                                 GNUNET_YES,
-                                 &handle_pay_finished,
-                                 ph);
-  return ph;
-}
-
-
-/**
- * Cancel a pay permission request.  This function cannot be used
- * on a request handle if a response is already served for it.
- *
- * @param pay the pay permission request handle
- */
-void
-TALER_MERCHANT_pay_cancel (struct TALER_MERCHANT_Pay *pay)
-{
-  if (NULL != pay->job)
-  {
-    GNUNET_CURL_job_cancel (pay->job);
-    pay->job = NULL;
-  }
-  GNUNET_free (pay->coins);
-  GNUNET_free (pay->url);
-  GNUNET_free (pay->json_enc);
-  GNUNET_free (pay);
-}
-
-
-/* end of merchant_api_pay.c */
diff --git a/src/lib/merchant_api_proposal.c b/src/lib/merchant_api_proposal.c
index 3d7218c..37fbc8c 100644
--- a/src/lib/merchant_api_proposal.c
+++ b/src/lib/merchant_api_proposal.c
@@ -2,18 +2,21 @@
   This file is part of TALER
   Copyright (C) 2014-2017 GNUnet e.V. and INRIA
 
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU Lesser General Public License as published by the Free 
Software
-  Foundation; either version 2.1, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
details.
-
-  You should have received a copy of the GNU Lesser General Public License 
along with
-  TALER; see the file COPYING.LGPL.  If not, see
-  <http://www.gnu.org/licenses/>
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1,
+  or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with TALER; see the file COPYING.LGPL.  If not,
+  see <http://www.gnu.org/licenses/>
 */
+
 /**
  * @file lib/merchant_api_proposal.c
  * @brief Implementation of the /proposal POST and GET
@@ -29,6 +32,7 @@
 #include "taler_merchant_service.h"
 #include <taler/taler_json_lib.h>
 #include <taler/taler_signatures.h>
+#include <taler/taler_curl_lib.h>
 
 
 /**
@@ -43,11 +47,6 @@ struct TALER_MERCHANT_ProposalOperation
   char *url;
 
   /**
-   * JSON encoding of the request to POST.
-   */
-  char *json_enc;
-
-  /**
    * Handle for the request.
    */
   struct GNUNET_CURL_Job *job;
@@ -66,6 +65,11 @@ struct TALER_MERCHANT_ProposalOperation
    * Reference to the execution context.
    */
   struct GNUNET_CURL_Context *ctx;
+
+  /**
+   * Minor context that holds body and headers.
+   */
+  struct TEAH_PostContext post_ctx;
 };
 
 /**
@@ -159,23 +163,25 @@ handle_proposal_finished (void *cls,
     }
     break;
   case MHD_HTTP_BAD_REQUEST:
-      /* This should never happen, either us or the merchant is buggy
-         (or API version conflict); just pass JSON reply to the application */
+      /* This should never happen, either us or
+         the merchant is buggy (or API version conflict);
+         just pass JSON reply to the application */
     break;
   case MHD_HTTP_FORBIDDEN:
     break;
   case MHD_HTTP_UNAUTHORIZED:
-    /* Nothing really to verify, merchant says one of the signatures is
-       invalid; as we checked them, this should never happen, we
-       should pass the JSON reply to the application */
+    /* Nothing really to verify, merchant says one
+       of the signatures is invalid; as we checked them,
+       this should never happen, we should pass the JSON
+       reply to the application */
     break;
   case MHD_HTTP_NOT_FOUND:
     /* Nothing really to verify, this should never
        happen, we should pass the JSON reply to the application */
     break;
   case MHD_HTTP_INTERNAL_SERVER_ERROR:
-    /* Server had an internal issue; we should retry, but this API
-       leaves this to the application */
+    /* Server had an internal issue; we should retry,
+       but this API leaves this to the application */
     break;
   default:
     /* unexpected response code */
@@ -200,19 +206,20 @@ handle_proposal_finished (void *cls,
  *
  * @param ctx execution context
  * @param backend_url URL of the backend
- * @param order basic information about this purchase, to be extended by the
- * backend
- * @param proposal_cb the callback to call when a reply for this request is
- * available
+ * @param order basic information about this purchase,
+ *        to be extended by the backend
+ * @param proposal_cb the callback to call when a reply
+ *        for this request is available
  * @param proposal_cb_cls closure for @a proposal_cb
  * @return a handle for this request, NULL on error
  */
 struct TALER_MERCHANT_ProposalOperation *
-TALER_MERCHANT_order_put (struct GNUNET_CURL_Context *ctx,
-                          const char *backend_url,
-                          const json_t *order,
-                          TALER_MERCHANT_ProposalCallback proposal_cb,
-                          void *proposal_cb_cls)
+TALER_MERCHANT_order_put
+  (struct GNUNET_CURL_Context *ctx,
+   const char *backend_url,
+   const json_t *order,
+   TALER_MERCHANT_ProposalCallback proposal_cb,
+   void *proposal_cb_cls)
 {
   struct TALER_MERCHANT_ProposalOperation *po;
   json_t *req;
@@ -226,32 +233,26 @@ TALER_MERCHANT_order_put (struct GNUNET_CURL_Context *ctx,
   req = json_pack ("{s:O}",
                    "order", (json_t *) order);
   eh = curl_easy_init ();
-  po->json_enc = json_dumps (req,
-                             JSON_COMPACT);
-  json_decref (req);
-  if (NULL == po->json_enc)
+  if (GNUNET_OK != TALER_curl_easy_post (&po->post_ctx,
+                                         eh,
+                                         req))
   {
     GNUNET_break (0);
     GNUNET_free (po);
     return NULL;
   }
+  json_decref (req);
+
   GNUNET_assert (CURLE_OK ==
                  curl_easy_setopt (eh,
                                    CURLOPT_URL,
                                    po->url));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDS,
-                                   po->json_enc));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDSIZE,
-                                   strlen (po->json_enc)));
-  po->job = GNUNET_CURL_job_add (ctx,
-                                 eh,
-                                 GNUNET_YES,
-                                 &handle_proposal_finished,
-                                 po);
+
+  po->job = GNUNET_CURL_job_add2 (ctx,
+                                  eh,
+                                  po->post_ctx.headers,
+                                  &handle_proposal_finished,
+                                  po);
   return po;
 }
 
@@ -404,6 +405,10 @@ TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context 
*ctx,
                      instance);
   }
   GNUNET_free (base);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "looking up proposal from %s\n",
+              plo->url);
+
   eh = curl_easy_init ();
   if (CURLE_OK != curl_easy_setopt (eh,
                                     CURLOPT_URL,
@@ -413,10 +418,6 @@ TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context 
*ctx,
     return NULL;
   }
 
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "looking up proposal from %s\n",
-              plo->url);
-
   if (NULL == (plo->job = GNUNET_CURL_job_add (ctx,
                                                eh,
                                                GNUNET_YES,
@@ -437,7 +438,8 @@ TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context 
*ctx,
  * @param po the proposal operation request handle
  */
 void
-TALER_MERCHANT_proposal_cancel (struct TALER_MERCHANT_ProposalOperation *po)
+TALER_MERCHANT_proposal_cancel
+  (struct TALER_MERCHANT_ProposalOperation *po)
 {
   if (NULL != po->job)
   {
@@ -445,7 +447,7 @@ TALER_MERCHANT_proposal_cancel (struct 
TALER_MERCHANT_ProposalOperation *po)
     po->job = NULL;
   }
   GNUNET_free (po->url);
-  GNUNET_free (po->json_enc);
+  GNUNET_free (po->post_ctx.json_enc);
   GNUNET_free (po);
 }
 
@@ -456,7 +458,8 @@ TALER_MERCHANT_proposal_cancel (struct 
TALER_MERCHANT_ProposalOperation *po)
  * @param plo handle to the request to be canceled
  */
 void
-TALER_MERCHANT_proposal_lookup_cancel (struct 
TALER_MERCHANT_ProposalLookupOperation *plo)
+TALER_MERCHANT_proposal_lookup_cancel
+  (struct TALER_MERCHANT_ProposalLookupOperation *plo)
 {
   if (NULL != plo->job)
   {
diff --git a/src/lib/merchant_api_refund.c b/src/lib/merchant_api_refund.c
index 993c815..ab95586 100644
--- a/src/lib/merchant_api_refund.c
+++ b/src/lib/merchant_api_refund.c
@@ -30,6 +30,7 @@
 #include "taler_merchant_service.h"
 #include <taler/taler_json_lib.h>
 #include <taler/taler_signatures.h>
+#include <taler/taler_curl_lib.h>
 
 
 struct TALER_MERCHANT_RefundLookupOperation
@@ -69,9 +70,9 @@ struct TALER_MERCHANT_RefundIncreaseOperation
   char *url;
 
   /**
-   * The request body
+   * Minor context that holds body and headers.
    */
-  char *json_enc;
+  struct TEAH_PostContext post_ctx;
 
   /**
    * The CURL context to connect to the backend
@@ -92,7 +93,6 @@ struct TALER_MERCHANT_RefundIncreaseOperation
    * Handle for the request
    */
   struct GNUNET_CURL_Job *job;
-
 };
 
 
@@ -176,7 +176,7 @@ TALER_MERCHANT_refund_increase_cancel (struct 
TALER_MERCHANT_RefundIncreaseOpera
     rio->job = NULL;
   }
   GNUNET_free (rio->url);
-  GNUNET_free (rio->json_enc);
+  GNUNET_free (rio->post_ctx.json_enc);
   GNUNET_free (rio);
 }
 
@@ -217,32 +217,28 @@ TALER_MERCHANT_refund_increase (struct 
GNUNET_CURL_Context *ctx,
                    "reason", reason,
                    "instance", instance);
   eh = curl_easy_init ();
-  rio->json_enc = json_dumps (req,
-                              JSON_COMPACT);
-  json_decref (req);
-  if (NULL == rio->json_enc)
+
+  if (GNUNET_OK != TALER_curl_easy_post (&rio->post_ctx,
+                                         eh,
+                                         req))
   {
     GNUNET_break (0);
     GNUNET_free (rio);
     return NULL;
   }
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_URL,
-                                   rio->url));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDS,
-                                   rio->json_enc));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDSIZE,
-                                   strlen (rio->json_enc)));
-  rio->job = GNUNET_CURL_job_add (ctx,
-                                  eh,
-                                  GNUNET_YES,
-                                  &handle_refund_increase_finished,
-                                  rio);
+
+  json_decref (req);
+
+  GNUNET_assert (CURLE_OK == curl_easy_setopt (eh,
+                                               CURLOPT_URL,
+                                               rio->url));
+  rio->job = GNUNET_CURL_job_add2
+    (ctx,
+     eh,
+     rio->post_ctx.headers,
+     &handle_refund_increase_finished,
+     rio);
+
   return rio;
 }
 
diff --git a/src/lib/merchant_api_tip_authorize.c 
b/src/lib/merchant_api_tip_authorize.c
index 268db3c..9286443 100644
--- a/src/lib/merchant_api_tip_authorize.c
+++ b/src/lib/merchant_api_tip_authorize.c
@@ -29,6 +29,7 @@
 #include "taler_merchant_service.h"
 #include <taler/taler_json_lib.h>
 #include <taler/taler_signatures.h>
+#include <taler/taler_curl_lib.h>
 
 
 /**
@@ -43,11 +44,6 @@ struct TALER_MERCHANT_TipAuthorizeOperation
   char *url;
 
   /**
-   * JSON encoding of the request to POST.
-   */
-  char *json_enc;
-
-  /**
    * Handle for the request.
    */
   struct GNUNET_CURL_Job *job;
@@ -66,6 +62,11 @@ struct TALER_MERCHANT_TipAuthorizeOperation
    * Reference to the execution context.
    */
   struct GNUNET_CURL_Context *ctx;
+
+  /**
+   * Minor context that holds body and headers.
+   */
+  struct TEAH_PostContext post_ctx;
 };
 
 
@@ -223,38 +224,30 @@ TALER_MERCHANT_tip_authorize (struct GNUNET_CURL_Context 
*ctx,
     GNUNET_free (tao);
     return NULL;
   }
-  if (NULL == (tao->json_enc =
-               json_dumps (te_obj,
-                           JSON_COMPACT)))
+
+  eh = curl_easy_init ();
+  if (GNUNET_OK != TALER_curl_easy_post (&tao->post_ctx,
+                                         eh,
+                                         te_obj))
   {
     GNUNET_break (0);
-    json_decref (te_obj);
-    GNUNET_free (tao->url);
     GNUNET_free (tao);
     return NULL;
   }
+
   json_decref (te_obj);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Requesting URL '%s'\n",
               tao->url);
-  eh = curl_easy_init ();
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_URL,
-                                   tao->url));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDS,
-                                   tao->json_enc));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDSIZE,
-                                   strlen (tao->json_enc)));
-  tao->job = GNUNET_CURL_job_add (ctx,
-                                  eh,
-                                  GNUNET_YES,
-                                  &handle_tip_authorize_finished,
-                                  tao);
+  GNUNET_assert (CURLE_OK == curl_easy_setopt (eh,
+                                               CURLOPT_URL,
+                                               tao->url));
+
+  tao->job = GNUNET_CURL_job_add2 (ctx,
+                                   eh,
+                                   tao->post_ctx.headers,
+                                   &handle_tip_authorize_finished,
+                                   tao);
   return tao;
 }
 
@@ -273,7 +266,7 @@ TALER_MERCHANT_tip_authorize_cancel (struct 
TALER_MERCHANT_TipAuthorizeOperation
     GNUNET_CURL_job_cancel (tao->job);
     tao->job = NULL;
   }
-  GNUNET_free_non_null (tao->json_enc);
+  GNUNET_free_non_null (tao->post_ctx.json_enc);
   GNUNET_free (tao->url);
   GNUNET_free (tao);
 }
diff --git a/src/lib/merchant_api_tip_pickup.c 
b/src/lib/merchant_api_tip_pickup.c
index 8785384..d3d0f66 100644
--- a/src/lib/merchant_api_tip_pickup.c
+++ b/src/lib/merchant_api_tip_pickup.c
@@ -29,6 +29,7 @@
 #include "taler_merchant_service.h"
 #include <taler/taler_json_lib.h>
 #include <taler/taler_signatures.h>
+#include <taler/taler_curl_lib.h>
 
 
 /**
@@ -43,9 +44,9 @@ struct TALER_MERCHANT_TipPickupOperation
   char *url;
 
   /**
-   * JSON encoding of the request to POST.
+   * Minor context that holds body and headers.
    */
-  char *json_enc;
+  struct TEAH_PostContext post_ctx;
 
   /**
    * Handle for the request.
@@ -282,39 +283,34 @@ TALER_MERCHANT_tip_pickup (struct GNUNET_CURL_Context 
*ctx,
   tpo->ctx = ctx;
   tpo->cb = pickup_cb;
   tpo->cb_cls = pickup_cb_cls;
-  tpo->url = TALER_url_join (backend_url, "/public/tip-pickup", NULL);
-  if (NULL == (tpo->json_enc =
-               json_dumps (tp_obj,
-                           JSON_COMPACT)))
+
+  tpo->url = TALER_url_join (backend_url,
+                             "/public/tip-pickup",
+                             NULL);
+  eh = curl_easy_init ();
+  if (GNUNET_OK != TALER_curl_easy_post (&tpo->post_ctx,
+                                         eh,
+                                         tp_obj))
   {
     GNUNET_break (0);
-    json_decref (tp_obj);
-    GNUNET_free (tpo->url);
     GNUNET_free (tpo);
     return NULL;
   }
+
   json_decref (tp_obj);
+
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Requesting URL '%s'\n",
               tpo->url);
-  eh = curl_easy_init ();
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_URL,
-                                   tpo->url));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDS,
-                                   tpo->json_enc));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDSIZE,
-                                   strlen (tpo->json_enc)));
-  tpo->job = GNUNET_CURL_job_add (ctx,
-                                  eh,
-                                  GNUNET_YES,
-                                  &handle_tip_pickup_finished,
-                                  tpo);
+
+  GNUNET_assert (CURLE_OK == curl_easy_setopt (eh,
+                                               CURLOPT_URL,
+                                               tpo->url));
+  tpo->job = GNUNET_CURL_job_add2 (ctx,
+                                   eh,
+                                   tpo->post_ctx.headers,
+                                   &handle_tip_pickup_finished,
+                                   tpo);
   return tpo;
 }
 
@@ -333,7 +329,7 @@ TALER_MERCHANT_tip_pickup_cancel (struct 
TALER_MERCHANT_TipPickupOperation *tpo)
     GNUNET_CURL_job_cancel (tpo->job);
     tpo->job = NULL;
   }
-  GNUNET_free_non_null (tpo->json_enc);
+  GNUNET_free_non_null (tpo->post_ctx.json_enc);
   GNUNET_free (tpo->url);
   GNUNET_free (tpo);
 }
diff --git a/src/lib/merchant_api_tip_query.c b/src/lib/merchant_api_tip_query.c
index 842fa68..d02de8b 100644
--- a/src/lib/merchant_api_tip_query.c
+++ b/src/lib/merchant_api_tip_query.c
@@ -207,14 +207,17 @@ TALER_MERCHANT_tip_query (struct GNUNET_CURL_Context *ctx,
   tqo->url = TALER_url_join (backend_url, "/tip-query",
                              "instance", instance,
                              NULL);
+
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Requesting URL '%s'\n",
               tqo->url);
+
   eh = curl_easy_init ();
   GNUNET_assert (CURLE_OK ==
                  curl_easy_setopt (eh,
                                    CURLOPT_URL,
                                    tqo->url));
+
   tqo->job = GNUNET_CURL_job_add (ctx,
                                   eh,
                                   GNUNET_YES,
diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c
index e373e6b..344112d 100644
--- a/src/lib/test_merchant_api.c
+++ b/src/lib/test_merchant_api.c
@@ -4995,6 +4995,7 @@ run (void *cls)
   GNUNET_assert (ctx = GNUNET_CURL_init
     (&GNUNET_CURL_gnunet_scheduler_reschedule,
      &rc));
+  GNUNET_CURL_enable_async_scope_header (ctx, "Taler-Correlation-Id");
   rc = GNUNET_CURL_gnunet_rc_create (ctx);
   GNUNET_assert (NULL != (exchange
     = TALER_EXCHANGE_connect (ctx,
diff --git a/src/lib/test_merchant_api_twisted.c 
b/src/lib/test_merchant_api_twisted.c
index 0335153..f5403bf 100644
--- a/src/lib/test_merchant_api_twisted.c
+++ b/src/lib/test_merchant_api_twisted.c
@@ -190,6 +190,80 @@ run (void *cls,
      struct TALER_TESTING_Interpreter *is)
 {
 
+  /**** Triggering #5719 ****/
+  struct TALER_TESTING_Command bug_5719[] = {
+
+    /**
+     * Move money to the exchange's bank account.
+     */
+    CMD_TRANSFER_TO_EXCHANGE ("5719-create-reserve",
+                              "EUR:1.01"),
+    /**
+     * Make a reserve exist, according to the previous
+     * transfer.
+     */
+    CMD_EXEC_WIREWATCH ("5719-wirewatch"),
+
+    TALER_TESTING_cmd_check_bank_transfer
+      ("5719-check-transfer",
+       EXCHANGE_URL,
+       "EUR:1.01",
+       USER_ACCOUNT_NO,
+       EXCHANGE_ACCOUNT_NO),
+
+    TALER_TESTING_cmd_withdraw_amount ("5719-withdraw",
+                                       "5719-create-reserve",
+                                       "EUR:1",
+                                       MHD_HTTP_OK),
+
+    TALER_TESTING_cmd_status ("5719-reserve-status",
+                              "5719-create-reserve",
+                              "EUR:0",
+                              MHD_HTTP_OK),
+    TALER_TESTING_cmd_proposal
+      ("5719-create-proposal",
+       twister_merchant_url,
+       MHD_HTTP_OK,
+       "{\"max_fee\":\
+          {\"currency\":\"EUR\",\
+           \"value\":0,\
+           \"fraction\":50000000},\
+        \"order_id\":\"5719TRIGGER\",\
+        \"refund_deadline\":\"\\/Date(0)\\/\",\
+        \"pay_deadline\":\"\\/Date(99999999999)\\/\",\
+        \"fulfillment_url\": \"https://example.com/\",\
+        \"amount\":\
+          {\"currency\":\"EUR\",\
+           \"value\":1,\
+           \"fraction\":0},\
+        \"summary\": \"merchant-lib testcase\",\
+        \"products\": [ {\"description\":\"triggering bug 5719\",\
+                         \"value\":\"{EUR:1}\"} ] }",
+        NULL),
+
+    /**
+     * Instruct the Twister to malform the response given by
+     * the exchange to the merchant.  This way, the parser will
+     * not manage to pass the callback a valid JSON and will
+     * instead pass a NULL pointer.  This should trigger the path
+     * mentioned in the bug report #5719.
+     */
+    TALER_TESTING_cmd_malform_response
+      ("5719-malform-xcg-resp",
+       PROXY_EXCHANGE_CONFIG_FILE),
+
+    TALER_TESTING_cmd_pay ("5719-deposit",
+                           twister_merchant_url,
+                           MHD_HTTP_SERVICE_UNAVAILABLE,
+                           "5719-create-proposal",
+                           "5719-withdraw",
+                           "EUR:1",
+                           "EUR:1.99", // no sense now
+                           "EUR:0.01"), // no sense now
+    TALER_TESTING_cmd_end ()
+  };
+  
+
   /**** Covering /check-payment ****/
   struct TALER_TESTING_Command check_payment[] = {
 
@@ -956,6 +1030,8 @@ run (void *cls,
     TALER_TESTING_cmd_batch ("pay",
                              pay),
 
+    TALER_TESTING_cmd_batch ("bug-5719",
+                             bug_5719),
     /**
      * End the suite.  Fixme: better to have a label for this
      * too, as it shows a "(null)" token on logs.

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



reply via email to

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