gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] tag demo-2019-11-02-00 updated (b4f2c5e -> 3a22e60)


From: gnunet
Subject: [taler-merchant] tag demo-2019-11-02-00 updated (b4f2c5e -> 3a22e60)
Date: Sat, 02 Nov 2019 14:59:35 +0100

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

dold pushed a change to tag demo-2019-11-02-00
in repository merchant.

*** WARNING: tag demo-2019-11-02-00 was modified! ***

    from b4f2c5e  (commit)
      to 3a22e60  (commit)
    from b4f2c5e  logging
     add cadda47  fix newlines
     add d19a45f  separate /order and /proposal handlers into different files
     add e8d2cde  clean up check payment logic
     add ae06bf1  simplify logic
     add ea48d9f  implement /public/poll-payment API, refactor to avoid code 
duplication
     add 3a22e60  implement TALER_MERCHANT_poll_payment()

No new revisions were added by this update.

Summary of changes:
 contrib/uncrustify.cfg                             |   4 +-
 src/backend/Makefile.am                            |  22 +-
 src/backend/taler-merchant-httpd.c                 |  88 ++-
 src/backend/taler-merchant-httpd.h                 |  31 +-
 src/backend/taler-merchant-httpd_check-payment.c   | 584 +++++++++---------
 ...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_poll-payment.c    | 557 +++++++++++++++++
 ...orize.h => taler-merchant-httpd_poll-payment.h} |  25 +-
 src/backend/taler-merchant-httpd_proposal.c        | 658 ---------------------
 src/backend/taler-merchant-httpd_proposal.h        |  22 -
 src/backend/taler-merchant-httpd_tip-pickup.c      |   4 +-
 src/include/taler_merchant_service.h               |  74 +++
 src/lib/Makefile.am                                |  11 +-
 src/lib/merchant_api_check_payment.c               |  27 +-
 ...check_payment.c => merchant_api_poll_payment.c} | 132 +++--
 17 files changed, 1168 insertions(+), 1301 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%)
 create mode 100644 src/backend/taler-merchant-httpd_poll-payment.c
 copy src/backend/{taler-merchant-httpd_tip-authorize.h => 
taler-merchant-httpd_poll-payment.h} (65%)
 copy src/lib/{merchant_api_check_payment.c => merchant_api_poll_payment.c} 
(54%)

diff --git a/contrib/uncrustify.cfg b/contrib/uncrustify.cfg
index 12dd62c..00cf228 100644
--- a/contrib/uncrustify.cfg
+++ b/contrib/uncrustify.cfg
@@ -49,8 +49,8 @@ nl_assign_brace=remove
 
 # No extra newlines that cause noisy diffs
 nl_start_of_file=remove
-nl_before_func_body_proto = 2
-nl_before_func_body_def = 3
+nl_before_func_body_proto = 0
+nl_before_func_body_def = 0
 nl_after_func_proto = 2
 nl_after_func_body = 3
 # If there's no new line, it's not a text file!
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index cd221ca..966d8f4 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -14,25 +14,25 @@ bin_PROGRAMS = \
 
 taler_merchant_httpd_SOURCES = \
   taler-merchant-httpd.c taler-merchant-httpd.h \
-  taler-merchant-httpd_parsing.c taler-merchant-httpd_parsing.h \
-  taler-merchant-httpd_responses.c taler-merchant-httpd_responses.h \
-  taler-merchant-httpd_mhd.c taler-merchant-httpd_mhd.h \
   taler-merchant-httpd_auditors.c taler-merchant-httpd_auditors.h \
+  taler-merchant-httpd_config.c taler-merchant-httpd_config.h \
+  taler-merchant-httpd_check-payment.c taler-merchant-httpd_check-payment.h \
   taler-merchant-httpd_exchanges.c taler-merchant-httpd_exchanges.h \
-  taler-merchant-httpd_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 \
+  taler-merchant-httpd_mhd.c taler-merchant-httpd_mhd.h \
+  taler-merchant-httpd_order.c taler-merchant-httpd_order.h \
+  taler-merchant-httpd_parsing.c taler-merchant-httpd_parsing.h \
+  taler-merchant-httpd_pay.c taler-merchant-httpd_pay.h \
+  taler-merchant-httpd_poll-payment.c taler-merchant-httpd_poll-payment.h \
+  taler-merchant-httpd_proposal.c taler-merchant-httpd_proposal.h \
+  taler-merchant-httpd_refund.c taler-merchant-httpd_refund.h \
+  taler-merchant-httpd_responses.c taler-merchant-httpd_responses.h \
   taler-merchant-httpd_tip-authorize.c taler-merchant-httpd_tip-authorize.h \
   taler-merchant-httpd_tip-pickup.c taler-merchant-httpd_tip-pickup.h \
   taler-merchant-httpd_tip-query.c taler-merchant-httpd_tip-query.h \
   taler-merchant-httpd_tip-reserve-helper.c 
taler-merchant-httpd_tip-reserve-helper.h \
   taler-merchant-httpd_track-transaction.c 
taler-merchant-httpd_track-transaction.h \
-  taler-merchant-httpd_track-transfer.c taler-merchant-httpd_track-transfer.h \
-  taler-merchant-httpd_refund.c taler-merchant-httpd_refund.h \
-  taler-merchant-httpd_check-payment.c taler-merchant-httpd_check-payment.h \
-  taler-merchant-httpd_config.c taler-merchant-httpd_config.h
-
-
+  taler-merchant-httpd_track-transfer.c taler-merchant-httpd_track-transfer.h
 taler_merchant_httpd_LDADD = \
   $(top_builddir)/src/backenddb/libtalermerchantdb.la \
   -ltalerexchange \
diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index ef7b650..8abb444 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"
@@ -47,6 +48,7 @@
 #include "taler-merchant-httpd_history.h"
 #include "taler-merchant-httpd_refund.h"
 #include "taler-merchant-httpd_check-payment.h"
