gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-merchant] 02/02: clean up code duplication in checki


From: gnunet
Subject: [GNUnet-SVN] [taler-merchant] 02/02: clean up code duplication in checking of tipping reserve status by moving shared logic to helper function
Date: Thu, 04 Apr 2019 23:15:00 +0200

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

grothoff pushed a commit to branch master
in repository merchant.

commit 3a38124fa780be01a06bb7d3a3c7d51824f256cf
Author: Christian Grothoff <address@hidden>
AuthorDate: Thu Apr 4 23:14:52 2019 +0200

    clean up code duplication in checking of tipping reserve status by moving 
shared logic to helper function
---
 src/backend/Makefile.am                            |   1 +
 src/backend/taler-merchant-httpd_responses.c       |  44 +--
 src/backend/taler-merchant-httpd_tip-authorize.c   | 226 ++---------
 src/backend/taler-merchant-httpd_tip-query.c       | 418 ++++-----------------
 .../taler-merchant-httpd_tip-reserve-helper.c      | 329 ++++++++++++++++
 .../taler-merchant-httpd_tip-reserve-helper.h      | 137 +++++++
 src/lib/test_merchant_api_new.c                    |   4 +-
 7 files changed, 582 insertions(+), 577 deletions(-)

diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 960dc46..67ba825 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -25,6 +25,7 @@ taler_merchant_httpd_SOURCES = \
   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 \
diff --git a/src/backend/taler-merchant-httpd_responses.c 
b/src/backend/taler-merchant-httpd_responses.c
index 71d0406..19fa9fe 100644
--- a/src/backend/taler-merchant-httpd_responses.c
+++ b/src/backend/taler-merchant-httpd_responses.c
@@ -43,14 +43,14 @@ TMH_RESPONSE_make_json (const json_t *json)
   char *json_str;
 
   json_str = json_dumps (json,
-                        JSON_INDENT(2));
+                         JSON_INDENT(2));
   if (NULL == json_str)
   {
     GNUNET_break (0);
     return NULL;
   }
   resp = MHD_create_response_from_buffer (strlen (json_str),
-                                         json_str,
+                                          json_str,
                                           MHD_RESPMEM_MUST_FREE);
   if (NULL == resp)
   {
@@ -152,9 +152,9 @@ TMH_RESPONSE_reply_json_pack (struct MHD_Connection 
*connection,
 
   va_start (argp, fmt);
   json = json_vpack_ex (&jerror,
-                       0,
-                       fmt,
-                       argp);
+                        0,
+                        fmt,
+                        argp);
   va_end (argp);
   if (NULL == json)
   {
@@ -182,10 +182,10 @@ TMH_RESPONSE_reply_json_pack (struct MHD_Connection 
*connection,
  */
 struct MHD_Response *
 TMH_RESPONSE_make_error (enum TALER_ErrorCode ec,
-                        const char *hint)
+                         const char *hint)
 {
   return TMH_RESPONSE_make_json_pack ("{s:I, s:s}",
-                                     "code", (json_int_t) ec,
+                                      "code", (json_int_t) ec,
                                       "hint", hint);
 }
 
@@ -200,13 +200,13 @@ TMH_RESPONSE_make_error (enum TALER_ErrorCode ec,
  */
 int
 TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection,
-                                  enum TALER_ErrorCode ec,
+                                   enum TALER_ErrorCode ec,
                                    const char *hint)
 {
   return TMH_RESPONSE_reply_json_pack (connection,
                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
                                        "{s:I, s:s}",
-                                      "code", (json_int_t) ec,
+                                       "code", (json_int_t) ec,
                                        "hint", hint);
 }
 
@@ -255,7 +255,7 @@ TMH_RESPONSE_reply_rc (struct MHD_Connection *connection,
   return TMH_RESPONSE_reply_json_pack (connection,
                                        response_code,
                                        "{s:I, s:s}",
-                                      "code", (json_int_t) ec,
+                                       "code", (json_int_t) ec,
                                        "error", msg);
 }
 
@@ -272,7 +272,7 @@ TMH_RESPONSE_reply_invalid_json (struct MHD_Connection 
*connection)
   return TMH_RESPONSE_reply_json_pack (connection,
                                        MHD_HTTP_BAD_REQUEST,
                                        "{s:I, s:s}",
-                                      "code", (json_int_t) 
TALER_EC_JSON_INVALID,
+                                       "code", (json_int_t) 
TALER_EC_JSON_INVALID,
                                        "error", "invalid json");
 }
 
@@ -288,13 +288,13 @@ TMH_RESPONSE_reply_invalid_json (struct MHD_Connection 
*connection)
  */
 int
 TMH_RESPONSE_reply_not_found (struct MHD_Connection *connection,
-                             enum TALER_ErrorCode ec,
+                              enum TALER_ErrorCode ec,
                               const char *object)
 {
   return TMH_RESPONSE_reply_json_pack (connection,
                                        MHD_HTTP_NOT_FOUND,
                                        "{s:I, s:s}",
-                                      "code", (json_int_t) ec,
+                                       "code", (json_int_t) ec,
                                        "error", object);
 }
 
@@ -309,13 +309,13 @@ TMH_RESPONSE_reply_not_found (struct MHD_Connection 
*connection,
  */
 int
 TMH_RESPONSE_reply_bad_request (struct MHD_Connection *connection,
-                               enum TALER_ErrorCode ec,
+                                enum TALER_ErrorCode ec,
                                 const char *issue)
 {
   return TMH_RESPONSE_reply_json_pack (connection,
                                        MHD_HTTP_BAD_REQUEST,
                                        "{s:I, s:s}",
-                                      "code", (json_int_t) ec,
+                                       "code", (json_int_t) ec,
                                        "error", issue);
 }
 
@@ -347,13 +347,13 @@ TMH_RESPONSE_add_global_headers (struct MHD_Response 
*response)
  */
 int
 TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection,
-                                  enum TALER_ErrorCode ec,
+                                   enum TALER_ErrorCode ec,
                                    const char *hint)
 {
   return TMH_RESPONSE_reply_json_pack (connection,
                                        MHD_HTTP_BAD_REQUEST,
                                        "{s:I, s:s}",
-                                      "code", (json_int_t) ec,
+                                       "code", (json_int_t) ec,
                                        "hint", hint);
 }
 
@@ -368,14 +368,14 @@ TMH_RESPONSE_reply_external_error (struct MHD_Connection 
*connection,
  */
 int
 TMH_RESPONSE_reply_arg_missing (struct MHD_Connection *connection,
-                               enum TALER_ErrorCode ec,
+                                enum TALER_ErrorCode ec,
                                 const char *param_name)
 {
   return TMH_RESPONSE_reply_json_pack (connection,
                                        MHD_HTTP_BAD_REQUEST,
                                        "{s:s, s:I, s:s}",
                                        "error", "missing parameter",
-                                      "code", (json_int_t) ec,
+                                       "code", (json_int_t) ec,
                                        "parameter", param_name);
 }
 
@@ -390,14 +390,14 @@ TMH_RESPONSE_reply_arg_missing (struct MHD_Connection 
*connection,
  */
 int
 TMH_RESPONSE_reply_arg_invalid (struct MHD_Connection *connection,
-                               enum TALER_ErrorCode ec,
-                               const char *param_name)
+                                enum TALER_ErrorCode ec,
+                                const char *param_name)
 {
   return TMH_RESPONSE_reply_json_pack (connection,
                                        MHD_HTTP_BAD_REQUEST,
                                        "{s:s, s:I, s:s}",
                                        "error", "invalid parameter",
-                                      "code", (json_int_t) ec,
+                                       "code", (json_int_t) ec,
                                        "parameter", param_name);
 }
 
diff --git a/src/backend/taler-merchant-httpd_tip-authorize.c 
b/src/backend/taler-merchant-httpd_tip-authorize.c
index 9301df9..ad19616 100644
--- a/src/backend/taler-merchant-httpd_tip-authorize.c
+++ b/src/backend/taler-merchant-httpd_tip-authorize.c
@@ -28,6 +28,7 @@
 #include "taler-merchant-httpd_exchanges.h"
 #include "taler-merchant-httpd_responses.h"
 #include "taler-merchant-httpd_tip-authorize.h"
+#include "taler-merchant-httpd_tip-reserve-helper.h"
 
 
 struct TipAuthContext
@@ -44,11 +45,6 @@ struct TipAuthContext
   void *json_parse_context;
 
   /**
-   * HTTP connection we are handling.
-   */
-  struct MHD_Connection *connection;
-
-  /**
    * Merchant instance to use.
    */
   const char *instance;
@@ -74,20 +70,9 @@ struct TipAuthContext
   json_t *root;
 
   /**
-   * Handle to pending /reserve/status request.
-   */
-  struct TALER_EXCHANGE_ReserveStatusHandle *rsh;
-
-  /**
-   * Handle for operation to obtain exchange handle.
-   */
-  struct TMH_EXCHANGES_FindOperation *fo;
-
-  /**
-   * Reserve expiration time as provided by the exchange.
-   * Set in #exchange_cont.
+   * Context for checking the tipping reserve's status.
    */
-  struct GNUNET_TIME_Relative idle_reserve_expiration_time;
+  struct CheckTipReserve ctr;
 
   /**
    * Tip amount requested.
@@ -95,11 +80,6 @@ struct TipAuthContext
   struct TALER_Amount amount;
 
   /**
-   * Private key used by this merchant for the tipping reserve.
-   */
-  struct TALER_ReservePrivateKeyP reserve_priv;
-
-  /**
    * Flag set to #GNUNET_YES when we have tried /reserve/status of the
    * tipping reserve already.
    */
@@ -110,10 +90,6 @@ struct TipAuthContext
    */
   int parsed_json;
 
-  /**
-   * Error code witnessing what the Exchange complained about.
-   */
-  enum TALER_ErrorCode exchange_ec;
 };
 
 
@@ -132,152 +108,13 @@ cleanup_tac (struct TM_HandlerContext *hc)
     json_decref (tac->root);
     tac->root = NULL;
   }
