gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] branch master updated (cadda47 -> e8d2cde)


From: gnunet
Subject: [taler-merchant] branch master updated (cadda47 -> e8d2cde)
Date: Sat, 02 Nov 2019 12:13:04 +0100

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

grothoff pushed a change to branch master
in repository merchant.

    from cadda47  fix newlines
     new d19a45f  separate /order and /proposal handlers into different files
     new e8d2cde  clean up check payment logic

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/backend/Makefile.am                            |   1 +
 src/backend/taler-merchant-httpd.c                 |   7 +-
 src/backend/taler-merchant-httpd_check-payment.c   | 595 +++++++++++--------
 ...tpd_proposal.c => taler-merchant-httpd_order.c} | 196 +-----
 ...-transaction.h => taler-merchant-httpd_order.h} |  29 +-
 src/backend/taler-merchant-httpd_pay.c             |   5 -
 src/backend/taler-merchant-httpd_proposal.c        | 658 ---------------------
 src/backend/taler-merchant-httpd_proposal.h        |  22 -
 src/lib/merchant_api_check_payment.c               |  27 +-
 9 files changed, 371 insertions(+), 1169 deletions(-)
 copy src/backend/{taler-merchant-httpd_proposal.c => 
taler-merchant-httpd_order.c} (74%)
 copy src/backend/{taler-merchant-httpd_track-transaction.h => 
taler-merchant-httpd_order.h} (59%)

diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index cd221ca..ec4b204 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -19,6 +19,7 @@ taler_merchant_httpd_SOURCES = \
   taler-merchant-httpd_mhd.c taler-merchant-httpd_mhd.h \
   taler-merchant-httpd_auditors.c taler-merchant-httpd_auditors.h \
   taler-merchant-httpd_exchanges.c taler-merchant-httpd_exchanges.h \
+  taler-merchant-httpd_order.c taler-merchant-httpd_order.h \
   taler-merchant-httpd_proposal.c taler-merchant-httpd_proposal.h \
   taler-merchant-httpd_pay.c taler-merchant-httpd_pay.h \
   taler-merchant-httpd_history.c taler-merchant-httpd_history.h \
diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index ef7b650..e06e144 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -36,6 +36,7 @@
 #include "taler-merchant-httpd_mhd.h"
 #include "taler-merchant-httpd_auditors.h"
 #include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_order.h"
 #include "taler-merchant-httpd_proposal.h"
 #include "taler-merchant-httpd_pay.h"
 #include "taler-merchant-httpd_track-transaction.h"
@@ -441,8 +442,6 @@ TMH_trigger_daemon ()
  * @param daemon_handle HTTP server to prepare to run
  */
 static struct GNUNET_SCHEDULER_Task *