+#include "taler-merchant-httpd_poll-payment.h"
 #include "taler-merchant-httpd_config.h"
 
 /**
@@ -167,6 +169,11 @@ struct GNUNET_CONTAINER_Heap *resume_timeout_heap;
  */
 struct GNUNET_CONTAINER_MultiHashMap *payment_trigger_map;
 
+/**
+ * Task responsible for timeouts in the #resume_timeout_heap.
+ */
+struct GNUNET_SCHEDULER_Task *resume_timeout_task;
+
 
 /**
  * Return #GNUNET_YES if given a valid correlation ID and
@@ -275,6 +282,73 @@ TMH_compute_pay_key (const char *order_id,
 }
 
 
+/**
+ * Create a taler://pay/ URI for the given @a con and @a order_id
+ * and @a session_id and @a instance_id.
+ *
+ * @param con HTTP connection
+ * @param order_id the order id
+ * @param session_id session, may be NULL
+ * @param instance_id instance, may be "default"
+ * @return corresponding taler://pay/ URI, or NULL on missing "host"
+ */
+char *
+TMH_make_taler_pay_uri (struct MHD_Connection *con,
+                        const char *order_id,
+                        const char *session_id,
+                        const char *instance_id)
+{
+  const char *host;
+  const char *forwarded_host;
+  const char *uri_path;
+  const char *uri_instance_id;
+  const char *query;
+  char *result;
+
+  host = MHD_lookup_connection_value (con,
+                                      MHD_HEADER_KIND,
+                                      "Host");
+  forwarded_host = MHD_lookup_connection_value (con,
+                                                MHD_HEADER_KIND,
+                                                "X-Forwarded-Host");
+
+  uri_path = MHD_lookup_connection_value (con,
+                                          MHD_HEADER_KIND,
+                                          "X-Forwarded-Prefix");
+  if (NULL == uri_path)
+    uri_path = "-";
+  if (NULL != forwarded_host)
+    host = forwarded_host;
+  if (0 == strcmp (instance_id,
+                   "default"))
+    uri_instance_id = "-";
+  else
+    uri_instance_id = instance_id;
+  if (NULL == host)
+  {
+    /* Should never happen, at least the host header should be defined */
+    GNUNET_break (0);
+    return NULL;
+  }
+
+  if (GNUNET_YES == TALER_mhd_is_https (con))
+    query = "";
+  else
+    query = "?insecure=1";
+  GNUNET_assert (NULL != order_id);
+  GNUNET_assert (0 < GNUNET_asprintf (&result,
+                                      "taler://pay/%s/%s/%s/%s%s%s%s",
+                                      host,
+                                      uri_path,
+                                      uri_instance_id,
+                                      order_id,
+                                      (NULL == session_id) ? "" : "/",
+                                      (NULL == session_id) ? "" : session_id,
+                                      query));
+  return result;
+}
+
+
 /**
  * Shutdown task (magically invoked when the application is being
  * quit)
@@ -310,6 +384,11 @@ do_shutdown (void *cls)
     GNUNET_CONTAINER_heap_destroy (resume_timeout_heap);
     resume_timeout_heap = NULL;
   }
+  if (NULL != resume_timeout_task)
+  {
+    GNUNET_SCHEDULER_cancel (resume_timeout_task);
+    resume_timeout_task = NULL;
+  }
   if (NULL != mhd)
   {
     MHD_stop_daemon (mhd);
@@ -441,8 +520,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 +1111,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 +1261,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},
@@ -1199,6 +1274,9 @@ url_handler (void *cls,
     { "/check-payment", MHD_HTTP_METHOD_GET, "text/plain",
       NULL, 0,
       &MH_handler_check_payment, MHD_HTTP_OK},
+    { "/public/poll-payment", MHD_HTTP_METHOD_GET, "text/plain",
+      NULL, 0,
+      &MH_handler_poll_payment, MHD_HTTP_OK},
     { "/config", MHD_HTTP_METHOD_GET, "text/plain",
       NULL, 0,
       &MH_handler_config, MHD_HTTP_OK},
diff --git a/src/backend/taler-merchant-httpd.h 
b/src/backend/taler-merchant-httpd.h
index bc53bbc..53dedcb 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -276,15 +276,21 @@ struct TMH_SuspendedConnection
    */
   struct MHD_Connection *con;
 
+  /**
+   * Associated heap node.
+   */
+  struct GNUNET_CONTAINER_HeapNode *hn;
+
   /**
    * Key of this entry in the #payment_trigger_map
    */
   struct GNUNET_HashCode key;
 
   /**
-   * Associated heap node.
+   * 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_CONTAINER_HeapNode *hn;
+  struct GNUNET_TIME_Absolute long_poll_timeout;
 
 };
 
@@ -319,6 +325,11 @@ extern unsigned long long default_wire_fee_amortization;
  */
 extern struct GNUNET_CONTAINER_Heap *resume_timeout_heap;
 
+/**
+ * Task responsible for timeouts in the #resume_timeout_heap.
+ */
+extern struct GNUNET_SCHEDULER_Task *resume_timeout_task;
+
 /**
  * Hash map from H(order_id,merchant_pub) to `struct TMH_SuspendedConnection`
  * entries to resume when a payment is made for the given order.
@@ -397,4 +408,20 @@ TMH_compute_pay_key (const char *order_id,
                      struct GNUNET_HashCode *key);
 
 
+/**
+ * Create a taler://pay/ URI for the given @a con and @a order_id
+ * and @a session_id and @a instance_id.
+ *
+ * @param con HTTP connection
+ * @param order_id the order id
+ * @param session_id session, may be NULL
+ * @param instance_id instance, may be "default"
+ * @return corresponding taler://pay/ URI, or NULL on missing "host"
+ */
+char *
+TMH_make_taler_pay_uri (struct MHD_Connection *con,
+                        const char *order_id,
+                        const char *session_id,
+                        const char *instance_id);
+
 #endif