-  if (NULL != tac->rsh)
-  {
-    TALER_EXCHANGE_reserve_status_cancel (tac->rsh);
-    tac->rsh = NULL;
-  }
-  if (NULL != tac->fo)
-  {
-    TMH_EXCHANGES_find_exchange_cancel (tac->fo);
-    tac->fo = NULL;
-  }
+  TMH_check_tip_reserve_cleanup (&tac->ctr);
   TMH_PARSE_post_cleanup_callback (tac->json_parse_context);
   GNUNET_free (tac);
 }
 
 
 /**
- * Function called with the result of the /reserve/status request
- * for the tipping reserve.  Update our database balance with the
- * result.
- *
- * @param cls closure with a `struct TipAuthContext *'
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
- *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param[in] json original response in JSON format (useful only for 
diagnostics)
- * @param balance current balance in the reserve, NULL on error
- * @param history_length number of entries in the transaction history, 0 on 
error
- * @param history detailed transaction history, NULL on error
- */
-static void
-handle_status (void *cls,
-               unsigned int http_status,
-               enum TALER_ErrorCode ec,
-               const json_t *json,
-               const struct TALER_Amount *balance,
-               unsigned int history_length,
-               const struct TALER_EXCHANGE_ReserveHistory *history)
-{
-  struct TipAuthContext *tac = cls;
-
-  tac->rsh = NULL;
-  if (MHD_HTTP_OK != http_status)
-  {
-
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                _("Failed to obtain tipping reserve status from exchange 
(%u/%d)\n"),
-                http_status,
-                ec);
-    tac->exchange_ec = ec;
-    MHD_resume_connection (tac->connection);
-    TMH_trigger_daemon ();
-    return;
-  }
-
-  /* Update DB based on status! */
-  for (unsigned int i=0;i<history_length;i++)
-  {
-    switch (history[i].type)
-    {
-    case TALER_EXCHANGE_RTT_DEPOSIT:
-      {
-        enum GNUNET_DB_QueryStatus qs;
-        struct GNUNET_HashCode uuid;
-        struct GNUNET_TIME_Absolute expiration;
-
-        expiration = GNUNET_TIME_absolute_add 
(history[i].details.in_details.timestamp,
-                                               
tac->idle_reserve_expiration_time);
-        GNUNET_CRYPTO_hash (history[i].details.in_details.wire_reference,
-                            history[i].details.in_details.wire_reference_size,
-                            &uuid);
-        qs = db->enable_tip_reserve_TR (db->cls,
-                                        &tac->reserve_priv,
-                                        &uuid,
-                                        &history[i].amount,
-                                        expiration);
-        if (0 > qs)
-        {
-          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                      _("Database error updating tipping reserve status: 
%d\n"),
-                      qs);
-        }
-      }
-      break;
-    case TALER_EXCHANGE_RTT_WITHDRAWAL:
-      /* expected */
-      break;
-    case TALER_EXCHANGE_RTT_PAYBACK:
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  _("Encountered unsupported /payback operation on tipping 
reserve\n"));
-      break;
-    case TALER_EXCHANGE_RTT_CLOSE:
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  _("Exchange closed reserve (due to expiration), balance 
calulation is likely wrong. Please create a fresh reserve.\n"));
-      break;
-    }
-  }
-  /* Finally, resume processing */
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Resuming HTTP processing\n");
-  MHD_resume_connection (tac->connection);
-  TMH_trigger_daemon ();
-}
-
-
-/**
- * Function called with the result of a #TMH_EXCHANGES_find_exchange()
- * operation.
- *
- * @param cls closure with a `struct TipAuthContext *'
- * @param eh handle to the exchange context
- * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if 
not available
- * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config
- */
-static void
-exchange_cont (void *cls,
-               struct TALER_EXCHANGE_Handle *eh,
-               const struct TALER_Amount *wire_fee,
-               int exchange_trusted)
-{
-  struct TipAuthContext *tac = cls;
-  struct TALER_ReservePublicKeyP reserve_pub;
-  const struct TALER_EXCHANGE_Keys *keys;
-
-  tac->fo = NULL;
-  if (NULL == eh)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                _("Failed to contact exchange configured for tipping!\n"));
-    MHD_resume_connection (tac->connection);
-    TMH_trigger_daemon ();
-    return;
-  }
-  keys = TALER_EXCHANGE_get_keys (eh);
-  GNUNET_assert (NULL != keys);
-  tac->idle_reserve_expiration_time
-    = keys->reserve_closing_delay;
-  GNUNET_CRYPTO_eddsa_key_get_public (&tac->reserve_priv.eddsa_priv,
-                                      &reserve_pub.eddsa_pub);
-  tac->rsh = TALER_EXCHANGE_reserve_status (eh,
-                                            &reserve_pub,
-                                            &handle_status,
-                                            tac);
-}
-
-
-/**
  * Handle a "/tip-authorize" request.
  *
  * @param rh context of the handler
@@ -305,14 +142,22 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
   {
     tac = GNUNET_new (struct TipAuthContext);
     tac->hc.cc = &cleanup_tac;
-    tac->connection = connection;
+    tac->ctr.connection = connection;
     *connection_cls = tac;
   }
   else
   {
     tac = *connection_cls;
   }
-
+  if (NULL != tac->ctr.response)
+  {
+    res = MHD_queue_response (connection,
+                              tac->ctr.response_code,
+                              tac->ctr.response);
+    MHD_destroy_response (tac->ctr.response);
+    tac->ctr.response = NULL;
+    return res;
+  }
   if (GNUNET_NO == tac->parsed_json)
   {
     struct GNUNET_JSON_Specification spec[] = {
@@ -336,13 +181,16 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
          (NULL == tac->root) )
       return MHD_YES;
 
-    if (NULL == json_object_get (tac->root, "pickup_url"))
+    if (NULL == json_object_get (tac->root,
+                                 "pickup_url"))
     {
       char *pickup_url = TALER_url_absolute_mhd (connection,
                                                  "/public/tip-pickup",
                                                  NULL);
       GNUNET_assert (NULL != pickup_url);
-      json_object_set_new (tac->root, "pickup_url", json_string (pickup_url));
+      json_object_set_new (tac->root,
+                           "pickup_url",
+                           json_string (pickup_url));
       GNUNET_free (pickup_url);
     }
 
@@ -364,8 +212,8 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
                 "Instance `%s' not configured\n",
                 tac->instance);
     return TMH_RESPONSE_reply_not_found (connection,
-                                        
TALER_EC_TIP_AUTHORIZE_INSTANCE_UNKNOWN,
-                                        "unknown instance");
+                                         
TALER_EC_TIP_AUTHORIZE_INSTANCE_UNKNOWN,
+                                         "unknown instance");
   }
   if (NULL == mi->tip_exchange)
   {
@@ -373,10 +221,10 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
                 "Instance `%s' not configured for tipping\n",
                 tac->instance);
     return TMH_RESPONSE_reply_not_found (connection,
-                                        
TALER_EC_TIP_AUTHORIZE_INSTANCE_DOES_NOT_TIP,
-                                        "exchange for tipping not configured 
for the instance");
+                                         
TALER_EC_TIP_AUTHORIZE_INSTANCE_DOES_NOT_TIP,
+                                         "exchange for tipping not configured 
for the instance");
   }
-  tac->reserve_priv = mi->tip_reserve;
+  tac->ctr.reserve_priv = mi->tip_reserve;
   ec = db->authorize_tip_TR (db->cls,
                              tac->justification,
                              &tac->amount,
@@ -390,17 +238,15 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
   if ( (TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS == ec) &&
        (GNUNET_NO == tac->checked_status) )
   {
-    MHD_suspend_connection (connection);
     tac->checked_status = GNUNET_YES;
-    tac->fo = TMH_EXCHANGES_find_exchange (mi->tip_exchange,
-                                           NULL,
-                                           &exchange_cont,
-                                           tac);
+    tac->ctr.none_authorized = GNUNET_YES;
+    TMH_check_tip_reserve (&tac->ctr,
+                           mi->tip_exchange);
     return MHD_YES;
   }
 
   /* handle irrecoverable errors */