-
-
 prepare_daemon ()
 {
   struct GNUNET_SCHEDULER_Task *ret;
@@ -1034,8 +1033,6 @@ instances_iterator_cb (void *cls,
  * @return NULL if that instance is unknown to us
  */
 static struct MerchantInstance *
-
-
 lookup_instance (const char *instance_id)
 {
   struct GNUNET_HashCode h_instance;
@@ -1186,7 +1183,7 @@ url_handler (void *cls,
       &MH_handler_history, MHD_HTTP_OK},
     { "/order", MHD_HTTP_METHOD_POST, "application/json",
       NULL, 0,
-      &MH_handler_proposal_put, MHD_HTTP_OK },
+      &MH_handler_order_post, MHD_HTTP_OK },
     { "/refund", MHD_HTTP_METHOD_POST, "application/json",
       NULL, 0,
       &MH_handler_refund_increase, MHD_HTTP_OK},
diff --git a/src/backend/taler-merchant-httpd_check-payment.c 
b/src/backend/taler-merchant-httpd_check-payment.c
index 74b8c5c..d9284c2 100644
--- a/src/backend/taler-merchant-httpd_check-payment.c
+++ b/src/backend/taler-merchant-httpd_check-payment.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  (C) 2017 Taler Systems SA
+  (C) 2017, 2019 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
@@ -17,6 +17,7 @@
  * @file backend/taler-merchant-httpd_check-payment.c
  * @brief implementation of /check-payment handler
  * @author Florian Dold
+ * @author Christian Grothoff
  */
 #include "platform.h"
 #include <string.h>
@@ -37,21 +38,114 @@
 #define MAX_RETRIES 5
 
 
+/**
+ * Data structure we keep for a check payment request.
+ */
+struct CheckPaymentRequestContext
+{
+  /**
+   * Must be first for #handle_mhd_completion_callback.
+   */
+  struct TM_HandlerContext hc;
+
+  /**
+   * Connection we are processing a request for.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * URL where the final contract can be found for this payment.
+   */
+  char *final_contract_url;
+
+  /**
+   * order ID for the payment
+   */
+  const char *order_id;
+
+  /**
+   * Where to get the contract
+   */
+  const char *contract_url;
+
+  /**
+   * session of the client
+   */
+  const char *session_id;
+
+  /**
+   * fulfillment URL of the contract (valid as long as
+   * @e contract_terms is valid).
+   */
+  const char *fulfillment_url;
+
+  /**
+   * At what time does this request expire? If set in the future, we
+   * may wait this long for a payment to arrive before responding.
+   */
+  struct GNUNET_TIME_Absolute long_poll_timeout;
+
+  /**
+   * Contract terms of the payment we are checking. NULL when they
+   * are not (yet) known.
+   */
+  json_t *contract_terms;
+
+  /**
+   * Hash of @e contract_terms, set only once @e contract_terms
+   * is available.
+   */
+  struct GNUNET_HashCode h_contract_terms;
+
+  /**
+   * Total refunds granted for this payment. Only initialized
+   * if @e refunded is set to #GNUNET_YES.
+   */
+  struct TALER_Amount refund_amount;
+
+  /**
+   * Set to #GNUNET_YES if this payment has been refunded and
+   * @e refund_amount is initialized.
+   */
+  int refunded;
+
+  /**
+   * Initially #GNUNET_SYSERR. If we queued a response, set to the
+   * result code (i.e. #MHD_YES or #MHD_NO).
+   */
+  int ret;
+
+};
+
+
+/**
+ * Clean up the session state for a check payment request.
+ *
+ * @param hc must be a `struct CheckPaymentRequestContext *`
+ */
+static void
+cprc_cleanup (struct TM_HandlerContext *hc)
+{
+  struct CheckPaymentRequestContext *cprc = (struct
+                                             CheckPaymentRequestContext *) hc;
+
+  if (NULL != cprc->contract_terms)
+    json_decref (cprc->contract_terms);
+  GNUNET_free_non_null (cprc->final_contract_url);
+  GNUNET_free (cprc);
+}
+
+
 /**
  * Make a taler://pay URI
  *
- * @param connection MHD connection to take host and path from
  * @param instance_id merchant's instance ID
- * @param order_id order ID to request a payment for
- * @param session_id session ID for the payment or NULL
- *                   if not a session-bound payment
+ * @param cprc payment request context
  * @returns the URI, must be freed with #GNUNET_free
  */
 static char *
-make_taler_pay_uri (struct MHD_Connection *connection,
-                    const char *instance_id,
-                    const char *order_id,
-                    const char *session_id)
+make_taler_pay_uri (const char *instance_id,
+                    const struct CheckPaymentRequestContext *cprc)
 {
   const char *host;
   const char *forwarded_host;
@@ -60,23 +154,25 @@ make_taler_pay_uri (struct MHD_Connection *connection,
   const char *query;
   char *result;
 
-  host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "Host");
-  forwarded_host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
+  host = MHD_lookup_connection_value (cprc->connection,
+                                      MHD_HEADER_KIND,
+                                      "Host");
+  forwarded_host = MHD_lookup_connection_value (cprc->connection,
+                                                MHD_HEADER_KIND,
                                                 "X-Forwarded-Host");
 
-  uri_path = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
+  uri_path = MHD_lookup_connection_value (cprc->connection,
+                                          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"))
+  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 */
@@ -84,21 +180,20 @@ make_taler_pay_uri (struct MHD_Connection *connection,
     return NULL;
   }
 
-  if (GNUNET_YES == TALER_mhd_is_https (connection))
+  if (GNUNET_YES == TALER_mhd_is_https (cprc->connection))
     query = "";
   else
     query = "?insecure=1";
-
-  GNUNET_assert (NULL != order_id);
-
+  GNUNET_assert (NULL != cprc->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,
-                                      (session_id == NULL) ? "" : "/",
-                                      (session_id == NULL) ? "" : session_id,
+                                      cprc->order_id,
+                                      (cprc->session_id == NULL) ? "" : "/",
+                                      (cprc->session_id == NULL) ? "" :
+                                      cprc->session_id,
                                       query));
   return result;
 }
@@ -123,50 +218,48 @@ process_refunds_cb (void *cls,
                     const struct TALER_Amount *refund_amount,
                     const struct TALER_Amount *refund_fee)
 {
-  struct TALER_Amount *acc_amount = cls;
+  struct CheckPaymentRequestContext *cprc = cls;
 
-  GNUNET_assert (GNUNET_SYSERR !=
-                 TALER_amount_add (acc_amount,
-                                   acc_amount,
-                                   refund_amount));
+  if (cprc->refunded)
+  {
+    GNUNET_assert (GNUNET_SYSERR !=
+                   TALER_amount_add (&cprc->refund_amount,
+                                     &cprc->refund_amount,
+                                     refund_amount));
+    return;
+  }
+  cprc->refund_amount = *refund_amount;
+  cprc->refunded = GNUNET_YES;
 }
 
 
 /**
  * The client did not yet pay, send it the payment request.
  *
- * @param connection connection to send on
- * @param order_id order ID for the payment
- * @param final_contract_url where to get the contract
- * @param session_id session of the client
- * @param fulfillment_url fulfillment URL of the contract
- * @param h_contract_terms_str hash of the contract terms, stringified
+ * @param cprc check pay request context
  * @param mi merchant instance
  * @return #MHD_YES on success
  */
 static int
-send_pay_request (struct MHD_Connection *connection,
-                  const char *order_id,
-                  const char *final_contract_url,
-                  const char *session_id,
-                  const char *fulfillment_url,
-                  const char *h_contract_terms_str,
+send_pay_request (const struct CheckPaymentRequestContext *cprc,
                   const struct MerchantInstance *mi)
 {
   int ret;
-  int qs;
   char *already_paid_order_id = NULL;
   char *taler_pay_uri;
 
   /* Check if resource_id has been paid for in the same session
    * with another order_id.
    */
-  if ( (NULL != session_id) && (NULL != fulfillment_url) )
+  if ( (NULL != cprc->session_id) &&
+       (NULL != cprc->fulfillment_url) )
   {
+    enum GNUNET_DB_QueryStatus qs;
+
     qs = db->find_session_info (db->cls,
                                 &already_paid_order_id,
-                                session_id,
-                                fulfillment_url,
+                                cprc->session_id,
+                                cprc->fulfillment_url,
                                 &mi->pubkey);
     if (qs < 0)
     {
@@ -175,60 +268,98 @@ send_pay_request (struct MHD_Connection *connection,
       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
       /* Always report on hard error as well to enable diagnostics */
       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-      return TMH_RESPONSE_reply_internal_error (connection,
+      return TMH_RESPONSE_reply_internal_error (cprc->connection,
                                                 
TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR,
                                                 "db error fetching pay session 
info");
     }
   }
-
-  taler_pay_uri = make_taler_pay_uri (connection, mi->id, order_id, 
session_id);
-
-  ret = TMH_RESPONSE_reply_json_pack (connection,
+  taler_pay_uri = make_taler_pay_uri (mi->id,
+                                      cprc);
+  ret = TMH_RESPONSE_reply_json_pack (cprc->connection,
                                       MHD_HTTP_OK,
                                       "{s:s, s:s, s:b, s:s?}",
-                                      "taler_pay_uri",
-                                      taler_pay_uri,
-                                      "contract_url",
-                                      final_contract_url,
-                                      "paid",
-                                      0,
+                                      "taler_pay_uri", taler_pay_uri,
+                                      "contract_url", cprc->final_contract_url,
+                                      "paid", 0,
                                       "already_paid_order_id",
-                                      already_paid_order_id
-                                      );
-  GNUNET_free_non_null (already_paid_order_id);
+                                      already_paid_order_id);
   GNUNET_free (taler_pay_uri);
+  GNUNET_free_non_null (already_paid_order_id);
   return ret;
 }
 
 
+/**
+ * Parse the "contract_terms" in @a cprc and set the
+ * "fulfillment_url" and the "h_contract_terms" in @a cprc
+ * accordingly.
+ *
+ * On errors, the response is being queued and the status
+ * code set in @cprc "ret".
+ *
+ * @param cprc[in,out] context to process
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ */
+static int
+parse_contract_terms (struct CheckPaymentRequestContext *cprc)
+{
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_string ("fulfillment_url",
+                             &cprc->fulfillment_url),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (cprc->contract_terms,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break (0);
+    cprc->ret
+      = TMH_RESPONSE_reply_internal_error (cprc->connection,
+                                           
TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
+                                           "Merchant database error (contract 
terms corrupted)");
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_OK !=
+      TALER_JSON_hash (cprc->contract_terms,
+                       &cprc->h_contract_terms))
+  {
+    GNUNET_break (0);
+    cprc->ret
+      = TMH_RESPONSE_reply_internal_error (cprc->connection,
+                                           
TALER_EC_CHECK_PAYMENT_FAILED_COMPUTE_PROPOSAL_HASH,
+                                           "Failed to hash proposal");
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
 /**
  * Check that we are aware of @a order_id and if so request the payment,
  * otherwise generate an error response.
  *
- * @param connection where to send the response
  * @param mi the merchant's instance
- * @param final_contract_url where to redirect for the contract
- * @param session_id the session_id
- * @param order_id the order to look up
- * @return #MHD_YES on success
+ * @param cprc session state
+ * @return status code to return to MHD for @a connection
  */
 static int
-check_order_and_request_payment (struct MHD_Connection *connection,
-                                 struct MerchantInstance *mi,
-                                 const char *final_contract_url,
-                                 const char *session_id,
-                                 const char *order_id)
+check_order_and_request_payment (struct MerchantInstance *mi,
+                                 struct CheckPaymentRequestContext *cprc)
 {
   enum GNUNET_DB_QueryStatus qs;
-  json_t *contract_terms;
-  struct GNUNET_HashCode h_contract_terms;
-  char *h_contract_terms_str;
-  int ret;
-  const char *fulfillment_url;
 
+  if (NULL != cprc->contract_terms)
+  {
+    /* This should never happen. */
+    GNUNET_break (0);
+    json_decref (cprc->contract_terms);
+    cprc->contract_terms = NULL;
+  }
   qs = db->find_order (db->cls,
-                       &contract_terms,
-                       order_id,
+                       &cprc->contract_terms,
+                       cprc->order_id,
                        &mi->pubkey);
   if (0 > qs)
   {
@@ -237,55 +368,23 @@ check_order_and_request_payment (struct MHD_Connection 
*connection,
     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
     /* Always report on hard error as well to enable diagnostics */
     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-    return TMH_RESPONSE_reply_internal_error (connection,
+    return TMH_RESPONSE_reply_internal_error (cprc->connection,
                                               
TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR,
                                               "db error fetching order");
   }
   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
-    return TMH_RESPONSE_reply_not_found (connection,
+    return TMH_RESPONSE_reply_not_found (cprc->connection,
                                          
TALER_EC_CHECK_PAYMENT_ORDER_ID_UNKNOWN,
                                          "unknown order_id");
   }
-  {
-    struct GNUNET_JSON_Specification spec[] = {
-      GNUNET_JSON_spec_string ("fulfillment_url", &fulfillment_url),
-      GNUNET_JSON_spec_end ()
-    };
 
-    if (GNUNET_OK != GNUNET_JSON_parse (contract_terms, spec, NULL, NULL))
-    {
-      GNUNET_break (0);
-      json_decref (contract_terms);
-      return TMH_RESPONSE_reply_internal_error (connection,
-                                                
TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
-                                                "Merchant database error 
(contract terms corrupted)");
-    }
-  }
   if (GNUNET_OK !=
-      TALER_JSON_hash (contract_terms,
-                       &h_contract_terms))
-  {
-    GNUNET_break (0);
-    json_decref (contract_terms);
-    return TMH_RESPONSE_reply_internal_error (connection,
-                                              
TALER_EC_CHECK_PAYMENT_FAILED_COMPUTE_PROPOSAL_HASH,
-                                              "Failed to hash proposal");
-  }
+      parse_contract_terms (cprc))
+    return cprc->ret;
   /* Offer was not picked up yet, but we ensured that it exists */
-  h_contract_terms_str = GNUNET_STRINGS_data_to_string_alloc 
(&h_contract_terms,
-                                                              sizeof (struct
-                                                                      
GNUNET_HashCode));
-  ret = send_pay_request (connection,
-                          order_id,
-                          final_contract_url,
-                          session_id,
-                          fulfillment_url,
-                          h_contract_terms_str,
-                          mi);
-  GNUNET_free_non_null (h_contract_terms_str);
-  json_decref (contract_terms);
-  return ret;
+  return send_pay_request (cprc,
+                           mi);
 }
 
 
@@ -310,131 +409,119 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh,
                           size_t *upload_data_size,
                           struct MerchantInstance *mi)
 {
-  const char *order_id;
-  const char *contract_url;
-  const char *session_id;
-  const char *fulfillment_url;
-  char *final_contract_url;
-  char *h_contract_terms_str;
+  struct CheckPaymentRequestContext *cprc = *connection_cls;
   enum GNUNET_DB_QueryStatus qs;
-  json_t *contract_terms;
-  struct GNUNET_HashCode h_contract_terms;
-  struct TALER_Amount refund_amount;
   int ret;
-  int refunded;
 
-  order_id = MHD_lookup_connection_value (connection,
-                                          MHD_GET_ARGUMENT_KIND,
-                                          "order_id");
-  if (NULL == order_id)
+  if (NULL == cprc)
   {
-    /* order_id is required but missing */
-    GNUNET_break_op (0);
-    return TMH_RESPONSE_reply_bad_request (connection,
-                                           TALER_EC_PARAMETER_MISSING,
-                                           "order_id required");
-  }
-  contract_url = MHD_lookup_connection_value (connection,
-                                              MHD_GET_ARGUMENT_KIND,
-                                              "contract_url");
-  if (NULL == contract_url)
-  {
-    final_contract_url = TALER_url_absolute_mhd (connection,
-                                                 "/public/proposal",
-                                                 "instance", mi->id,
-                                                 "order_id", order_id,
-                                                 NULL);
-    GNUNET_assert (NULL != final_contract_url);
-  }
-  else
-  {
-    final_contract_url = GNUNET_strdup (contract_url);
-  }
-  session_id = MHD_lookup_connection_value (connection,
-                                            MHD_GET_ARGUMENT_KIND,
-                                            "session_id");
-
-  db->preflight (db->cls);
-  qs = db->find_contract_terms (db->cls,
-                                &contract_terms,
-                                order_id,
-                                &mi->pubkey);
-  if (0 > qs)
-  {
-    /* single, read-only SQL statements should never cause
-       serialization problems */
-    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
-    /* Always report on hard error as well to enable diagnostics */
-    GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-    GNUNET_free (final_contract_url);
-    return TMH_RESPONSE_reply_internal_error (connection,
-                                              
TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
-                                              "db error fetching contract 
terms");
-  }
-
-  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-  {
-    /* Check that we're at least aware of the order */
-    ret = check_order_and_request_payment (connection,
-                                           mi,
-                                           final_contract_url,
-                                           session_id,
-                                           order_id);
-    GNUNET_free (final_contract_url);
-    return ret;
-  }
-
-  GNUNET_assert (NULL != contract_terms);
-
-  /* Get the amount and fulfillment_url from the contract. */
-  {
-    struct TALER_Amount amount;
-    struct GNUNET_JSON_Specification spec[] = {
-      TALER_JSON_spec_amount ("amount", &amount),
-      GNUNET_JSON_spec_string ("fulfillment_url", &fulfillment_url),
-      GNUNET_JSON_spec_end ()
-    };
-
-    if (GNUNET_OK != GNUNET_JSON_parse (contract_terms, spec, NULL, NULL))
+    /* First time here, parse request and check order is known */
+    const char *long_poll_timeout_s;
+
+    cprc = GNUNET_new (struct CheckPaymentRequestContext);
+    cprc->hc.cc = &cprc_cleanup;
+    cprc->ret = GNUNET_SYSERR;
+    cprc->connection = connection;
+    *connection_cls = cprc;
+
+    cprc->order_id = MHD_lookup_connection_value (connection,
+                                                  MHD_GET_ARGUMENT_KIND,
+                                                  "order_id");
+    if (NULL == cprc->order_id)
+    {
+      /* order_id is required but missing */
+      GNUNET_break_op (0);
+      return TMH_RESPONSE_reply_bad_request (connection,
+                                             TALER_EC_PARAMETER_MISSING,
+                                             "order_id required");
+    }
+    long_poll_timeout_s = MHD_lookup_connection_value (connection,
+                                                       MHD_GET_ARGUMENT_KIND,
+                                                       "timeout");
+    if (NULL != long_poll_timeout_s)
+    {
+      unsigned int timeout;
+
+      if (1 != sscanf (long_poll_timeout_s,
+                       "%u",
+                       &timeout))
+      {
+        GNUNET_break_op (0);
+        return TMH_RESPONSE_reply_bad_request (connection,
+                                               TALER_EC_PARAMETER_MALFORMED,
+                                               "timeout must be non-negative 
number");
+      }
+      cprc->long_poll_timeout
+        = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (
+                                              GNUNET_TIME_UNIT_SECONDS,
+                                              timeout));
+    }
+    else
     {
-      GNUNET_break (0);
-      GNUNET_free (final_contract_url);
-      json_decref (contract_terms);
+      cprc->long_poll_timeout = GNUNET_TIME_UNIT_ZERO_ABS;
+    }
+    cprc->contract_url = MHD_lookup_connection_value (connection,
+                                                      MHD_GET_ARGUMENT_KIND,
+                                                      "contract_url");
+    if (NULL == cprc->contract_url)
+    {
+      cprc->final_contract_url = TALER_url_absolute_mhd (connection,
+                                                         "/public/proposal",
+                                                         "instance", mi->id,
+                                                         "order_id",
+                                                         cprc->order_id,
+                                                         NULL);
+      GNUNET_assert (NULL != cprc->final_contract_url);
+    }
+    else
+    {
+      cprc->final_contract_url = GNUNET_strdup (cprc->contract_url);
+    }
+    cprc->session_id = MHD_lookup_connection_value (connection,
+                                                    MHD_GET_ARGUMENT_KIND,
+                                                    "session_id");
+    db->preflight (db->cls);
+    qs = db->find_contract_terms (db->cls,
+                                  &cprc->contract_terms,
+                                  cprc->order_id,
+                                  &mi->pubkey);
+    if (0 > qs)
+    {
+      /* single, read-only SQL statements should never cause
+         serialization problems */
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+      /* Always report on hard error as well to enable diagnostics */
+      GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
       return TMH_RESPONSE_reply_internal_error (connection,
                                                 
TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
-                                                "Merchant database error 
(contract terms corrupted)");
+                                                "db error fetching contract 
terms");
     }
-    TALER_amount_get_zero (amount.currency, &refund_amount);
-  }
 
-  if (GNUNET_OK !=
-      TALER_JSON_hash (contract_terms,
-                       &h_contract_terms))
-  {
-    GNUNET_break (0);
-    json_decref (contract_terms);
-    GNUNET_free (final_contract_url);
-    return TMH_RESPONSE_reply_internal_error (connection,
-                                              
TALER_EC_CHECK_PAYMENT_FAILED_COMPUTE_PROPOSAL_HASH,
-                                              "Failed to hash proposal");
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+    {
+      /* Check that we're at least aware of the order */
+      return check_order_and_request_payment (mi,
+                                              cprc);
+    }
+
+    GNUNET_assert (NULL != cprc->contract_terms);
   }
 
-  h_contract_terms_str = GNUNET_STRINGS_data_to_string_alloc 
(&h_contract_terms,
-                                                              sizeof (struct
-                                                                      
GNUNET_HashCode));
 
+  if (GNUNET_OK !=
+      parse_contract_terms (cprc))
+    return cprc->ret;
 
   /* Check if the order has been paid for. */
-  if (NULL != session_id)
+  if (NULL != cprc->session_id)
   {
     /* Check if paid within a session. */
-
     char *already_paid_order_id = NULL;
 
     qs = db->find_session_info (db->cls,
                                 &already_paid_order_id,
-                                session_id,
-                                fulfillment_url,
+                                cprc->session_id,
+                                cprc->fulfillment_url,
                                 &mi->pubkey);
     if (qs < 0)
     {
@@ -449,56 +536,39 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh,
     }
     else if (0 == qs)
     {
-      ret = send_pay_request (connection,
-                              order_id,
-                              final_contract_url,
-                              session_id,
-                              fulfillment_url,
-                              h_contract_terms_str,
+      ret = send_pay_request (cprc,
                               mi);
       GNUNET_free_non_null (already_paid_order_id);
       return ret;
     }
     GNUNET_break (1 == qs);
-    GNUNET_break (0 == strcmp (order_id, already_paid_order_id));
+    GNUNET_break (0 == strcmp (cprc->order_id,
+                               already_paid_order_id));
     GNUNET_free_non_null (already_paid_order_id);
   }
   else
   {
     /* Check if paid regardless of session. */
-
     json_t *xcontract_terms = NULL;
 
     qs = db->find_paid_contract_terms_from_hash (db->cls,
                                                  &xcontract_terms,
-                                                 &h_contract_terms,
+                                                 &cprc->h_contract_terms,
                                                  &mi->pubkey);
     if (0 > qs)
     {
       /* Always report on hard error as well to enable diagnostics */
       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-      GNUNET_free_non_null (h_contract_terms_str);
-      GNUNET_free (final_contract_url);
-      json_decref (contract_terms);
       return TMH_RESPONSE_reply_internal_error (connection,
                                                 
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
                                                 "Merchant database error");
     }
     if (0 == qs)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "not paid yet\n");
-      ret = send_pay_request (connection,
-                              order_id,
-                              final_contract_url,
-                              session_id,
-                              fulfillment_url,
-                              h_contract_terms_str,
-                              mi);
-      GNUNET_free_non_null (h_contract_terms_str);
-      GNUNET_free (final_contract_url);
-      json_decref (contract_terms);
-      return ret;
-
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "not paid yet\n");
+      return send_pay_request (cprc,
+                               mi);
     }
     GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
     GNUNET_assert (NULL != xcontract_terms);
@@ -510,9 +580,9 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh,
   {
     qs = db->get_refunds_from_contract_terms_hash (db->cls,
                                                    &mi->pubkey,
-                                                   &h_contract_terms,
+                                                   &cprc->h_contract_terms,
                                                    &process_refunds_cb,
-                                                   &refund_amount);
+                                                   cprc);
     if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
       break;
   }
@@ -520,26 +590,25 @@ MH_handler_check_payment (struct TMH_RequestHandler *rh,
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Database hard error on refunds_from_contract_terms_hash 
lookup: %s\n",
-                GNUNET_h2s (&h_contract_terms));
-    GNUNET_free_non_null (h_contract_terms_str);
-    GNUNET_free (final_contract_url);
-    json_decref (contract_terms);
+                GNUNET_h2s (&cprc->h_contract_terms));
     return TMH_RESPONSE_reply_internal_error (connection,
                                               
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
                                               "Merchant database error");
   }
-  GNUNET_free_non_null (h_contract_terms_str);
-
-  refunded = (0 != refund_amount.value) || (0 != refund_amount.fraction);
-
-  ret = TMH_RESPONSE_reply_json_pack (connection,
-                                      MHD_HTTP_OK,
-                                      "{s:o, s:b, s:b, s:o}",
-                                      "contract_terms", contract_terms,
-                                      "paid", 1,
-                                      "refunded", refunded,
-                                      "refund_amount", TALER_JSON_from_amount (
-                                        &refund_amount));
-  GNUNET_free (final_contract_url);
-  return ret;
+  if (cprc->refunded)
+    return TMH_RESPONSE_reply_json_pack (connection,
+                                         MHD_HTTP_OK,
+                                         "{s:o, s:b, s:b, s:o}",
+                                         "contract_terms", 
cprc->contract_terms,
+                                         "paid", 1,
+                                         "refunded", cprc->refunded,
+                                         "refund_amount",
+                                         TALER_JSON_from_amount (
+                                           &cprc->refund_amount));
+  return TMH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_OK,
+                                       "{s:o, s:b, s:b }",
+                                       "contract_terms", cprc->contract_terms,
+                                       "paid", 1,
+                                       "refunded", 0);
 }
diff --git a/src/backend/taler-merchant-httpd_proposal.c 
b/src/backend/taler-merchant-httpd_order.c
similarity index 74%
copy from src/backend/taler-merchant-httpd_proposal.c
copy to src/backend/taler-merchant-httpd_order.c
index 6afd275..250a2e5 100644
--- a/src/backend/taler-merchant-httpd_proposal.c
+++ b/src/backend/taler-merchant-httpd_order.c
@@ -18,7 +18,7 @@
 */
 
 /**
- * @file backend/taler-merchant-httpd_proposal.c
+ * @file backend/taler-merchant-httpd_order.c
  * @brief HTTP serving layer mainly intended to communicate
  * with the frontend
  * @author Marcello Stanisci
@@ -644,12 +644,12 @@ proposal_put (struct MHD_Connection *connection,
  * @return MHD result code
  */
 int
-MH_handler_proposal_put (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_order_post (struct TMH_RequestHandler *rh,
+                       struct MHD_Connection *connection,
+                       void **connection_cls,
+                       const char *upload_data,
+                       size_t *upload_data_size,
+                       struct MerchantInstance *mi)
 {
   int res;
   struct TMH_JsonParseContext *ctx;
@@ -697,184 +697,4 @@ MH_handler_proposal_put (struct TMH_RequestHandler *rh,
 }
 
 
-/**
- * Manage a GET /proposal request. Query the db and returns the
- * proposal's data related to the transaction id given as the URL's
- * parameter.
- *
- * Binds the proposal to a nonce.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @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
- * @return MHD result code
- */
-int
-MH_handler_proposal_lookup (struct TMH_RequestHandler *rh,
-                            struct MHD_Connection *connection,
-                            void **connection_cls,
-                            const char *upload_data,
-                            size_t *upload_data_size,
-                            struct MerchantInstance *mi)
-{
-  const char *order_id;
-  const char *nonce;
-  int res;
-  enum GNUNET_DB_QueryStatus qs;
-  json_t *contract_terms;
-  struct GNUNET_CRYPTO_EddsaSignature merchant_sig;
-  const char *stored_nonce;
-
-  order_id = MHD_lookup_connection_value (connection,
-                                          MHD_GET_ARGUMENT_KIND,
-                                          "order_id");
-  if (NULL == order_id)
-    return TMH_RESPONSE_reply_arg_missing (connection,
-                                           TALER_EC_PARAMETER_MISSING,
-                                           "order_id");
-  nonce = MHD_lookup_connection_value (connection,
-                                       MHD_GET_ARGUMENT_KIND,
-                                       "nonce");
-  if (NULL == nonce)
-    return TMH_RESPONSE_reply_arg_missing (connection,
-                                           TALER_EC_PARAMETER_MISSING,
-                                           "nonce");
-  db->preflight (db->cls);
-  qs = db->find_contract_terms (db->cls,
-                                &contract_terms,
-                                order_id,
-                                &mi->pubkey);
-  if (0 > qs)
-  {
-    /* single, read-only SQL statements should never cause
-       serialization problems */
-    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
-    /* Always report on hard error as well to enable diagnostics */
-    GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-    return TMH_RESPONSE_reply_internal_error (connection,
-                                              
TALER_EC_PROPOSAL_LOOKUP_DB_ERROR,
-                                              "An error occurred while 
retrieving proposal data from db");
-  }
-  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-  {
-    struct GNUNET_TIME_Absolute timestamp;
-    struct GNUNET_JSON_Specification spec[] = {
-      GNUNET_JSON_spec_absolute_time ("timestamp", &timestamp),
-      GNUNET_JSON_spec_end ()
-    };
-
-    db->preflight (db->cls);
-    qs = db->find_order (db->cls,
-                         &contract_terms,
-                         order_id,
-                         &mi->pubkey);
-    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-    {
-      return TMH_RESPONSE_reply_not_found (connection,
-                                           TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND,
-                                           "unknown order id");
-    }
-    GNUNET_assert (NULL != contract_terms);
-    json_object_set_new (contract_terms,
-                         "nonce",
-                         json_string (nonce));
-
-    /* extract fields we need to sign separately */
-    res = TMH_PARSE_json_data (connection,
-                               contract_terms,
-                               spec);
-    if (GNUNET_NO == res)
-    {
-      return MHD_YES;
-    }
-    if (GNUNET_SYSERR == res)
-    {
-      return TMH_RESPONSE_reply_internal_error (connection,
-                                                
TALER_EC_PROPOSAL_ORDER_PARSE_ERROR,
-                                                "Impossible to parse the 
order");
-    }
-
-    for (unsigned int i = 0; i<MAX_RETRIES; i++)
-    {
-      db->preflight (db->cls);
-      qs = db->insert_contract_terms (db->cls,
-                                      order_id,
-                                      &mi->pubkey,
-                                      timestamp,
-                                      contract_terms);
-      if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
-        break;
-    }
-    if (0 > qs)
-    {
-      /* Special report if retries insufficient */
-      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
-      /* Always report on hard error as well to enable diagnostics */
-      GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-      return TMH_RESPONSE_reply_internal_error (connection,
-                                                
TALER_EC_PROPOSAL_STORE_DB_ERROR,
-                                                "db error: could not store 
this proposal's data into db");
-    }
-    // FIXME: now we can delete (merchant_pub, order_id) from the 
merchant_orders table
-  }
-
-  GNUNET_assert (NULL != contract_terms);
-
-  stored_nonce
-    = json_string_value (json_object_get (contract_terms,
-                                          "nonce"));
-
-  if (NULL == stored_nonce)
-  {
-    GNUNET_break (0);
-    return TMH_RESPONSE_reply_internal_error (connection,
-                                              
TALER_EC_PROPOSAL_ORDER_PARSE_ERROR,
-                                              "existing proposal has no 
nonce");
-  }
-
-  if (0 != strcmp (stored_nonce,
-                   nonce))
-  {
-    return TMH_RESPONSE_reply_bad_request (connection,
-                                           TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND,
-                                           "mismatched nonce");
-  }
-
-
-  /* create proposal signature */
-  {
-    struct TALER_ProposalDataPS pdps = {
-      .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT),
-      .purpose.size = htonl (sizeof (pdps))
-    };
-
-    if (GNUNET_OK !=
-        TALER_JSON_hash (contract_terms,
-                         &pdps.hash))
-    {
-      GNUNET_break (0);
-      return TMH_RESPONSE_reply_internal_error (connection,
-                                                TALER_EC_INTERNAL_LOGIC_ERROR,
-                                                "Could not hash order");
-    }
-
-    GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv,
-                              &pdps.purpose,
-                              &merchant_sig);
-  }
-  res = TMH_RESPONSE_reply_json_pack (connection,
-                                      MHD_HTTP_OK,
-                                      "{ s:o, s:o }",
-                                      "contract_terms",
-                                      contract_terms,
-                                      "sig",
-                                      GNUNET_JSON_from_data_auto (
-                                        &merchant_sig));
-  return res;
-}
-
-
-/* end of taler-merchant-httpd_proposal.c */
+/* end of taler-merchant-httpd_order.c */
diff --git a/src/backend/taler-merchant-httpd_track-transaction.h 
b/src/backend/taler-merchant-httpd_order.h
similarity index 59%
copy from src/backend/taler-merchant-httpd_track-transaction.h
copy to src/backend/taler-merchant-httpd_order.h
index 91b8c47..fd09880 100644
--- a/src/backend/taler-merchant-httpd_track-transaction.h
+++ b/src/backend/taler-merchant-httpd_order.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  (C) 2014, 2015 GNUnet e.V.
+  (C) 2014, 2015, 2019 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,20 +14,21 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file backend/taler-merchant-httpd_track-transaction.h
- * @brief headers for /track/transaction handler
- * @author Christian Grothoff
+ * @file backend/taler-merchant-httpd_order.h
+ * @brief headers for /order handler
  * @author Marcello Stanisci
  */
-#ifndef TALER_MERCHANT_HTTPD_TRACK_TRANSACTION_H
-#define TALER_MERCHANT_HTTPD_TRACK_TRANSACTION_H
+#ifndef TALER_MERCHANT_HTTPD_ORDER_H
+#define TALER_MERCHANT_HTTPD_ORDER_H
 #include <microhttpd.h>
 #include "taler-merchant-httpd.h"
 
 /**
- * Handle a "/track/transaction" request.
+ * Generate a proposal, given its order. In practical terms, it adds the
+ * fields  'exchanges', 'merchant_pub', and 'H_wire' to the order gotten
+ * from the frontend. Finally, it signs this data, and returns it to the
+ * frontend.
  *
- * @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
@@ -36,12 +37,12 @@
  * @return MHD result code
  */
 int
-MH_handler_track_transaction (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_order_post (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/backend/taler-merchant-httpd_pay.c 
b/src/backend/taler-merchant-httpd_pay.c
index 1bd68d3..e9c6e69 100644
--- a/src/backend/taler-merchant-httpd_pay.c
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -523,8 +523,6 @@ abort_deposit (struct PayContext *pc)
  * @return the mhd response
  */
 static struct MHD_Response *
-
-
 sign_success_response (struct PayContext *pc)
 {
   json_t *refunds;
@@ -661,8 +659,6 @@ pay_context_cleanup (struct TM_HandlerContext *hc)
  * @return taler error code, #TALER_EC_NONE if amount is sufficient
  */
 static enum TALER_ErrorCode
-
-
 check_payment_sufficient (struct PayContext *pc)
 {
   struct TALER_Amount acc_fee;
@@ -2202,7 +2198,6 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "/pay: picked instance %s\n",
                 mi->id);
-
   }
   else
   {
diff --git a/src/backend/taler-merchant-httpd_proposal.c 
b/src/backend/taler-merchant-httpd_proposal.c
index 6afd275..7afd0f7 100644
--- a/src/backend/taler-merchant-httpd_proposal.c
+++ b/src/backend/taler-merchant-httpd_proposal.c
@@ -39,664 +39,6 @@
  */
 #define MAX_RETRIES 3
 
-/**
- * What is the label under which we find/place the merchant's
- * jurisdiction in the locations list by default?
- */
-#define STANDARD_LABEL_MERCHANT_JURISDICTION "_mj"
-
-/**
- * What is the label under which we find/place the merchant's
- * address in the locations list by default?
- */
-#define STANDARD_LABEL_MERCHANT_ADDRESS "_ma"
-
-
-/**
- * Check that the given JSON array of products is well-formed.
- *
- * @param products JSON array to check
- * @return #GNUNET_OK if all is fine
- */
-static int
-check_products (json_t *products)
-{
-  size_t index;
-  json_t *value;
-
-  if (! json_is_array (products))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  json_array_foreach (products, index, value) {
-    const char *description;
-    const char *error_name;
-    unsigned int error_line;
-    int res;
-    struct GNUNET_JSON_Specification spec[] = {
-      GNUNET_JSON_spec_string ("description", &description),
-      /* FIXME: there are other fields in the product specification
-         that are currently not labeled as optional. Maybe check
-         those as well, or make them truly optional. */
-      GNUNET_JSON_spec_end ()
-    };
-
-    /* extract fields we need to sign separately */
-    res = GNUNET_JSON_parse (value,
-                             spec,
-                             &error_name,
-                             &error_line);
-    if (GNUNET_OK != res)
-    {
-      GNUNET_break (0);
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Product description parsing failed at #%u: %s:%u\n",
-                  (unsigned int) index,
-                  error_name,
-                  error_line);
-      return GNUNET_SYSERR;
-    }
-    GNUNET_JSON_parse_free (spec);
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Information we keep for individual calls
- * to requests that parse JSON, but keep no other state.
- */
-struct TMH_JsonParseContext
-{
-
-  /**
-   * This field MUST be first.
-   * FIXME: Explain why!
-   */
-  struct TM_HandlerContext hc;
-
-  /**
-   * Placeholder for #TMH_PARSE_post_json() to keep its internal state.
-   */
-  void *json_parse_context;
-};
-
-
-/**
- * Custom cleanup routine for a `struct TMH_JsonParseContext`.
- *
- * @param hc the `struct TMH_JsonParseContext` to clean up.
- */
-static void
-json_parse_cleanup (struct TM_HandlerContext *hc)
-{
-  struct TMH_JsonParseContext *jpc = (struct TMH_JsonParseContext *) hc;
-
-  TMH_PARSE_post_cleanup_callback (jpc->json_parse_context);
-  GNUNET_free (jpc);
-}
-
-
-/**
- * Generate the base URL for the given merchant instance.
- *
- * @param connection the MHD connection
- * @param instance_id the merchant instance ID
- * @returns the merchant instance's base URL
- */
-static char *
-make_merchant_base_url (struct MHD_Connection *connection, const
-                        char *instance_id)
-{
-  const char *host;
-  const char *forwarded_host;
-  const char *uri_path;
-  struct TALER_Buffer buf = { 0 };
-
-  if (GNUNET_YES == TALER_mhd_is_https (connection))
-    TALER_buffer_write_str (&buf, "https://";);
-  else
-    TALER_buffer_write_str (&buf, "http://";);
-
-
-  host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "Host");
-  forwarded_host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
-                                                "X-Forwarded-Host");
-
-  if (NULL != forwarded_host)
-  {
-    TALER_buffer_write_str (&buf, forwarded_host);
-  }
-  else
-  {
-    GNUNET_assert (NULL != host);
-    TALER_buffer_write_str (&buf, host);
-  }
-
-  uri_path = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
-                                          "X-Forwarded-Prefix");
-  if (NULL != uri_path)
-  {
-    /* Currently the merchant backend is only supported at the root of the 
path,
-       this might change in the future.  */
-    GNUNET_assert (0);
-  }
-
-  TALER_buffer_write_path (&buf, "public");
-
-  if (0 != strcmp (instance_id, "default"))
-  {
-    TALER_buffer_write_path (&buf, "/instances/");
-    TALER_buffer_write_str (&buf, instance_id);
-  }
-  TALER_buffer_write_path (&buf, "");
-
-  return TALER_buffer_reap_str (&buf);
-}
-
-
-/**
- * Transform an order into a proposal and store it in the
- * database. Write the resulting proposal or an error message
- * of a MHD connection.
- *
- * @param connection connection to write the result or error to
- * @param root root of the request
- * @param order[in] order to process (can be modified)
- * @return MHD result code
- */
-static int
-proposal_put (struct MHD_Connection *connection,
-              json_t *root,
-              json_t *order,
-              const struct MerchantInstance *mi)
-{
-  int res;
-  struct TALER_Amount total;
-  const char *order_id;
-  const char *summary;
-  const char *fulfillment_url;
-  json_t *products;
-  json_t *merchant;
-  struct GNUNET_TIME_Absolute timestamp;
-  struct GNUNET_TIME_Absolute refund_deadline;
-  struct GNUNET_TIME_Absolute wire_transfer_deadline;
-  struct GNUNET_TIME_Absolute pay_deadline;
-  struct GNUNET_JSON_Specification spec[] = {
-    TALER_JSON_spec_amount ("amount", &total),
-    GNUNET_JSON_spec_string ("order_id", &order_id),
-    GNUNET_JSON_spec_string ("summary", &summary),
-    GNUNET_JSON_spec_string ("fulfillment_url",
-                             &fulfillment_url),
-    /**
-     * The following entries we don't actually need,
-     * except to check that the order is well-formed */
-    GNUNET_JSON_spec_json ("products", &products),
-    GNUNET_JSON_spec_json ("merchant", &merchant),
-    GNUNET_JSON_spec_absolute_time ("timestamp",
-                                    &timestamp),
-    GNUNET_JSON_spec_absolute_time ("refund_deadline",
-                                    &refund_deadline),
-    GNUNET_JSON_spec_absolute_time ("pay_deadline",
-                                    &pay_deadline),
-    GNUNET_JSON_spec_absolute_time ("wire_transfer_deadline",
-                                    &wire_transfer_deadline),
-    GNUNET_JSON_spec_end ()
-  };
-  enum GNUNET_DB_QueryStatus qs;
-  struct WireMethod *wm;
-
-  /* Add order_id if it doesn't exist. */
-  if (NULL ==
-      json_string_value (json_object_get (order,
-                                          "order_id")))
-  {
-    char buf[256];
-    time_t timer;
-    struct tm*tm_info;
-    size_t off;
-    uint64_t rand;
-    char *last;
-
-    time (&timer);
-    tm_info = localtime (&timer);
-    if (NULL == tm_info)
-    {
-      return TMH_RESPONSE_reply_internal_error
-               (connection,
-               TALER_EC_PROPOSAL_NO_LOCALTIME,
-               "failed to determine local time");
-    }
-    off = strftime (buf,
-                    sizeof (buf),
-                    "%Y.%j",
-                    tm_info);
-    buf[off++] = '-';
-    rand = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
-                                     UINT64_MAX);
-    last = GNUNET_STRINGS_data_to_string (&rand,
-                                          sizeof (uint64_t),
-                                          &buf[off],
-                                          sizeof (buf) - off);
-    *last = '\0';
-    json_object_set_new (order,
-                         "order_id",
-                         json_string (buf));
-  }
-
-  /* Add timestamp if it doesn't exist */
-  if (NULL == json_object_get (order,
-                               "timestamp"))
-  {
-    struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
-
-    (void) GNUNET_TIME_round_abs (&now);
-    json_object_set_new (order,
-                         "timestamp",
-                         GNUNET_JSON_from_time_abs (now));
-  }
-
-  /* If no refund_deadline given, set one as zero.  */
-  if (NULL == json_object_get (order,
-                               "refund_deadline"))
-  {
-    struct GNUNET_TIME_Absolute zero = { 0 };
-
-    json_object_set_new (order,
-                         "refund_deadline",
-                         GNUNET_JSON_from_time_abs (zero));
-  }
-
-  if (NULL == json_object_get (order,
-                               "pay_deadline"))
-  {
-    struct GNUNET_TIME_Absolute t;
-
-    t = GNUNET_TIME_relative_to_absolute (default_pay_deadline);
-    (void) GNUNET_TIME_round_abs (&t);
-    json_object_set_new (order,
-                         "pay_deadline",
-                         GNUNET_JSON_from_time_abs (t));
-  }
-
-  if (NULL == json_object_get (order,
-                               "wire_transfer_deadline"))
-  {
-    struct GNUNET_TIME_Absolute t;
-
-    t = GNUNET_TIME_relative_to_absolute (default_wire_transfer_delay);
-    (void) GNUNET_TIME_round_abs (&t);
-    json_object_set_new (order,
-                         "wire_transfer_deadline",
-                         GNUNET_JSON_from_time_abs (t));
-  }
-
-  if (NULL == json_object_get (order,
-                               "max_wire_fee"))
-  {
-    json_object_set_new (order,
-                         "max_wire_fee",
-                         TALER_JSON_from_amount
-                           (&default_max_wire_fee));
-  }
-
-  if (NULL == json_object_get (order,
-                               "max_fee"))
-  {
-    json_object_set_new (order,
-                         "max_fee",
-                         TALER_JSON_from_amount
-                           (&default_max_deposit_fee));
-  }
-
-  if (NULL == json_object_get (order,
-                               "wire_fee_amortization"))
-  {
-    json_object_set_new
-      (order,
-      "wire_fee_amortization",
-      json_integer
-        ((json_int_t) default_wire_fee_amortization));
-  }
-
-  if (NULL == json_object_get (order,
-                               "merchant_base_url"))
-  {
-    char *url;
-
-    url = make_merchant_base_url (connection, mi->id);
-    json_object_set_new (order,
-                         "merchant_base_url",
-                         json_string (url));
-    GNUNET_free (url);
-  }
-
-  if (NULL == json_object_get (order,
-                               "products"))
-  {
-    json_object_set_new (order,
-                         "products",
-                         json_array ());
-  }
-
-  /* Fill in merchant information if necessary */
-  if (NULL == json_object_get (order, "merchant"))
-  {
-    const char *mj = NULL;
-    const char *ma = NULL;
-    json_t *locations;
-    char *label;
-    json_t *jmerchant;
-
-    jmerchant = json_object ();
-    json_object_set_new (jmerchant,
-                         "name",
-                         json_string (mi->name));
-    json_object_set_new (jmerchant,
-                         "instance",
-                         json_string (mi->id));
-    locations = json_object_get (order,
-                                 "locations");
-    if (NULL != locations)
-    {
-      json_t *loca;
-      json_t *locj;
-
-      /* Handle merchant address */
-      GNUNET_assert (0 < GNUNET_asprintf (&label,
-                                          "%s-address",
-                                          mi->id));
-      loca = json_object_get (default_locations,
-                              label);
-      if (NULL != loca)
-      {
-        loca = json_deep_copy (loca);
-        ma = STANDARD_LABEL_MERCHANT_ADDRESS;
-        json_object_set_new (locations,
-                             ma,
-                             loca);
-        json_object_set_new (jmerchant,
-                             "address",
-                             json_string (ma));
-      }
-      GNUNET_free (label);
-
-      /* Handle merchant jurisdiction */
-      GNUNET_assert (0 < GNUNET_asprintf (&label,
-                                          "%s-jurisdiction",
-                                          mi->id));
-      locj = json_object_get (default_locations,
-                              label);
-      if (NULL != locj)
-      {
-        if ( (NULL != loca) &&
-             (1 == json_equal (locj,
-                               loca)) )
-        {
-          /* addresses equal, re-use */
-          mj = ma;
-        }
-        else
-        {
-          locj = json_deep_copy (locj);
-          mj = STANDARD_LABEL_MERCHANT_JURISDICTION;
-          json_object_set_new (locations,
-                               mj,
-                               locj);
-        }
-        json_object_set_new (merchant,
-                             "jurisdiction",
-                             json_string (mj));
-      }
-      GNUNET_free (label);
-    } /* have locations */
-    json_object_set_new (order,
-                         "merchant",
-                         jmerchant);
-  } /* needed to synthesize merchant info */
-
-  /* extract fields we need to sign separately */
-  res = TMH_PARSE_json_data (connection,
-                             order,
-                             spec);
-  /* json is malformed */
-  if (GNUNET_NO == res)
-  {
-    return MHD_YES;
-  }
-
-  /* other internal errors might have occurred */
-  if (GNUNET_SYSERR == res)
-  {
-    return TMH_RESPONSE_reply_internal_error
-             (connection,
-             TALER_EC_PROPOSAL_ORDER_PARSE_ERROR,
-             "Impossible to parse the order");
-  }
-
-  if (wire_transfer_deadline.abs_value_us <
-      refund_deadline.abs_value_us)
-  {
-    GNUNET_JSON_parse_free (spec);
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "invariant failed: wire_transfer_deadline >= 
refund_deadline\n");
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "wire_transfer_deadline: %s\n",
-                GNUNET_STRINGS_absolute_time_to_string (
-                  wire_transfer_deadline));
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "refund_deadline: %s\n",
-                GNUNET_STRINGS_absolute_time_to_string (refund_deadline));
-    return TMH_RESPONSE_reply_arg_invalid
-             (connection,
-             TALER_EC_PARAMETER_MALFORMED,
-             "order:wire_transfer_deadline;order:refund_deadline");
-  }
-
-
-  /* check contract is well-formed */
-  if (GNUNET_OK != check_products (products))
-  {
-    GNUNET_JSON_parse_free (spec);
-    return TMH_RESPONSE_reply_arg_invalid
-             (connection,
-             TALER_EC_PARAMETER_MALFORMED,
-             "order:products");
-  }
-
-  /* add fields to the contract that the backend should provide */
-  json_object_set (order,
-                   "exchanges",
-                   trusted_exchanges);
-
-  json_object_set (order,
-                   "auditors",
-                   j_auditors);
-  /* TODO (#4939-12806): add proper mechanism for
-     selection of wire method(s) by merchant! */
-  wm = mi->wm_head;
-
-  if (NULL == wm)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "No wire method available for instance '%s'\n", mi->id);
-    GNUNET_JSON_parse_free (spec);
-    return TMH_RESPONSE_reply_not_found (connection,
-                                         
TALER_EC_PROPOSAL_INSTANCE_CONFIGURATION_LACKS_WIRE,
-                                         "No wire method configured for 
instance");
-  }
-  json_object_set_new (order,
-                       "H_wire",
-                       GNUNET_JSON_from_data_auto (&wm->h_wire));
-  json_object_set_new (order,
-                       "wire_method",
-                       json_string (wm->wire_method));
-  json_object_set_new (order,
-                       "merchant_pub",
-                       GNUNET_JSON_from_data_auto (&mi->pubkey));
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Inserting order '%s' for instance '%s'\n",
-              order_id,
-              mi->id);
-
-  for (unsigned int i = 0; i<MAX_RETRIES; i++)
-  {
-    db->preflight (db->cls);
-    qs = db->insert_order (db->cls,
-                           order_id,
-                           &mi->pubkey,
-                           timestamp,
-                           order);
-    if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
-      break;
-  }
-  if (0 > qs)
-  {
-    /* Special report if retries insufficient */
-    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-    {
-      GNUNET_break (0);
-      return TMH_RESPONSE_reply_internal_error
-               (connection,
-               TALER_EC_PROPOSAL_STORE_DB_ERROR_SOFT,
-               "db error: could not check for existing order"
-               " due to repeated soft transaction failure");
-    }
-
-    {
-      /* Hard error could be constraint violation,
-         check if order already exists */
-      json_t *contract_terms = NULL;
-
-      db->preflight (db->cls);
-      qs = db->find_order (db->cls,
-                           &contract_terms,
-                           order_id,
-                           &mi->pubkey);
-      if (0 < qs)
-      {
-        /* Yep, indeed uniqueness constraint violation */
-        int rv;
-        char *msg;
-
-        GNUNET_JSON_parse_free (spec);
-        GNUNET_asprintf (&msg,
-                         "order ID `%s' already exists",
-                         order_id);
-        {
-          /* Log plenty of details for the admin */
-          char *js;
-
-          js = json_dumps (contract_terms,
-                           JSON_COMPACT);
-          GNUNET_log
-            (GNUNET_ERROR_TYPE_ERROR,
-            _ ("Order ID `%s' already exists with proposal `%s'\n"),
-            order_id,
-            js);
-          free (js);
-        }
-        json_decref (contract_terms);
-
-        /* contract_terms may be private, only expose
-         * duplicate order_id to the network */
-        rv = TMH_RESPONSE_reply_external_error
-               (connection,
-               TALER_EC_PROPOSAL_STORE_DB_ERROR_ALREADY_EXISTS,
-               msg);
-        GNUNET_free (msg);
-        return rv;
-      }
-    }
-
-    /* Other hard transaction error (disk full, etc.) */
-    GNUNET_JSON_parse_free (spec);
-    return TMH_RESPONSE_reply_internal_error
-             (connection,
-             TALER_EC_PROPOSAL_STORE_DB_ERROR_HARD,
-             "db error: could not store this proposal's data into db");
-  }
-
-  /* DB transaction succeeded, generate positive response */
-  res = TMH_RESPONSE_reply_json_pack (connection,
-                                      MHD_HTTP_OK,
-                                      "{s:s}",
-                                      "order_id",
-                                      order_id);
-  GNUNET_JSON_parse_free (spec);
-  return res;
-}
-
-
-/**
- * Generate a proposal, given its order. In practical terms,
- * it adds the fields  'exchanges', 'merchant_pub', and 'H_wire'
- * to the order gotten from the frontend. Finally, it signs this
- * data, and returns it to the frontend.
- *
- * @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
- * @return MHD result code
- */
-int
-MH_handler_proposal_put (struct TMH_RequestHandler *rh,
-                         struct MHD_Connection *connection,
-                         void **connection_cls,
-                         const char *upload_data,
-                         size_t *upload_data_size,
-                         struct MerchantInstance *mi)
-{
-  int res;
-  struct TMH_JsonParseContext *ctx;
-  json_t *root;
-  json_t *order;
-
-  if (NULL == *connection_cls)
-  {
-    ctx = GNUNET_new (struct TMH_JsonParseContext);
-    ctx->hc.cc = &json_parse_cleanup;
-    *connection_cls = ctx;
-  }
-  else
-  {
-    ctx = *connection_cls;
-  }
-
-  res = TMH_PARSE_post_json (connection,
-                             &ctx->json_parse_context,
-                             upload_data,
-                             upload_data_size,
-                             &root);
-
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-
-  /* A error response was already generated */
-  if ( (GNUNET_NO == res) ||
-       /* or, need more data to accomplish parsing */
-       (NULL == root) )
-    return MHD_YES;
-  order = json_object_get (root,
-                           "order");
-  if (NULL == order)
-  {
-    res = TMH_RESPONSE_reply_arg_missing
-            (connection,
-            TALER_EC_PARAMETER_MISSING,
-            "order");
-  }
-  else
-    res = proposal_put (connection, root, order, mi);
-  json_decref (root);
-  return res;
-}
-
-
 /**
  * Manage a GET /proposal request. Query the db and returns the
  * proposal's data related to the transaction id given as the URL's
diff --git a/src/backend/taler-merchant-httpd_proposal.h 
b/src/backend/taler-merchant-httpd_proposal.h
index b118523..85db1ff 100644
--- a/src/backend/taler-merchant-httpd_proposal.h
+++ b/src/backend/taler-merchant-httpd_proposal.h
@@ -23,28 +23,6 @@
 #include <microhttpd.h>
 #include "taler-merchant-httpd.h"
 
-/**
- * Generate a proposal, given its order. In practical terms, it adds the
- * fields  'exchanges', 'merchant_pub', and 'H_wire' to the order gotten
- * from the frontend. Finally, it signs this data, and returns it to the
- * frontend.
- *
- * @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
- * @return MHD result code
- */
-int
-MH_handler_proposal_put (struct TMH_RequestHandler *rh,
-                         struct MHD_Connection *connection,
-                         void **connection_cls,
-                         const char *upload_data,
-                         size_t *upload_data_size,
-                         struct MerchantInstance *mi);
-
-
 /**
  * Manage a GET /proposal request. Query the db and returns the
  * proposal's data related to the transaction id given as the URL's
diff --git a/src/lib/merchant_api_check_payment.c 
b/src/lib/merchant_api_check_payment.c
index 8a85d26..35cd44c 100644
--- a/src/lib/merchant_api_check_payment.c
+++ b/src/lib/merchant_api_check_payment.c
@@ -79,12 +79,10 @@ handle_check_payment_finished (void *cls,
 {
   struct TALER_MERCHANT_CheckPaymentOperation *cpo = cls;
   struct TALER_Amount refund_amount = { 0 };
-  int refunded;
   const json_t *json = response;
+  const json_t *refunded;
 
   struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_boolean ("refunded",
-                              &refunded),
     TALER_JSON_spec_amount ("refund_amount",
                             &refund_amount),
     GNUNET_JSON_spec_end ()
@@ -103,7 +101,7 @@ handle_check_payment_finished (void *cls,
              json,
              GNUNET_SYSERR,
              GNUNET_SYSERR,
-             &refund_amount,
+             NULL,
              NULL);
     TALER_MERCHANT_check_payment_cancel (cpo);
     return;
@@ -123,7 +121,7 @@ handle_check_payment_finished (void *cls,
                json,
                GNUNET_SYSERR,
                GNUNET_SYSERR,
-               &refund_amount,
+               NULL,
                NULL);
     }
     else
@@ -133,17 +131,19 @@ handle_check_payment_finished (void *cls,
                json,
                GNUNET_NO,
                GNUNET_NO,
-               &refund_amount,
+               NULL,
                taler_pay_uri);
     }
     TALER_MERCHANT_check_payment_cancel (cpo);
     return;
   }
 
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (json,
-                         spec,
-                         NULL, NULL))
+  if ( (NULL == (refunded = json_object_get (json, "refunded"))) ||
+       ( (json_true () == refunded) &&
+         (GNUNET_OK !=
+          GNUNET_JSON_parse (json,
+                             spec,
+                             NULL, NULL)) ) )
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "check payment failed to parse JSON\n");
@@ -153,7 +153,7 @@ handle_check_payment_finished (void *cls,
              json,
              GNUNET_SYSERR,
              GNUNET_SYSERR,
-             &refund_amount,
+             NULL,
              NULL);
     TALER_MERCHANT_check_payment_cancel (cpo);
     return;
@@ -163,10 +163,9 @@ handle_check_payment_finished (void *cls,
            MHD_HTTP_OK,
            json,
            GNUNET_YES,
-           refunded,
-           &refund_amount,
+           (json_true () == refunded),
+           (json_true () == refunded) ? &refund_amount : NULL,
            NULL);
-  GNUNET_JSON_parse_free (spec);
   TALER_MERCHANT_check_payment_cancel (cpo);
 }
 

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



reply via email to

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