diff --git a/src/backend/taler-merchant-httpd_check-payment.c 
b/src/backend/taler-merchant-httpd_check-payment.c
index 74b8c5c..fad3fdc 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>
@@ -38,69 +39,96 @@
 
 
 /**
- * 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
- * @returns the URI, must be freed with #GNUNET_free
+ * Data structure we keep for a check payment request.
  */
-static char *
-make_taler_pay_uri (struct MHD_Connection *connection,
-                    const char *instance_id,
-                    const char *order_id,
-                    const char *session_id)
+struct CheckPaymentRequestContext
 {
-  const char *host;
-  const char *forwarded_host;
-  const char *uri_path;
-  const char *uri_instance_id;
-  const char *query;
-  char *result;
-
-  host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "Host");
-  forwarded_host = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
-                                                "X-Forwarded-Host");
-
-  uri_path = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
-                                          "X-Forwarded-Prefix");
-  if (NULL == uri_path)
-    uri_path = "-";
-
-  if (NULL != forwarded_host)
-    host = forwarded_host;
-
-  if (0 == strcmp (instance_id, "default"))
-    uri_instance_id = "-";
-  else
-    uri_instance_id = instance_id;
+  /**
+   * Must be first for #handle_mhd_completion_callback.
+   */
+  struct TM_HandlerContext hc;
 
-  if (NULL == host)
-  {
-    /* Should never happen, at least the host header should be defined */
-    GNUNET_break (0);
-    return NULL;
-  }
+  struct MHD_Connection *connection;
 
-  if (GNUNET_YES == TALER_mhd_is_https (connection))
-    query = "";
-  else
-    query = "?insecure=1";
-
-  GNUNET_assert (NULL != order_id);
-
-  GNUNET_assert (0 < GNUNET_asprintf (&result,
-                                      "taler://pay/%s/%s/%s/%s%s%s%s",
-                                      host,
-                                      uri_path,
-                                      uri_instance_id,
-                                      order_id,
-                                      (session_id == NULL) ? "" : "/",
-                                      (session_id == NULL) ? "" : session_id,
-                                      query));
-  return result;
+  /**
+   * Which merchant instance is this for?
+   */
+  struct MerchantInstance *mi;
+
+  /**
+   * 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;
+
+  /**
+   * 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);
 }
 
 
@@ -123,51 +151,47 @@ 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 mi merchant instance
+ * @param cprc check pay request context
  * @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,
-                  const struct MerchantInstance *mi)
+send_pay_request (const struct CheckPaymentRequestContext *cprc)
 {
   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,
-                                &mi->pubkey);
+                                cprc->session_id,
+                                cprc->fulfillment_url,
+                                &cprc->mi->pubkey);
     if (qs < 0)
     {
       /* single, read-only SQL statements should never cause
@@ -175,61 +199,99 @@ 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 = TMH_make_taler_pay_uri (cprc->connection,
+                                          cprc->order_id,
+                                          cprc->session_id,
+                                          cprc->mi->id);
+  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 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,
-                       &mi->pubkey);
+                       &cprc->contract_terms,
+                       cprc->order_id,
+                       &cprc->mi->pubkey);
   if (0 > qs)
   {
     /* single, read-only SQL statements should never cause
@@ -237,55 +299,22 @@ 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);
 }
 
 
@@ -310,131 +339,92 @@ 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)
-  {
-    /* 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
+  if (NULL == cprc)
   {
-    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 */
+    cprc = GNUNET_new (struct CheckPaymentRequestContext);
+    cprc->hc.cc = &cprc_cleanup;
+    cprc->ret = GNUNET_SYSERR;
+    cprc->connection = connection;
+    cprc->mi = mi;
+    *connection_cls = cprc;
+
+    cprc->order_id = MHD_lookup_connection_value (connection,
+                                                  MHD_GET_ARGUMENT_KIND,
+                                                  "order_id");
+    if (NULL == cprc->order_id)
     {
-      GNUNET_break (0);
-      GNUNET_free (final_contract_url);
-      json_decref (contract_terms);
+      /* order_id is required but missing */
+      GNUNET_break_op (0);
+      return TMH_RESPONSE_reply_bad_request (connection,
+                                             TALER_EC_PARAMETER_MISSING,
+                                             "order_id required");
+    }
+    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 (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;
+  }
 