-  if (TALER_EC_NONE != (ec | tac->exchange_ec))
+  if (TALER_EC_NONE != ec)
   {
     unsigned int rc;
     const char *msg;
@@ -425,22 +271,6 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
       break;
     }
 
-    /* If the exchange complained earlier, we do
-     * override what the database returned.  */
-    switch (tac->exchange_ec)
-    {
-    case TALER_EC_RESERVE_STATUS_UNKNOWN:
-      rc = MHD_HTTP_NOT_FOUND;
-      msg = "Exchange does not find any reserve having this key";
-      /* We override what the DB returned, as an exchange error
-       * is more important.  */
-      ec = TALER_EC_TIP_AUTHORIZE_RESERVE_UNKNOWN; 
-      break;
-    default:
-      /* This makes the compiler silent.  */
-      break;
-    }
-
     return TMH_RESPONSE_reply_rc (connection,
                                   rc,
                                   ec,
diff --git a/src/backend/taler-merchant-httpd_tip-query.c 
b/src/backend/taler-merchant-httpd_tip-query.c
index 0f99e49..b63f3d2 100644
--- a/src/backend/taler-merchant-httpd_tip-query.c
+++ b/src/backend/taler-merchant-httpd_tip-query.c
@@ -29,6 +29,7 @@
 #include "taler-merchant-httpd_exchanges.h"
 #include "taler-merchant-httpd_responses.h"
 #include "taler-merchant-httpd_tip-query.h"
+#include "taler-merchant-httpd_tip-reserve-helper.h"
 
 
 /**
@@ -46,75 +47,15 @@ struct TipQueryContext
   struct TM_HandlerContext hc;
 
   /**
-   * HTTP connection we are handling.
-   */
-  struct MHD_Connection *connection;
-
-  /**
    * Merchant instance to use.
    */
   const char *instance;
 
   /**
-   * Handle to pending /reserve/status request.
-   */
-  struct TALER_EXCHANGE_ReserveStatusHandle *rsh;
-
-  /**
-   * Handle for operation to obtain exchange handle.
-   */
-  struct TMH_EXCHANGES_FindOperation *fo;
-
-  /**
-   * Reserve expiration time as provided by the exchange.
-   * Set in #exchange_cont.
-   */
-  struct GNUNET_TIME_Relative idle_reserve_expiration_time;
-
-  /**
-   * Tip amount requested.
-   */
-  struct TALER_Amount amount_deposited;
-
-  /**
-   * Tip amount requested.
-   */
-  struct TALER_Amount amount_withdrawn;
-
-  /**
-   * Amount authorized.
-   */
-  struct TALER_Amount amount_authorized;
-
-  /**
-   * Private key used by this merchant for the tipping reserve.
-   */
-  struct TALER_ReservePrivateKeyP reserve_priv;
-
-  /**
-   * No tips were authorized yet.
-   */
-  int none_authorized;
-
-  /**
-   * Response to return, NULL if we don't have one yet.
-   */
-  struct MHD_Response *response;
-
-  /**
-   * HTTP status code to use for the reply, i.e 200 for "OK".
-   * Special value UINT_MAX is used to indicate hard errors
-   * (no reply, return #MHD_NO).
+   * Context for checking the tipping reserve's status.
    */
-  unsigned int response_code;
+  struct CheckTipReserve ctr;
 
-  /**
-   * #GNUNET_NO if the @e connection was not suspended,
-   * #GNUNET_YES if the @e connection was suspended,
-   * #GNUNET_SYSERR if @e connection was resumed to as
-   * part of #MH_force_pc_resume during shutdown.
-   */
-  int suspended;
 };
 
 