+  GNUNET_assert (NULL != cprc->contract_terms);
 
   /* 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 +439,37 @@ 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,
-                              mi);
+      ret = send_pay_request (cprc);
       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);
     }
     GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
     GNUNET_assert (NULL != xcontract_terms);
@@ -510,9 +481,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 +491,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_poll-payment.c 
b/src/backend/taler-merchant-httpd_poll-payment.c
new file mode 100644
index 0000000..945a356
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_poll-payment.c
@@ -0,0 +1,557 @@
+/*
+  This file is part of TALER
+  (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
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file backend/taler-merchant-httpd_poll-payment.c
+ * @brief implementation of /public/poll-payment handler
+ * @author Florian Dold
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <string.h>
+#include <microhttpd.h>
+#include <jansson.h>
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+#include "taler-merchant-httpd.h"
+#include "taler-merchant-httpd_mhd.h"
+#include "taler-merchant-httpd_parsing.h"
+#include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_responses.h"
+#include "taler-merchant-httpd_poll-payment.h"
+
+/**
+ * Maximum number of retries for database operations.
+ */
+#define MAX_RETRIES 5
+
+
+/**
+ * Data structure we keep for a check payment request.
+ */
+struct PollPaymentRequestContext
+{
+  /**
+   * Must be first for #handle_mhd_completion_callback.
+   */
+  struct TM_HandlerContext hc;
+
+  /**
+   * Entry in the #resume_timeout_heap for this check payment, if we are
+   * suspended.
+   */
+  struct TMH_SuspendedConnection sc;
+
+  /**
+   * Which merchant instance is this for?
+   */
+  struct MerchantInstance *mi;
+
+  /**
+   * 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;
+
+  /**
+   * fulfillment URL of the contract (valid as long as
+   * @e contract_terms is valid).
+   */
+  const char *fulfillment_url;
+
+  /**
+   * session of the client
+   */
+  const char *session_id;
+
+  /**
+   * 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 PollPaymentRequestContext *`
+ */
+static void
+pprc_cleanup (struct TM_HandlerContext *hc)
+{
+  struct PollPaymentRequestContext *pprc = (struct
+                                            PollPaymentRequestContext *) hc;
+
+  if (NULL != pprc->contract_terms)
+    json_decref (pprc->contract_terms);
+  GNUNET_free_non_null (pprc->final_contract_url);
+  GNUNET_free (pprc);
+}
+
+
+/**
+ * Function called with information about a refund.
+ * It is responsible for summing up the refund amount.
+ *
+ * @param cls closure
+ * @param coin_pub public coin from which the refund comes from
+ * @param rtransaction_id identificator of the refund
+ * @param reason human-readable explaination of the refund
+ * @param refund_amount refund amount which is being taken from coin_pub
+ * @param refund_fee cost of this refund operation
+ */
+static void
+process_refunds_cb (void *cls,
+                    const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                    uint64_t rtransaction_id,
+                    const char *reason,
+                    const struct TALER_Amount *refund_amount,
+                    const struct TALER_Amount *refund_fee)
+{
+  struct PollPaymentRequestContext *pprc = cls;
+
+  if (pprc->refunded)
+  {
+    GNUNET_assert (GNUNET_SYSERR !=
+                   TALER_amount_add (&pprc->refund_amount,
+                                     &pprc->refund_amount,
+                                     refund_amount));
+    return;
+  }
+  pprc->refund_amount = *refund_amount;
+  pprc->refunded = GNUNET_YES;
+}
+
+
+/**
+ * Resume processing all suspended connections past timeout.
+ *
+ * @param cls unused
+ */
+static void
+do_resume (void *cls)
+{
+  struct TMH_SuspendedConnection *sc;
+
+  (void) cls;
+  resume_timeout_task = NULL;
+  while (1)
+  {
+    sc = GNUNET_CONTAINER_heap_peek (resume_timeout_heap);
+    if (NULL == sc)
+      return;
+    if  (0 !=
+         GNUNET_TIME_absolute_get_remaining (
+           sc->long_poll_timeout).rel_value_us)
+      break;
+    GNUNET_assert (sc ==
+                   GNUNET_CONTAINER_heap_remove_root (resume_timeout_heap));
+    sc->hn = NULL;
+    GNUNET_assert (GNUNET_YES ==
+                   GNUNET_CONTAINER_multihashmap_remove (payment_trigger_map,
+                                                         &sc->key,
+                                                         sc));
+    MHD_resume_connection (sc->con);
+  }
+  resume_timeout_task = GNUNET_SCHEDULER_add_at (sc->long_poll_timeout,
+                                                 &do_resume,
+                                                 NULL);
+}
+
+
+/**
+ * The client did not yet pay, send it the payment request.
+ *
+ * @param pprc check pay request context
+ * @return #MHD_YES on success
+ */
+static int
+send_pay_request (struct PollPaymentRequestContext *pprc)
+{
+  int ret;
+  char *already_paid_order_id = NULL;
+  char *taler_pay_uri;
+
+  if (0 !=
+      GNUNET_TIME_absolute_get_remaining (
+        pprc->sc.long_poll_timeout).rel_value_us)
+  {
+    struct TMH_SuspendedConnection *sc;
+
+    /* long polling: do not queue a response, suspend connection instead */
+    TMH_compute_pay_key (pprc->order_id,
+                         &pprc->mi->pubkey,
+                         &pprc->sc.key);
+    GNUNET_assert (GNUNET_OK ==
+                   GNUNET_CONTAINER_multihashmap_put (payment_trigger_map,
+                                                      &pprc->sc.key,
+                                                      &pprc->sc,
+                                                      
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
+    pprc->sc.hn = GNUNET_CONTAINER_heap_insert (resume_timeout_heap,
+                                                &pprc->sc,
+                                                pprc->sc.long_poll_timeout.
+                                                abs_value_us);
+    MHD_suspend_connection (pprc->sc.con);
+    if (NULL != resume_timeout_task)
+    {
+      GNUNET_SCHEDULER_cancel (resume_timeout_task);
+      resume_timeout_task = NULL;
+    }
+    sc = GNUNET_CONTAINER_heap_peek (resume_timeout_heap);
+    resume_timeout_task = GNUNET_SCHEDULER_add_at (sc->long_poll_timeout,
+                                                   &do_resume,
+                                                   NULL);
+    return MHD_YES;
+  }
+
+  /* Check if resource_id has been paid for in the same session
+   * with another order_id.
+   */
+  if ( (NULL != pprc->session_id) &&
+       (NULL != pprc->fulfillment_url) )
+  {
+    enum GNUNET_DB_QueryStatus qs;
+
+    qs = db->find_session_info (db->cls,
+                                &already_paid_order_id,
+                                pprc->session_id,
+                                pprc->fulfillment_url,
+                                &pprc->mi->pubkey);
+    if (qs < 0)
+    {
+      /* 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 (pprc->sc.con,
+                                                
TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR,
+                                                "db error fetching pay session 
info");
+    }
+  }
+  taler_pay_uri = TMH_make_taler_pay_uri (pprc->sc.con,
+                                          pprc->order_id,
+                                          pprc->session_id,
+                                          pprc->mi->id);
+  ret = TMH_RESPONSE_reply_json_pack (pprc->sc.con,
+                                      MHD_HTTP_OK,
+                                      "{s:s, s:s, s:b, s:s?}",
+                                      "taler_pay_uri", taler_pay_uri,
+                                      "contract_url", pprc->final_contract_url,
+                                      "paid", 0,
+                                      "already_paid_order_id",
+                                      already_paid_order_id);
+  GNUNET_free (taler_pay_uri);
+  GNUNET_free_non_null (already_paid_order_id);
+  return ret;
+}
+
+
+/**
+ * Manages a /public/poll-payment call, checking the status
+ * of a payment and, if necessary, constructing the URL
+ * for a payment redirect URL.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param mi merchant backend instance, never NULL
+ * @return MHD result code
+ */
+int
+MH_handler_poll_payment (struct TMH_RequestHandler *rh,
+                         struct MHD_Connection *connection,
+                         void **connection_cls,
+                         const char *upload_data,
+                         size_t *upload_data_size,
+                         struct MerchantInstance *mi)
+{
+  struct PollPaymentRequestContext *pprc = *connection_cls;
+  enum GNUNET_DB_QueryStatus qs;
+  int ret;
+
+  if (NULL == pprc)
+  {
+    /* First time here, parse request and check order is known */
+    const char *long_poll_timeout_s;
+    const char *cts;
+
+    pprc = GNUNET_new (struct PollPaymentRequestContext);
+    pprc->hc.cc = &pprc_cleanup;
+    pprc->ret = GNUNET_SYSERR;
+    pprc->sc.con = connection;
+    pprc->mi = mi;
+    *connection_cls = pprc;
+
+    pprc->order_id = MHD_lookup_connection_value (connection,
+                                                  MHD_GET_ARGUMENT_KIND,
+                                                  "order_id");
+    if (NULL == pprc->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");
+    }
+    cts = MHD_lookup_connection_value (connection,
+                                       MHD_GET_ARGUMENT_KIND,
+                                       "h_contract");
+    if (NULL == cts)
+    {
+      /* h_contract required but missing */
+      GNUNET_break_op (0);
+      return TMH_RESPONSE_reply_bad_request (connection,
+                                             TALER_EC_PARAMETER_MISSING,
+                                             "h_contract required");
+    }
+    if (GNUNET_OK !=
+        GNUNET_CRYPTO_hash_from_string (cts,
+                                        &pprc->h_contract_terms))
+    {
+      /* cts has wrong encoding */
+      GNUNET_break_op (0);
+      return TMH_RESPONSE_reply_bad_request (connection,
+                                             TALER_EC_PARAMETER_MALFORMED,
+                                             "h_contract malformed");
+    }
+    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");
+      }
+      pprc->sc.long_poll_timeout
+        = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (
+                                              GNUNET_TIME_UNIT_SECONDS,
+                                              timeout));
+    }
+    else
+    {
+      pprc->sc.long_poll_timeout = GNUNET_TIME_UNIT_ZERO_ABS;
+    }
+    pprc->contract_url = MHD_lookup_connection_value (connection,
+                                                      MHD_GET_ARGUMENT_KIND,
+                                                      "contract_url");
+    if (NULL == pprc->contract_url)
+    {
+      pprc->final_contract_url = TALER_url_absolute_mhd (connection,
+                                                         "/public/proposal",
+                                                         "instance", mi->id,
+                                                         "order_id",
+                                                         pprc->order_id,
+                                                         NULL);
+      GNUNET_assert (NULL != pprc->final_contract_url);
+    }
+    else
+    {
+      pprc->final_contract_url = GNUNET_strdup (pprc->contract_url);
+    }
+    pprc->session_id = MHD_lookup_connection_value (connection,
+                                                    MHD_GET_ARGUMENT_KIND,
+                                                    "session_id");
+
+    /* obtain contract terms, indirectly checking that the client's contract
+       terms hash is actually valid and known. */
+    db->preflight (db->cls);
+    qs = db->find_contract_terms_from_hash (db->cls,
+                                            &pprc->contract_terms,
+                                            &pprc->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);
+      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,
+                  "Contract unknown\n");
+      return TMH_RESPONSE_reply_not_found (connection,
+                                           
TALER_EC_POLL_PAYMENT_CONTRACT_NOT_FOUND,
+                                           "Given order_id doesn't map to any 
proposal");
+    }
+    GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
+
+    {
+      struct GNUNET_JSON_Specification spec[] = {
+        GNUNET_JSON_spec_string ("fulfillment_url",
+                                 &pprc->fulfillment_url),
+        GNUNET_JSON_spec_end ()
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (pprc->contract_terms,
+                             spec,
+                             NULL, NULL))
+      {
+        GNUNET_break (0);
+        return TMH_RESPONSE_reply_internal_error (connection,
+                                                  
TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
+                                                  "Merchant database error 
(contract terms corrupted)");
+      }
+    }
+
+  } /* end of first-time initialization / sanity checks */
+
+
+  db->preflight (db->cls);
+
+  /* Check if the order has been paid for. */
+  if (NULL != pprc->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,
+                                pprc->session_id,
+                                pprc->fulfillment_url,
+                                &mi->pubkey);
+    if (qs < 0)
+    {
+      /* single, read-only SQL statements should never cause
+         serialization problems */
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+      /* Always report on hard error as well to enable diagnostics */
+      GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+      return TMH_RESPONSE_reply_internal_error (connection,
+                                                
TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR,
+                                                "db error fetching pay session 
info");
+    }
+    else if (0 == qs)
+    {
+      ret = send_pay_request (pprc);
+      GNUNET_free_non_null (already_paid_order_id);
+      return ret;
+    }
+    GNUNET_break (1 == qs);
+    GNUNET_break (0 == strcmp (pprc->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,
+                                                 &pprc->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);
+      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");
+      return send_pay_request (pprc);
+    }
+    GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
+    GNUNET_assert (NULL != xcontract_terms);
+    json_decref (xcontract_terms);
+  }
+
+  /* Accumulate refunds, if any. */
+  for (unsigned int i = 0; i<MAX_RETRIES; i++)
+  {
+    qs = db->get_refunds_from_contract_terms_hash (db->cls,
+                                                   &mi->pubkey,
+                                                   &pprc->h_contract_terms,
+                                                   &process_refunds_cb,
+                                                   pprc);
+    if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
+      break;
+  }
+  if (0 > qs)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Database hard error on refunds_from_contract_terms_hash 
lookup: %s\n",
+                GNUNET_h2s (&pprc->h_contract_terms));
+    return TMH_RESPONSE_reply_internal_error (connection,
+                                              
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
+                                              "Merchant database error");
+  }
+  if (pprc->refunded)
+    return TMH_RESPONSE_reply_json_pack (connection,
+                                         MHD_HTTP_OK,
+                                         "{s:b, s:b, s:o}",
+                                         "paid", 1,
+                                         "refunded", pprc->refunded,
+                                         "refund_amount",
+                                         TALER_JSON_from_amount (
+                                           &pprc->refund_amount));
+  return TMH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_OK,
+                                       "{s:b, s:b }",
+                                       "paid", 1,
+                                       "refunded", 0);
+}
diff --git a/src/backend/taler-merchant-httpd_tip-authorize.h 
b/src/backend/taler-merchant-httpd_poll-payment.h
similarity index 65%
copy from src/backend/taler-merchant-httpd_tip-authorize.h
copy to src/backend/taler-merchant-httpd_poll-payment.h
index 8706d64..e9f54c2 100644
--- a/src/backend/taler-merchant-httpd_tip-authorize.h
+++ b/src/backend/taler-merchant-httpd_poll-payment.h
@@ -14,18 +14,19 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file backend/taler-merchant-httpd_tip-authorize.h
- * @brief headers for /tip-authorize handler
+ * @file backend/taler-merchant-httpd_poll-payment.h
+ * @brief headers for /public/poll-payment handler
  * @author Christian Grothoff
+ * @author Florian Dold
  */
-#ifndef TALER_MERCHANT_HTTPD_TIP_AUTHORIZE_H
-#define TALER_MERCHANT_HTTPD_TIP_AUTHORIZE_H
+#ifndef TALER_MERCHANT_HTTPD_POLL_PAYMENT_H
+#define TALER_MERCHANT_HTTPD_POLL_PAYMENT_H
 #include <microhttpd.h>
 #include "taler-merchant-httpd.h"
 
 /**
- * Manages a /tip-authorize call, creating a TIP ID and storing the
- * authorization in our DB.
+ * Manages a /public/poll-payment call, checking the status
+ * of a payment.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
@@ -36,11 +37,11 @@
  * @return MHD result code
  */
 int
-MH_handler_tip_authorize (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_poll_payment (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_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/backend/taler-merchant-httpd_tip-pickup.c 
b/src/backend/taler-merchant-httpd_tip-pickup.c
index bead341..d53596c 100644
--- a/src/backend/taler-merchant-httpd_tip-pickup.c
+++ b/src/backend/taler-merchant-httpd_tip-pickup.c
@@ -649,7 +649,9 @@ MH_handler_tip_pickup_get (struct TMH_RequestHandler *rh,
                                            "tip_id required");
   }
 
-  if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (tip_id_str, &tip_id))
+  if (GNUNET_OK !=
+      GNUNET_CRYPTO_hash_from_string (tip_id_str,
+                                      &tip_id))
   {
     /* tip_id has wrong encoding */
     GNUNET_break_op (0);
diff --git a/src/include/taler_merchant_service.h 
b/src/include/taler_merchant_service.h
index fff74ea..1d424f2 100644
--- a/src/include/taler_merchant_service.h
+++ b/src/include/taler_merchant_service.h
@@ -1077,4 +1077,78 @@ void
 TALER_MERCHANT_tip_query_cancel (struct TALER_MERCHANT_TipQueryOperation *tqh);
 
 
+/* ********************** /public/poll-payment ************************* */
+
+
+/**
+ * Handle for a /public/poll-payment operation.
+ */
+struct TALER_MERCHANT_PollPaymentOperation;
+
+
+/**
+ * Callback to process a GET /poll-payment request
+ *
+ * @param cls closure
+ * @param http_status HTTP status code for this request
+ * @param obj raw response body
+ * @param paid #GNUNET_YES if the payment is settled, #GNUNET_NO if not
+ *        settled, $GNUNET_SYSERR on error
+ *        (note that refunded payments are returned as paid!)
+ * @param refunded #GNUNET_YES if there is at least on refund on this payment,
+ *        #GNUNET_NO if refunded, #GNUNET_SYSERR or error
+ * @param refunded_amount amount that was refunded, NULL if there
+ *        was no refund
+ * @param taler_pay_uri the URI that instructs the wallets to process
+ *                      the payment
+ */
+typedef void
+(*TALER_MERCHANT_PollPaymentCallback) (void *cls,
+                                       unsigned int http_status,
+                                       const json_t *obj,
+                                       int paid,
+                                       int refunded,
+                                       struct TALER_Amount *refund_amount,
+                                       const char *taler_pay_uri);
+
+
+/**
+ * Issue a /poll-payment request to the backend.  Polls the status
+ * of a payment.
+ *
+ * @param ctx execution context
+ * @param backend_url base URL of the merchant backend
+ * @param order_id order id to identify the payment
+ * @param h_contract hash of the contract for @a order_id
+ * @parem session_id sesion id for the payment (or NULL if the payment is not 
bound to a session)
+ * @param timeout timeout to use in long polling (how long may the server wait 
to reply
+ *        before generating an unpaid response). Note that this is just 
provided to
+ *        the server, we as client will block until the response comes back or 
until
+ *        #TALER_MERCHANT_poll_payment_cancel() is called.
+ * @param poll_payment_cb callback which will work the response gotten from 
the backend
+ * @param poll_payment_cb_cls closure to pass to @a poll_payment_cb
+ * @return handle for this operation, NULL upon errors
+ */
+struct TALER_MERCHANT_PollPaymentOperation *
+TALER_MERCHANT_poll_payment (struct GNUNET_CURL_Context *ctx,
+                             const char *backend_url,
+                             const char *order_id,
+                             const struct GNUNET_HashCode *h_contract,
+                             const char *session_id,
+                             struct GNUNET_TIME_Relative timeout,
+                             TALER_MERCHANT_PollPaymentCallback
+                             poll_payment_cb,
+                             void *poll_payment_cls);
+
+
+/**
+ * Cancel a GET /public/poll-payment request.
+ *
+ * @param cpo handle to the request to be canceled
+ */
+void
+TALER_MERCHANT_poll_payment_cancel (struct
+                                    TALER_MERCHANT_PollPaymentOperation *cpo);
+
+
 #endif  /* _TALER_MERCHANT_SERVICE_H */
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 6a5e9bf..3d54d68 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -19,16 +19,17 @@ libtalermerchanttesting_la_LDFLAGS = \
   -no-undefined
 
 libtalermerchant_la_SOURCES = \
+  merchant_api_check_payment.c \
+  merchant_api_history.c \
   merchant_api_proposal.c \
   merchant_api_pay.c \
+  merchant_api_poll_payment.c \
+  merchant_api_refund.c \
   merchant_api_tip_authorize.c \
   merchant_api_tip_pickup.c \
+  merchant_api_tip_query.c \
   merchant_api_track_transaction.c \
-  merchant_api_track_transfer.c \
-  merchant_api_history.c \
-  merchant_api_refund.c \
-  merchant_api_check_payment.c \
-  merchant_api_tip_query.c
+  merchant_api_track_transfer.c
 libtalermerchant_la_LIBADD = \
   -ltalerexchange \
   -ltalercurl \
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);
 }
 