@@ -128,288 +69,52 @@ cleanup_tqc (struct TM_HandlerContext *hc)
 {
   struct TipQueryContext *tqc = (struct TipQueryContext *) hc;
 
-  if (NULL != tqc->rsh)
-  {
-    TALER_EXCHANGE_reserve_status_cancel (tqc->rsh);
-    tqc->rsh = NULL;
-  }
-  if (NULL != tqc->fo)
-  {
-    TMH_EXCHANGES_find_exchange_cancel (tqc->fo);
-    tqc->fo = NULL;
-  }
+  TMH_check_tip_reserve_cleanup (&tqc->ctr);
   GNUNET_free (tqc);
 }
 
 
 /**
- * Resume the given context and send the given response.  Stores the response
- * in the @a pc and signals MHD to resume the connection.  Also ensures MHD
- * runs immediately.
+ * We've been resumed after processing the reserve data from the
+ * exchange without error. Generate the final response.
  *
- * @param pc payment context
- * @param response_code response code to use
- * @param response response data to send back
+ * @param tqc context for which to generate the response.
  */
-static void
-resume_with_response (struct TipQueryContext *tqc,
-                      unsigned int response_code,
-                      struct MHD_Response *response)
+static int
+generate_final_response (struct TipQueryContext *tqc)
 {
-  tqc->response_code = response_code;
-  tqc->response = response;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Resuming /tip-query response (%u)\n",
-              response_code);
-  GNUNET_assert (GNUNET_YES == tqc->suspended);
-  tqc->suspended = GNUNET_NO;
-  MHD_resume_connection (tqc->connection);
-  TMH_trigger_daemon (); /* we resumed, kick MHD */
-}
-
-
-/**
- * Function called with the result of the /reserve/status request
- * for the tipping reserve.  Update our database balance with the
- * result.
- *
- * @param cls closure with a `struct TipAuthContext *'
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
- *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
- * @param ec taler-specific error code, #TALER_EC_NONE on success
- * @param[in] json original response in JSON format (useful only for 
diagnostics)
- * @param balance current balance in the reserve, NULL on error
- * @param history_length number of entries in the transaction history, 0 on 
error
- * @param history detailed transaction history, NULL on error
- */
-static void
-handle_status (void *cls,
-               unsigned int http_status,
-               enum TALER_ErrorCode ec,
-               const json_t *json,
-               const struct TALER_Amount *balance,
-               unsigned int history_length,
-               const struct TALER_EXCHANGE_ReserveHistory *history)
-{
-  struct TipQueryContext *tqc = cls;
-  struct GNUNET_TIME_Absolute reserve_expiration = GNUNET_TIME_UNIT_ZERO_ABS;
-
-  tqc->rsh = NULL;
-  if (MHD_HTTP_OK != http_status)
-  {
-    GNUNET_break_op (0);
-    resume_with_response (tqc,
-                          MHD_HTTP_SERVICE_UNAVAILABLE,
-                          TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_STATUS_FAILED_EXCHANGE_DOWN,
-                                                   "Unable to obtain reserve 
status from exchange"));
-    return;
-  }
-
-  if (0 == history_length)
-  {
-    GNUNET_break_op (0);
-    resume_with_response (tqc,
-                          MHD_HTTP_SERVICE_UNAVAILABLE,
-                          TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_HISTORY_FAILED_EMPTY,
-                                                   "Exchange returned empty 
reserve history"));
-    return;
-  }
-
-  if (TALER_EXCHANGE_RTT_DEPOSIT != history[0].type)
-  {
-    GNUNET_break_op (0);
-    resume_with_response (tqc,
-                          MHD_HTTP_SERVICE_UNAVAILABLE,
-                          TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_HISTORY_INVALID_NO_DEPOSIT,
-                                                   "Exchange returned invalid 
reserve history"));
-    return;
-  }
+  struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
+  struct TALER_Amount amount_available;
 
-  if (GNUNET_OK !=
-      TALER_amount_get_zero (history[0].amount.currency,
-                             &tqc->amount_withdrawn))
+  GNUNET_CRYPTO_eddsa_key_get_public (&tqc->ctr.reserve_priv.eddsa_priv,
+                                      &reserve_pub);
+  if (GNUNET_SYSERR ==
+      TALER_amount_subtract (&amount_available,
+                             &tqc->ctr.amount_deposited,
+                             &tqc->ctr.amount_withdrawn))
   {
     GNUNET_break_op (0);
-    resume_with_response (tqc,
-                          MHD_HTTP_SERVICE_UNAVAILABLE,
-                          TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_HISTORY_INVALID_CURRENCY,
-                                                   "Exchange returned invalid 
reserve history"));
-    return;
-  }
-
-  if (0 != strcasecmp (TMH_currency,
-                       history[0].amount.currency))
-  {
-    GNUNET_break_op (0);
-    resume_with_response (tqc,
-                          MHD_HTTP_SERVICE_UNAVAILABLE,
-                          TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_CURRENCY_MISSMATCH,
-                                                   "Exchange currency 
unexpected"));
-    return;
-  }
-
-  if (GNUNET_YES == tqc->none_authorized)
-    tqc->amount_authorized = tqc->amount_withdrawn;
-  tqc->amount_deposited = tqc->amount_withdrawn;
-
-  /* Update DB based on status! */
-  for (unsigned int i=0;i<history_length;i++)
-  {
-    switch (history[i].type)
-    {
-    case TALER_EXCHANGE_RTT_DEPOSIT:
-      {
-        enum GNUNET_DB_QueryStatus qs;
-        struct GNUNET_HashCode uuid;
-        struct GNUNET_TIME_Absolute deposit_expiration;
-
-        deposit_expiration = GNUNET_TIME_absolute_add 
(history[i].details.in_details.timestamp,
-                                                       
tqc->idle_reserve_expiration_time);
-        /* We're interested in the latest DEPOSIT timestamp, since this 
determines the
-         * reserve's expiration date. Note that the history isn't 
chronologically ordered. */
-        reserve_expiration = GNUNET_TIME_absolute_max (reserve_expiration, 
deposit_expiration);
-        GNUNET_CRYPTO_hash (history[i].details.in_details.wire_reference,
-                            history[i].details.in_details.wire_reference_size,
-                            &uuid);
-        qs = db->enable_tip_reserve_TR (db->cls,
-                                        &tqc->reserve_priv,
-                                        &uuid,
-                                        &history[i].amount,
-                                        deposit_expiration);
-        if (GNUNET_OK !=
-            TALER_amount_add (&tqc->amount_deposited,
-                              &tqc->amount_deposited,
-                              &history[i].amount))
-        {
-          GNUNET_break_op (0);
-          resume_with_response (tqc,
-                                MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_DEPOSIT,
-                                                         "Exchange returned 
invalid reserve history (amount overflow)"));
-          return;
-        }
-
-        if (0 > qs)
-        {
-          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                      _("Database error updating tipping reserve status: 
%d\n"),
-                      qs);
-        }
-      }
-      break;
-    case TALER_EXCHANGE_RTT_WITHDRAWAL:
-      if (GNUNET_OK !=
-          TALER_amount_add (&tqc->amount_withdrawn,
-                            &tqc->amount_withdrawn,
-                            &history[i].amount))
-      {
-        GNUNET_break_op (0);
-        resume_with_response (tqc,
-                              MHD_HTTP_INTERNAL_SERVER_ERROR,
-                              TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_WITHDRAW,
-                                                       "Exchange returned 
invalid reserve history (amount overflow)"));
-        return;
-      }
-      break;
-    case TALER_EXCHANGE_RTT_PAYBACK:
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  _("Encountered unsupported /payback operation on tipping 
reserve\n"));
-      break;
-    case TALER_EXCHANGE_RTT_CLOSE:
-      /* We count 'closing' amounts just like withdrawals */
-      if (GNUNET_OK !=
-          TALER_amount_add (&tqc->amount_withdrawn,
-                            &tqc->amount_withdrawn,
-                            &history[i].amount))
-      {
-        GNUNET_break_op (0);
-        resume_with_response (tqc,
-                              MHD_HTTP_INTERNAL_SERVER_ERROR,
-                              TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_CLOSED,
-                                                       "Exchange returned 
invalid reserve history (amount overflow)"));
-        return;
-      }
-      break;
-    }
-  }
-
-  {
-    struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
-    struct TALER_Amount amount_available;
-
-    GNUNET_CRYPTO_eddsa_key_get_public (&tqc->reserve_priv.eddsa_priv,
-                                        &reserve_pub);
-    if (GNUNET_SYSERR ==
-        TALER_amount_subtract (&amount_available,
-                               &tqc->amount_deposited,
-                               &tqc->amount_withdrawn))
-    {
-        GNUNET_break_op (0);
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "amount overflow, deposited %s but withdrawn %s\n",
-                    TALER_amount_to_string (&tqc->amount_deposited),
-                    TALER_amount_to_string (&tqc->amount_withdrawn));
-
-        resume_with_response (tqc,
-                              MHD_HTTP_INTERNAL_SERVER_ERROR,
-                              TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_INCONSISTENT,
-                                                       "Exchange returned 
invalid reserve history (amount overflow)"));
-    }
-    resume_with_response (tqc,
-                          MHD_HTTP_OK,
-                          TMH_RESPONSE_make_json_pack ("{s:o, s:o, s:o, s:o, 
s:o}",
-                                                       "reserve_pub",
-                                                       
GNUNET_JSON_from_data_auto (&reserve_pub),
-                                                       "reserve_expiration",
-                                                       
GNUNET_JSON_from_time_abs (reserve_expiration),
-                                                       "amount_authorized",
-                                                       TALER_JSON_from_amount 
(&tqc->amount_authorized),
-                                                       "amount_picked_up",
-                                                       TALER_JSON_from_amount 
(&tqc->amount_withdrawn),
-                                                       "amount_available",
-                                                       TALER_JSON_from_amount 
(&amount_available)));
-  }
-}
-
-
-/**
- * Function called with the result of a #TMH_EXCHANGES_find_exchange()
- * operation.
- *
- * @param cls closure with a `struct TipQueryContext *`
- * @param eh handle to the exchange context
- * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if 
not available
- * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config
- */
-static void
-exchange_cont (void *cls,
-               struct TALER_EXCHANGE_Handle *eh,
-               const struct TALER_Amount *wire_fee,
-               int exchange_trusted)
-{
-  struct TipQueryContext *tqc = cls;
-  struct TALER_ReservePublicKeyP reserve_pub;
-  const struct TALER_EXCHANGE_Keys *keys;
-
-  tqc->fo = NULL;
-  if (NULL == eh)
-  {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                _("Failed to contact exchange configured for tipping!\n"));
-    MHD_resume_connection (tqc->connection);
-    TMH_trigger_daemon ();
-    return;
-  }
-  keys = TALER_EXCHANGE_get_keys (eh);
-  GNUNET_assert (NULL != keys);
-  tqc->idle_reserve_expiration_time
-    = keys->reserve_closing_delay;
-  GNUNET_CRYPTO_eddsa_key_get_public (&tqc->reserve_priv.eddsa_priv,
-                                      &reserve_pub.eddsa_pub);
-  tqc->rsh = TALER_EXCHANGE_reserve_status (eh,
-                                            &reserve_pub,
-                                            &handle_status,
-                                            tqc);
+                "amount overflow, deposited %s but withdrawn %s\n",
+                TALER_amount_to_string (&tqc->ctr.amount_deposited),
+                TALER_amount_to_string (&tqc->ctr.amount_withdrawn));
+    return TMH_RESPONSE_reply_internal_error (tqc->ctr.connection,
+                                              
TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_INCONSISTENT,
+                                              "Exchange returned invalid 
reserve history (amount overflow)");
+  }
+  return TMH_RESPONSE_reply_json_pack (tqc->ctr.connection,
+                                       MHD_HTTP_OK,
+                                       "{s:o, s:o, s:o, s:o, s:o}",
+                                       "reserve_pub",
+                                       GNUNET_JSON_from_data_auto 
(&reserve_pub),
+                                       "reserve_expiration",
+                                       GNUNET_JSON_from_time_abs 
(tqc->ctr.reserve_expiration),
+                                       "amount_authorized",
+                                       TALER_JSON_from_amount 
(&tqc->ctr.amount_authorized),
+                                       "amount_picked_up",
+                                       TALER_JSON_from_amount 
(&tqc->ctr.amount_withdrawn),
+                                       "amount_available",
+                                       TALER_JSON_from_amount 
(&amount_available));
 }
 
 
@@ -438,7 +143,7 @@ MH_handler_tip_query (struct TMH_RequestHandler *rh,
   {
     tqc = GNUNET_new (struct TipQueryContext);
     tqc->hc.cc = &cleanup_tqc;
-    tqc->connection = connection;
+    tqc->ctr.connection = connection;
     *connection_cls = tqc;
   }
   else
@@ -446,26 +151,34 @@ MH_handler_tip_query (struct TMH_RequestHandler *rh,
     tqc = *connection_cls;
   }
 
-  if (0 != tqc->response_code)
+  if (0 != tqc->ctr.response_code)
   {
     /* We are *done* processing the request, just queue the response (!) */
-    if (UINT_MAX == tqc->response_code)
+    if (UINT_MAX == tqc->ctr.response_code)
     {
       GNUNET_break (0);
       return MHD_NO; /* hard error */
     }
     res = MHD_queue_response (connection,
-                              tqc->response_code,
-                              tqc->response);
-    MHD_destroy_response (tqc->response);
-    tqc->response = NULL;
+                              tqc->ctr.response_code,
+                              tqc->ctr.response);
+    MHD_destroy_response (tqc->ctr.response);
+    tqc->ctr.response = NULL;
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-               "Queueing response (%u) for /tip-query (%s).\n",
-               (unsigned int) tqc->response_code,
-               res ? "OK" : "FAILED");
+                "Queueing response (%u) for /tip-query (%s).\n",
+                (unsigned int) tqc->ctr.response_code,
+                res ? "OK" : "FAILED");
     return res;
   }
 
+  if (NULL != tqc->instance)
+  {
+    /* We've been here before, so TMH_check_tip_reserve() must have
+       finished and left the result for us. Finish processing. */
+    return generate_final_response (tqc);
+  }
+
+  /* No error yet, so first time here, let's query the exchange */
   tqc->instance = MHD_lookup_connection_value (connection,
                                                MHD_GET_ARGUMENT_KIND,
                                                "instance");
@@ -481,8 +194,8 @@ MH_handler_tip_query (struct TMH_RequestHandler *rh,
                 "Instance `%s' not configured\n",
                 tqc->instance);
     return TMH_RESPONSE_reply_not_found (connection,
-                                        
TALER_EC_TIP_AUTHORIZE_INSTANCE_UNKNOWN,
-                                        "unknown instance");
+                                         
TALER_EC_TIP_AUTHORIZE_INSTANCE_UNKNOWN,
+                                         "unknown instance");
   }
   if (NULL == mi->tip_exchange)
   {
@@ -493,7 +206,7 @@ MH_handler_tip_query (struct TMH_RequestHandler *rh,
                                          
TALER_EC_TIP_AUTHORIZE_INSTANCE_DOES_NOT_TIP,
                                          "exchange for tipping not configured 
for the instance");
   }
-  tqc->reserve_priv = mi->tip_reserve;
+  tqc->ctr.reserve_priv = mi->tip_reserve;
 
   {
     int qs;
@@ -501,8 +214,8 @@ MH_handler_tip_query (struct TMH_RequestHandler *rh,
     {
       db->preflight (db->cls);
       qs = db->get_authorized_tip_amount (db->cls,
-                                          &tqc->reserve_priv,
-                                          &tqc->amount_authorized);
+                                          &tqc->ctr.reserve_priv,
+                                          &tqc->ctr.amount_authorized);
       if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
         break;
     }
@@ -518,17 +231,12 @@ MH_handler_tip_query (struct TMH_RequestHandler *rh,
     {
       /* we'll set amount_authorized to zero later once
          we know the currency */
-      tqc->none_authorized = GNUNET_YES;
+      tqc->ctr.none_authorized = GNUNET_YES;
     }
   }
 
-  MHD_suspend_connection (connection);
-  tqc->suspended = GNUNET_YES;
-
-  tqc->fo = TMH_EXCHANGES_find_exchange (mi->tip_exchange,
-                                         NULL,
-                                         &exchange_cont,
-                                         tqc);
+  TMH_check_tip_reserve (&tqc->ctr,
+                         mi->tip_exchange);
   return MHD_YES;
 }
 
diff --git a/src/backend/taler-merchant-httpd_tip-reserve-helper.c 
b/src/backend/taler-merchant-httpd_tip-reserve-helper.c
new file mode 100644
index 0000000..dc16195
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_tip-reserve-helper.c
@@ -0,0 +1,329 @@
+/*
+  This file is part of TALER
+  (C) 2018--2019 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file backend/taler-merchant-httpd_tip-reserve-helper.c
+ * @brief helper functions to check the status of a tipping reserve
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_tip-reserve-helper.h"
+
+
+/**
+ * Resume the given context and send the given response.  Stores the response
+ * in the @a ctr and signals MHD to resume the connection.  Also ensures MHD
+ * runs immediately.
+ *
+ * @param ctr tip reserve query helper context
+ * @param response_code response code to use
+ * @param response response data to send back
+ */
+static void
+resume_with_response (struct CheckTipReserve *ctr,
+                      unsigned int response_code,
+                      struct MHD_Response *response)
+{
+  ctr->response_code = response_code;
+  ctr->response = response;
+  GNUNET_assert (GNUNET_YES == ctr->suspended);
+  ctr->suspended = GNUNET_NO;
+  MHD_resume_connection (ctr->connection);
+  TMH_trigger_daemon (); /* we resumed, kick MHD */
+}
+
+
+/**
+ * Function called with the result of the /reserve/status request
+ * for the tipping reserve.  Update our database balance with the
+ * result.
+ *
+ * @param cls closure with a `struct CheckTipReserve *'
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
+ *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
+ * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param[in] json original response in JSON format (useful only for 
diagnostics)
+ * @param balance current balance in the reserve, NULL on error
+ * @param history_length number of entries in the transaction history, 0 on 
error
+ * @param history detailed transaction history, NULL on error
+ */
+static void
+handle_status (void *cls,
+               unsigned int http_status,
+               enum TALER_ErrorCode ec,
+               const json_t *json,
+               const struct TALER_Amount *balance,
+               unsigned int history_length,
+               const struct TALER_EXCHANGE_ReserveHistory *history)
+{
+  struct CheckTipReserve *ctr = cls;
+
+  ctr->rsh = NULL;
+  ctr->reserve_expiration = GNUNET_TIME_UNIT_ZERO_ABS;
+  if (MHD_HTTP_NOT_FOUND == http_status)
+  {
+    resume_with_response (ctr,
+                          MHD_HTTP_NOT_FOUND,
+                          TMH_RESPONSE_make_error (ec,
+                                                   "Reserve unknown at 
exchange"));
+    return;
+  }
+  if (MHD_HTTP_OK != http_status)
+  {
+    GNUNET_break_op (0);
+    resume_with_response (ctr,
+                          MHD_HTTP_SERVICE_UNAVAILABLE,
+                          TMH_RESPONSE_make_error (ec,
+                                                   "Exchange returned error 
code for reserve status"));
+    return;
+  }
+
+  if (0 == history_length)
+  {
+    GNUNET_break_op (0);
+    resume_with_response (ctr,
+                          MHD_HTTP_SERVICE_UNAVAILABLE,
+                          TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_HISTORY_FAILED_EMPTY,
+                                                   "Exchange returned empty 
reserve history"));
+    return;
+  }
+
+  if (TALER_EXCHANGE_RTT_DEPOSIT != history[0].type)
+  {
+    GNUNET_break_op (0);
+    resume_with_response (ctr,
+                          MHD_HTTP_SERVICE_UNAVAILABLE,
+                          TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_HISTORY_INVALID_NO_DEPOSIT,
+                                                   "Exchange returned invalid 
reserve history"));
+    return;
+  }
+
+  if (GNUNET_OK !=
+      TALER_amount_get_zero (history[0].amount.currency,
+                             &ctr->amount_withdrawn))
+  {
+    GNUNET_break_op (0);
+    resume_with_response (ctr,
+                          MHD_HTTP_SERVICE_UNAVAILABLE,
+                          TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_HISTORY_INVALID_CURRENCY,
+                                                   "Exchange returned invalid 
reserve history"));
+    return;
+  }
+
+  if (0 != strcasecmp (TMH_currency,
+                       history[0].amount.currency))
+  {
+    GNUNET_break_op (0);
+    resume_with_response (ctr,
+                          MHD_HTTP_SERVICE_UNAVAILABLE,
+                          TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_CURRENCY_MISSMATCH,
+                                                   "Exchange currency 
unexpected"));
+    return;
+  }
+
+  if (GNUNET_YES == ctr->none_authorized)
+    ctr->amount_authorized = ctr->amount_withdrawn;
+  ctr->amount_deposited = ctr->amount_withdrawn;
+
+  /* Update DB based on status! */
+  for (unsigned int i=0;i<history_length;i++)
+  {
+    switch (history[i].type)
+    {
+    case TALER_EXCHANGE_RTT_DEPOSIT:
+      {
+        enum GNUNET_DB_QueryStatus qs;
+        struct GNUNET_HashCode uuid;
+        struct GNUNET_TIME_Absolute deposit_expiration;
+
+        deposit_expiration = GNUNET_TIME_absolute_add 
(history[i].details.in_details.timestamp,
+                                                       
ctr->idle_reserve_expiration_time);
+        /* We're interested in the latest DEPOSIT timestamp, since this 
determines the
+         * reserve's expiration date. Note that the history isn't 
chronologically ordered. */
+        ctr->reserve_expiration = GNUNET_TIME_absolute_max 
(ctr->reserve_expiration,
+                                                            
deposit_expiration);
+        GNUNET_CRYPTO_hash (history[i].details.in_details.wire_reference,
+                            history[i].details.in_details.wire_reference_size,
+                            &uuid);
+        qs = db->enable_tip_reserve_TR (db->cls,
+                                        &ctr->reserve_priv,
+                                        &uuid,
+                                        &history[i].amount,
+                                        deposit_expiration);
+        if (GNUNET_OK !=
+            TALER_amount_add (&ctr->amount_deposited,
+                              &ctr->amount_deposited,
+                              &history[i].amount))
+        {
+          GNUNET_break_op (0);
+          resume_with_response (ctr,
+                                MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_DEPOSIT,
+                                                         "Exchange returned 
invalid reserve history (amount overflow)"));
+          return;
+        }
+
+        if (0 > qs)
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      _("Database error updating tipping reserve status: 
%d\n"),
+                      qs);
+        }
+      }
+      break;
+    case TALER_EXCHANGE_RTT_WITHDRAWAL:
+      if (GNUNET_OK !=
+          TALER_amount_add (&ctr->amount_withdrawn,
+                            &ctr->amount_withdrawn,
+                            &history[i].amount))
+      {
+        GNUNET_break_op (0);
+        resume_with_response (ctr,
+                              MHD_HTTP_INTERNAL_SERVER_ERROR,
+                              TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_WITHDRAW,
+                                                       "Exchange returned 
invalid reserve history (amount overflow)"));
+        return;
+      }
+      break;
+    case TALER_EXCHANGE_RTT_PAYBACK:
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  _("Encountered unsupported /payback operation on tipping 
reserve\n"));
+      /* FIXME: probably should count these like deposits!? */
+      break;
+    case TALER_EXCHANGE_RTT_CLOSE:
+      /* We count 'closing' amounts just like withdrawals */
+      if (GNUNET_OK !=
+          TALER_amount_add (&ctr->amount_withdrawn,
+                            &ctr->amount_withdrawn,
+                            &history[i].amount))
+      {
+        GNUNET_break_op (0);
+        resume_with_response (ctr,
+                              MHD_HTTP_INTERNAL_SERVER_ERROR,
+                              TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_HISTORY_ARITHMETIC_ISSUE_CLOSED,
+                                                       "Exchange returned 
invalid reserve history (amount overflow)"));
+        return;
+      }
+      break;
+    }
+  }
+
+  /* normal, non-error continuation */
+  resume_with_response (ctr,
+                        0,
+                        NULL);
+}
+
+
+/**
+ * Function called with the result of a #TMH_EXCHANGES_find_exchange()
+ * operation.  Given the exchange handle, we will then interrogate
+ * the exchange about the status of the tipping reserve.
+ *
+ * @param cls closure with a `struct CheckTipReserve *`
+ * @param eh handle to the exchange context
+ * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if 
not available
+ * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config
+ */
+static void
+exchange_cont (void *cls,
+               struct TALER_EXCHANGE_Handle *eh,
+               const struct TALER_Amount *wire_fee,
+               int exchange_trusted)
+{
+  struct CheckTipReserve *ctr = cls;
+  struct TALER_ReservePublicKeyP reserve_pub;
+  const struct TALER_EXCHANGE_Keys *keys;
+
+  ctr->fo = NULL;
+  if (NULL == eh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to contact exchange configured for tipping!\n"));
+    resume_with_response (ctr,
+                          MHD_HTTP_SERVICE_UNAVAILABLE,
+                          TMH_RESPONSE_make_error 
(TALER_EC_TIP_QUERY_RESERVE_STATUS_FAILED_EXCHANGE_DOWN,
+                                                   "Unable to obtain /keys 
from exchange"));
+    return;
+  }
+  keys = TALER_EXCHANGE_get_keys (eh);
+  GNUNET_assert (NULL != keys);
+  ctr->idle_reserve_expiration_time
+    = keys->reserve_closing_delay;
+  GNUNET_CRYPTO_eddsa_key_get_public (&ctr->reserve_priv.eddsa_priv,
+                                      &reserve_pub.eddsa_pub);
+  ctr->rsh = TALER_EXCHANGE_reserve_status (eh,
+                                            &reserve_pub,
+                                            &handle_status,
+                                            ctr);
+}
+
+
+/**
+ * Check the status of the given reserve at the given exchange.
+ * Suspends the MHD connection while this is happening and resumes
+ * processing once we know the reserve status (or once an error
+ * code has been determined).
+ *
+ * @param[in,out] ctr context for checking the reserve status
+ * @param tip_exchange the URL of the exchange to query
+ */
+void
+TMH_check_tip_reserve (struct CheckTipReserve *ctr,
+                       const char *tip_exchange)
+{
+  MHD_suspend_connection (ctr->connection);
+  ctr->suspended = GNUNET_YES;
+  ctr->fo = TMH_EXCHANGES_find_exchange (tip_exchange,
+                                         NULL,
+                                         &exchange_cont,
+                                         ctr);
+  if (NULL == ctr->fo)
+  {
+    GNUNET_break (0);
+    resume_with_response (ctr,
+                          MHD_HTTP_INTERNAL_SERVER_ERROR,
+                          TMH_RESPONSE_make_error 
(TALER_EC_INTERNAL_INVARIANT_FAILURE,
+                                                   "Unable to find exchange 
handle"));
+  }
+}
+
+
+/**
+ * Clean up any state that might be left in @a ctr.
+ *
+ * @param[in] context to clean up
+ */
+void
+TMH_check_tip_reserve_cleanup (struct CheckTipReserve *ctr)
+{
+  if (NULL != ctr->rsh)
+  {
+    TALER_EXCHANGE_reserve_status_cancel (ctr->rsh);
+    ctr->rsh = NULL;
+  }
+  if (NULL != ctr->fo)
+  {
+    TMH_EXCHANGES_find_exchange_cancel (ctr->fo);
+    ctr->fo = NULL;
+  }
+  if (NULL != ctr->response)
+  {
+    MHD_destroy_response (ctr->response);
+    ctr->response = NULL;
+  }
+}
+
+/* end of taler-merchant-httpd_tip-reserve-helper.c */
diff --git a/src/backend/taler-merchant-httpd_tip-reserve-helper.h 
b/src/backend/taler-merchant-httpd_tip-reserve-helper.h
new file mode 100644
index 0000000..8726b54
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_tip-reserve-helper.h
@@ -0,0 +1,137 @@
+/*
+  This file is part of TALER
+  (C) 2018--2019 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file backend/taler-merchant-httpd_tip-reserve-helper.h
+ * @brief helper functions to check the status of a tipping reserve
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_TIP_RESERVE_HELPER_H
+#define TALER_MERCHANT_HTTPD_TIP_RESERVE_HELPER_H
+#include <jansson.h>
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.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_tip-reserve-helper.h"
+
+
+/**
+ * Context with input, output and internal state for
+ * #TMH_check_tip_reserve() and #TMH_check_tip_reserve_cleanup().
+ */
+struct CheckTipReserve
+{
+  /**
+   * Input: MHD connection we should resume when finished
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * Input: private key of the reserve.
+   */
+  struct TALER_ReservePrivateKeyP reserve_priv;
+
+  /**
+   * Output: Set to delay after which the reserve will expire if idle.
+   */
+  struct GNUNET_TIME_Relative idle_reserve_expiration_time;
+
+  /**
+   * Internal: exchange find operation.
+   */
+  struct TMH_EXCHANGES_FindOperation *fo;
+
+  /**
+   * Internal: reserve status operation.
+   */
+  struct TALER_EXCHANGE_ReserveStatusHandle *rsh;
+
+  /**
+   * Output: response object to return (on error only)
+   */
+  struct MHD_Response *response;
+
+  /**
+   * Output: Total amount deposited into the reserve.
+   */
+  struct TALER_Amount amount_deposited;
+
+  /**
+   * Output: total tip amount requested.
+   */
+  struct TALER_Amount amount_withdrawn;
+
+  /**
+   * Input: total amount authorized.
+   */
+  struct TALER_Amount amount_authorized;
+
+  /**
+   * Output: set to the time when the reserve will expire
+   */
+  struct GNUNET_TIME_Absolute reserve_expiration;
+
+  /**
+   * Output: HTTP status code to return (on error only)
+   */
+  unsigned int response_code;
+
+  /**
+   * Input: Set to #GNUNET_NO if no tips were authorized yet.
+   * Used to know that @e amount_authorized is not yet initialized
+   * and in that case the helper will set it to zero (once we know
+   * the currency).
+   */
+  int none_authorized;
+
+  /**
+   * Internal: Is the @e connection currently suspended?
+   * #GNUNET_NO if the @e connection was not suspended,
+   * #GNUNET_YES if the @e connection was suspended,
+   * #GNUNET_SYSERR if @e connection was resumed to as
+   * part of #MH_force_pc_resume during shutdown.
+   */
+  int suspended;
+
+};
+
+
+/**
+ * Check the status of the given reserve at the given exchange.
+ * Suspends the MHD connection while this is happening and resumes
+ * processing once we know the reserve status (or once an error
+ * code has been determined).
+ *
+ * @param[in,out] ctr context for checking the reserve status
+ * @param tip_exchange the URL of the exchange to query
+ */
+void
+TMH_check_tip_reserve (struct CheckTipReserve *ctr,
+                       const char *tip_exchange);
+
+
+/**
+ * Clean up any state that might be left in @a ctr.
+ *
+ * @param[in] context to clean up
+ */
+void
+TMH_check_tip_reserve_cleanup (struct CheckTipReserve *ctr);
+
+#endif
diff --git a/src/lib/test_merchant_api_new.c b/src/lib/test_merchant_api_new.c
index 94aafac..67df2b9 100644
--- a/src/lib/test_merchant_api_new.c
+++ b/src/lib/test_merchant_api_new.c
@@ -511,7 +511,7 @@ run (void *cls,
     /**
      * The following block will (1) create a new
      * reserve, then (2) a proposal, then (3) pay for
-     * it, and finally (4) attempt to pick up a refund 
+     * it, and finally (4) attempt to pick up a refund
      * from it without any increasing taking place
      * in the first place.
      **/
@@ -641,7 +641,7 @@ run (void *cls,
        "nulltip",
        "tip 2",
        "EUR:5.01",
-       TALER_EC_TIP_AUTHORIZE_RESERVE_UNKNOWN),
+       TALER_EC_RESERVE_STATUS_UNKNOWN),
 
     TALER_TESTING_cmd_tip_query ("query-tip-1",
                                  merchant_url,

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



reply via email to

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