diff --git a/src/lib/merchant_api_check_payment.c 
b/src/lib/merchant_api_poll_payment.c
similarity index 54%
copy from src/lib/merchant_api_check_payment.c
copy to src/lib/merchant_api_poll_payment.c
index 8a85d26..84da443 100644
--- a/src/lib/merchant_api_check_payment.c
+++ b/src/lib/merchant_api_poll_payment.c
@@ -15,8 +15,8 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file lib/merchant_api_check_payment.c
- * @brief Implementation of the /check-payment GET request
+ * @file lib/merchant_api_poll_payment.c
+ * @brief Implementation of the /poll-payment GET request
  * @author Christian Grothoff
  * @author Marcello Stanisci
  * @author Florian Dold
@@ -33,9 +33,9 @@
 
 
 /**
- * @brief A check payment operation handle
+ * @brief A poll payment operation handle
  */
-struct TALER_MERCHANT_CheckPaymentOperation
+struct TALER_MERCHANT_PollPaymentOperation
 {
 
   /**
@@ -51,7 +51,7 @@ struct TALER_MERCHANT_CheckPaymentOperation
   /**
    * Function to call with the result.
    */
-  TALER_MERCHANT_CheckPaymentCallback cb;
+  TALER_MERCHANT_PollPaymentCallback cb;
 
   /**
    * Closure for @a cb.
@@ -66,25 +66,23 @@ struct TALER_MERCHANT_CheckPaymentOperation
 
 
 /**
- * Function called when we're done processing the GET /check-payment request.
+ * Function called when we're done processing the GET /poll-payment request.
  *
- * @param cls the `struct TALER_MERCHANT_CheckPaymentOperation`
+ * @param cls the `struct TALER_MERCHANT_PollPaymentOperation`
  * @param response_code HTTP response code, 0 on error
  * @param json response body, should be NULL
  */
 static void
-handle_check_payment_finished (void *cls,
-                               long response_code,
-                               const void *response)
+handle_poll_payment_finished (void *cls,
+                              long response_code,
+                              const void *response)
 {
-  struct TALER_MERCHANT_CheckPaymentOperation *cpo = cls;
+  struct TALER_MERCHANT_PollPaymentOperation *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 ()
@@ -95,7 +93,7 @@ handle_check_payment_finished (void *cls,
   if (MHD_HTTP_OK != response_code)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Checking payment failed with HTTP status code %u\n",
+                "Polling payment failed with HTTP status code %u\n",
                 (unsigned int) response_code);
     GNUNET_break_op (0);
     cpo->cb (cpo->cb_cls,
@@ -103,9 +101,9 @@ handle_check_payment_finished (void *cls,
              json,
              GNUNET_SYSERR,
              GNUNET_SYSERR,
-             &refund_amount,
+             NULL,
              NULL);
-    TALER_MERCHANT_check_payment_cancel (cpo);
+    TALER_MERCHANT_poll_payment_cancel (cpo);
     return;
   }
 
@@ -116,14 +114,14 @@ handle_check_payment_finished (void *cls,
     if (NULL == taler_pay_uri)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "no taler_pay_uri in unpaid check-payment response\n");
+                  "no taler_pay_uri in unpaid poll-payment response\n");
       GNUNET_break_op (0);
       cpo->cb (cpo->cb_cls,
                0,
                json,
                GNUNET_SYSERR,
                GNUNET_SYSERR,
-               &refund_amount,
+               NULL,
                NULL);
     }
     else
@@ -133,29 +131,32 @@ handle_check_payment_finished (void *cls,
                json,
                GNUNET_NO,
                GNUNET_NO,
-               &refund_amount,
+               NULL,
                taler_pay_uri);
     }
-    TALER_MERCHANT_check_payment_cancel (cpo);
+    TALER_MERCHANT_poll_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");
+                "poll payment failed to parse JSON\n");
     GNUNET_break_op (0);
     cpo->cb (cpo->cb_cls,
              0,
              json,
              GNUNET_SYSERR,
              GNUNET_SYSERR,
-             &refund_amount,
+             NULL,
              NULL);
-    TALER_MERCHANT_check_payment_cancel (cpo);
+    TALER_MERCHANT_poll_payment_cancel (cpo);
     return;
   }
 
@@ -163,49 +164,70 @@ 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);
+  TALER_MERCHANT_poll_payment_cancel (cpo);
 }
 
 
 /**
- * Issue a /check-payment request to the backend.  Checks the status
+ * Issue a /poll-payment request to the backend.  Polls the status
  * of a payment.
  *
  * @param ctx execution context
  * @param backend_url base URL of the merchant backend
  * @param order_id order id to identify the payment
+ * @param h_contract hash of the contract for @a order_id
  * @parem session_id sesion id for the payment (or NULL if the payment is not 
bound to a session)
- * @param check_payment_cb callback which will work the response gotten from 
the backend
- * @param check_payment_cb_cls closure to pass to @a check_payment_cb
+ * @param timeout timeout to use in long polling (how long may the server wait 
to reply
+ *        before generating an unpaid response). Note that this is just 
provided to
+ *        the server, we as client will block until the response comes back or 
until
+ *        #TALER_MERCHANT_poll_payment_cancel() is called.
+ * @param poll_payment_cb callback which will work the response gotten from 
the backend
+ * @param poll_payment_cb_cls closure to pass to @a poll_payment_cb
  * @return handle for this operation, NULL upon errors
  */
-struct TALER_MERCHANT_CheckPaymentOperation *
-TALER_MERCHANT_check_payment (struct GNUNET_CURL_Context *ctx,
-                              const char *backend_url,
-                              const char *order_id,
-                              const char *session_id,
-                              TALER_MERCHANT_CheckPaymentCallback
-                              check_payment_cb,
-                              void *check_payment_cb_cls)
+struct TALER_MERCHANT_PollPaymentOperation *
+TALER_MERCHANT_poll_payment (struct GNUNET_CURL_Context *ctx,
+                             const char *backend_url,
+                             const char *order_id,
+                             const struct GNUNET_HashCode *h_contract,
+                             const char *session_id,
+                             struct GNUNET_TIME_Relative timeout,
+                             TALER_MERCHANT_PollPaymentCallback
+                             poll_payment_cb,
+                             void *poll_payment_cb_cls)
 {
-  struct TALER_MERCHANT_CheckPaymentOperation *cpo;
+  struct TALER_MERCHANT_PollPaymentOperation *cpo;
   CURL *eh;
+  char *h_contract_s;
+  char *timeout_s;
+  unsigned int ts;
 
   GNUNET_assert (NULL != backend_url);
   GNUNET_assert (NULL != order_id);
-
-  cpo = GNUNET_new (struct TALER_MERCHANT_CheckPaymentOperation);
+  h_contract_s = GNUNET_STRINGS_data_to_string_alloc (h_contract,
+                                                      sizeof (*h_contract));
+  ts = (unsigned int) (timeout.rel_value_us
+                       / GNUNET_TIME_UNIT_SECONDS.rel_value_us);
+  GNUNET_asprintf (&timeout_s,
+                   "%u",
+                   ts);
+  cpo = GNUNET_new (struct TALER_MERCHANT_PollPaymentOperation);
   cpo->ctx = ctx;
-  cpo->cb = check_payment_cb;
-  cpo->cb_cls = check_payment_cb_cls;
-  cpo->url = TALER_url_join (backend_url, "check-payment",
+  cpo->cb = poll_payment_cb;
+  cpo->cb_cls = poll_payment_cb_cls;
+  cpo->url = TALER_url_join (backend_url,
+                             "public/poll-payment",
                              "order_id", order_id,
                              "session_id", session_id,
+                             "h_contract", h_contract_s,
+                             (0 != ts) ? "timeout" : NULL,
+                             timeout_s,
                              NULL);
+  GNUNET_free (h_contract_s);
+  GNUNET_free (timeout_s);
   eh = curl_easy_init ();
   if (CURLE_OK != curl_easy_setopt (eh,
                                     CURLOPT_URL,
@@ -216,13 +238,13 @@ TALER_MERCHANT_check_payment (struct GNUNET_CURL_Context 
*ctx,
   }
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "checking payment from %s\n",
+              "polling payment from %s\n",
               cpo->url);
 
   if (NULL == (cpo->job = GNUNET_CURL_job_add (ctx,
                                                eh,
                                                GNUNET_YES,
-                                               &handle_check_payment_finished,
+                                               &handle_poll_payment_finished,
                                                cpo)))
   {
     GNUNET_break (0);
@@ -233,13 +255,13 @@ TALER_MERCHANT_check_payment (struct GNUNET_CURL_Context 
*ctx,
 
 
 /**
- * Cancel a GET /check-payment request.
+ * Cancel a GET /poll-payment request.
  *
  * @param cph handle to the request to be canceled
  */
 void
-TALER_MERCHANT_check_payment_cancel (struct
-                                     TALER_MERCHANT_CheckPaymentOperation *cph)
+TALER_MERCHANT_poll_payment_cancel (struct
+                                    TALER_MERCHANT_PollPaymentOperation *cph)
 {
   if (NULL != cph->job)
   {
@@ -251,4 +273,4 @@ TALER_MERCHANT_check_payment_cancel (struct
 }
 
 
-/* end of merchant_api_check_payment.c */
+/* end of merchant_api_poll_payment.c */

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



reply via email to

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