gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-merchant] branch master updated (4885774 -> 3a38124)


From: gnunet
Subject: [GNUnet-SVN] [taler-merchant] branch master updated (4885774 -> 3a38124)
Date: Thu, 04 Apr 2019 23:14:58 +0200

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

grothoff pushed a change to branch master
in repository merchant.

    from 4885774  Address compilation warning
     new 6595073  fix indentation issues
     new 3a38124  clean up code duplication in checking of tipping reserve 
status by moving shared logic to helper function

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


Summary of changes:
 src/backend/Makefile.am                            |   1 +
 src/backend/taler-merchant-httpd_pay.c             | 493 ++++++++++-----------
 src/backend/taler-merchant-httpd_responses.c       |  44 +-
 src/backend/taler-merchant-httpd_tip-authorize.c   | 226 ++--------
 src/backend/taler-merchant-httpd_tip-pickup.c      | 114 ++---
 src/backend/taler-merchant-httpd_tip-query.c       | 422 +++---------------
 .../taler-merchant-httpd_tip-reserve-helper.c      | 329 ++++++++++++++
 .../taler-merchant-httpd_tip-reserve-helper.h      | 137 ++++++
 src/lib/test_merchant_api_new.c                    |   4 +-
 9 files changed, 887 insertions(+), 883 deletions(-)
 create mode 100644 src/backend/taler-merchant-httpd_tip-reserve-helper.c
 create mode 100644 src/backend/taler-merchant-httpd_tip-reserve-helper.h

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_pay.c 
b/src/backend/taler-merchant-httpd_pay.c
index 96dbdc0..6bb1aba 100644
--- a/src/backend/taler-merchant-httpd_pay.c
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -472,13 +472,12 @@ sign_success_response (struct PayContext *pc)
   struct MHD_Response *mret;
 
   refunds = TM_get_refund_json (pc->mi,
-                               &pc->h_contract_terms,
-                               &ec,
-                               &errmsg);
-
+                                &pc->h_contract_terms,
+                                &ec,
+                                &errmsg);
   if (NULL == refunds)
     return TMH_RESPONSE_make_error (ec,
-                                   errmsg);
+                                    errmsg);
 
   mr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK);
   mr.purpose.size = htonl (sizeof (mr));
@@ -536,14 +535,14 @@ sign_success_response (struct PayContext *pc)
  */
 static void
 resume_pay_with_error (struct PayContext *pc,
-                      unsigned int http_status,
-                      enum TALER_ErrorCode ec,
-                      const char *msg)
+                       unsigned int http_status,
+                       enum TALER_ErrorCode ec,
+                       const char *msg)
 {
   resume_pay_with_response (pc,
-                           http_status,
-                           TMH_RESPONSE_make_error (ec,
-                                                    msg));
+                            http_status,
+                            TMH_RESPONSE_make_error (ec,
+                                                     msg));
 }
 
 
@@ -637,20 +636,20 @@ check_payment_sufficient (struct PayContext *pc)
 
     GNUNET_assert (GNUNET_YES == dc->found_in_db);
     if ( (GNUNET_OK !=
-         TALER_amount_add (&acc_fee,
-                           &dc->deposit_fee,
-                           &acc_fee)) ||
-        (GNUNET_OK !=
-         TALER_amount_add (&acc_amount,
-                           &dc->amount_with_fee,
-                           &acc_amount)) )
+          TALER_amount_add (&acc_fee,
+                            &dc->deposit_fee,
+                            &acc_fee)) ||
+         (GNUNET_OK !=
+          TALER_amount_add (&acc_amount,
+                            &dc->amount_with_fee,
+                            &acc_amount)) )
     {
       GNUNET_break_op (0);
       /* Overflow in these amounts? Very strange. */
       return TALER_EC_PAY_AMOUNT_OVERFLOW;
     }
     if (1 ==
-       TALER_amount_cmp (&dc->deposit_fee,
+        TALER_amount_cmp (&dc->deposit_fee,
                           &dc->amount_with_fee))
     {
       GNUNET_break_op (0);
@@ -663,22 +662,22 @@ check_payment_sufficient (struct PayContext *pc)
       int new_exchange = GNUNET_YES;
 
       for (unsigned int j=0;j<i;j++)
-       if (0 == strcasecmp (dc->exchange_url,
-                            pc->dc[j].exchange_url))
-       {
-         new_exchange = GNUNET_NO;
-         break;
-       }
+        if (0 == strcasecmp (dc->exchange_url,
+                             pc->dc[j].exchange_url))
+        {
+          new_exchange = GNUNET_NO;
+          break;
+        }
       if (GNUNET_YES == new_exchange)
       {
-       if (GNUNET_OK !=
-           TALER_amount_add (&total_wire_fee,
-                             &total_wire_fee,
-                             &dc->wire_fee))
-       {
-         GNUNET_break_op (0);
-         return TALER_EC_PAY_EXCHANGE_REJECTED;
-       }
+        if (GNUNET_OK !=
+            TALER_amount_add (&total_wire_fee,
+                              &total_wire_fee,
+                              &dc->wire_fee))
+        {
+          GNUNET_break_op (0);
+          return TALER_EC_PAY_EXCHANGE_REJECTED;
+        }
       }
     }
   }
@@ -814,51 +813,51 @@ check_payment_sufficient (struct PayContext *pc)
  */
 static void
 generate_error_response (struct PayContext *pc,
-                        enum TALER_ErrorCode ec)
+                         enum TALER_ErrorCode ec)
 {
   switch (ec)
   {
   case TALER_EC_PAY_AMOUNT_OVERFLOW:
     resume_pay_with_error (pc,
-                          MHD_HTTP_BAD_REQUEST,
-                          ec,
-                          "Overflow adding up amounts");
+                           MHD_HTTP_BAD_REQUEST,
+                           ec,
+                           "Overflow adding up amounts");
     break;
   case TALER_EC_PAY_FEES_EXCEED_PAYMENT:
     resume_pay_with_error (pc,
-                          MHD_HTTP_BAD_REQUEST,
-                          ec,
-                          "Deposit fees exceed coin's contribution");
+                           MHD_HTTP_BAD_REQUEST,
+                           ec,
+                           "Deposit fees exceed coin's contribution");
     break;
   case TALER_EC_PAY_PAYMENT_INSUFFICIENT_DUE_TO_FEES:
     resume_pay_with_error (pc,
-                          MHD_HTTP_METHOD_NOT_ACCEPTABLE,
-                          ec,
-                          "insufficient funds (including excessive exchange 
fees to be covered by customer)");
+                           MHD_HTTP_METHOD_NOT_ACCEPTABLE,
+                           ec,
+                           "insufficient funds (including excessive exchange 
fees to be covered by customer)");
     break;
   case TALER_EC_PAY_PAYMENT_INSUFFICIENT:
     resume_pay_with_error (pc,
-                          MHD_HTTP_METHOD_NOT_ACCEPTABLE,
-                          ec,
-                          "insufficient funds");
+                           MHD_HTTP_METHOD_NOT_ACCEPTABLE,
+                           ec,
+                           "insufficient funds");
     break;
   case TALER_EC_PAY_WIRE_FEE_CURRENCY_MISSMATCH:
     resume_pay_with_error (pc,
-                          MHD_HTTP_INTERNAL_SERVER_ERROR,
-                          ec,
-                          "wire_fee currency does not match");
+                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                           ec,
+                           "wire_fee currency does not match");
     break;
   case TALER_EC_PAY_EXCHANGE_REJECTED:
     resume_pay_with_error (pc,
-                          MHD_HTTP_PRECONDITION_FAILED,
-                          ec,
-                          "exchange charges incompatible wire fee");
+                           MHD_HTTP_PRECONDITION_FAILED,
+                           ec,
+                           "exchange charges incompatible wire fee");
     break;
   default:
     resume_pay_with_error (pc,
-                          MHD_HTTP_INTERNAL_SERVER_ERROR,
-                          ec,
-                          "unexpected error code");
+                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                           ec,
+                           "unexpected error code");
     GNUNET_break (0);
     break;
   }
@@ -907,9 +906,9 @@ begin_transaction (struct PayContext *pc);
 static void
 deposit_cb (void *cls,
             unsigned int http_status,
-           enum TALER_ErrorCode ec,
+            enum TALER_ErrorCode ec,
             const struct TALER_ExchangeSignatureP *exchange_sig,
-           const struct TALER_ExchangePublicKeyP *sign_key,
+            const struct TALER_ExchangePublicKeyP *sign_key,
             const json_t *proof)
 {
   struct DepositConfirmation *dc = cls;
@@ -922,8 +921,8 @@ deposit_cb (void *cls,
   if (MHD_HTTP_OK != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-               "Deposit operation failed with HTTP code %u\n",
-               http_status);
+                "Deposit operation failed with HTTP code %u\n",
+                http_status);
     /* Transaction failed; stop all other ongoing deposits */
     abort_deposit (pc);
     db->rollback (db->cls);
@@ -935,9 +934,9 @@ deposit_cb (void *cls,
                                 MHD_HTTP_SERVICE_UNAVAILABLE,
                                 TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:I, 
s:s}",
                                                              "error", 
"exchange failed",
-                                                            "code", 
(json_int_t) TALER_EC_PAY_EXCHANGE_FAILED,
-                                                            "exchange-code", 
(json_int_t) ec,
-                                                            
"exchange-http-status", (json_int_t) http_status,
+                                                             "code", 
(json_int_t) TALER_EC_PAY_EXCHANGE_FAILED,
+                                                             "exchange-code", 
(json_int_t) ec,
+                                                             
"exchange-http-status", (json_int_t) http_status,
                                                              "hint", "The 
exchange provided an unexpected response"));
     }
     else
@@ -965,16 +964,16 @@ deposit_cb (void *cls,
   /* NOTE: not run in any transaction block, simply as a
      transaction by itself! */
   qs = db->store_deposit (db->cls,
-                         &pc->h_contract_terms,
-                         &pc->mi->pubkey,
-                         &dc->coin_pub,
-                         dc->exchange_url,
-                         &dc->amount_with_fee,
-                         &dc->deposit_fee,
-                         &dc->refund_fee,
-                         &dc->wire_fee,
-                         sign_key,
-                         proof);
+                          &pc->h_contract_terms,
+                          &pc->mi->pubkey,
+                          &dc->coin_pub,
+                          dc->exchange_url,
+                          &dc->amount_with_fee,
+                          &dc->deposit_fee,
+                          &dc->refund_fee,
+                          &dc->wire_fee,
+                          sign_key,
+                          proof);
   if (0 > qs)
   {
     /* Special report if retries insufficient */
@@ -988,9 +987,9 @@ deposit_cb (void *cls,
     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
     /* Forward error including 'proof' for the body */
     resume_pay_with_error (pc,
-                          MHD_HTTP_INTERNAL_SERVER_ERROR,
-                          TALER_EC_PAY_DB_STORE_PAY_ERROR,
-                          "Merchant database error");
+                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                           TALER_EC_PAY_DB_STORE_PAY_ERROR,
+                           "Merchant database error");
     return;
   }
   dc->found_in_db = GNUNET_YES;
@@ -1027,9 +1026,9 @@ process_pay_with_exchange (void *cls,
        exchanges.  Reject the payment. */
     GNUNET_break_op (0);
     resume_pay_with_error (pc,
-                          MHD_HTTP_PRECONDITION_FAILED,
-                          TALER_EC_PAY_EXCHANGE_REJECTED,
-                          "exchange not supported");
+                           MHD_HTTP_PRECONDITION_FAILED,
+                           TALER_EC_PAY_EXCHANGE_REJECTED,
+                           "exchange not supported");
     return;
   }
   pc->mh = mh;
@@ -1038,9 +1037,9 @@ process_pay_with_exchange (void *cls,
   {
     GNUNET_break (0);
     resume_pay_with_error (pc,
-                          MHD_HTTP_INTERNAL_SERVER_ERROR,
-                          TALER_EC_PAY_EXCHANGE_KEYS_FAILURE,
-                          "no keys");
+                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                           TALER_EC_PAY_EXCHANGE_KEYS_FAILURE,
+                           "no keys");
     return;
   }
 
@@ -1071,7 +1070,7 @@ process_pay_with_exchange (void *cls,
                                 MHD_HTTP_BAD_REQUEST,
                                 TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:o, 
s:o}",
                                                              "error", 
"denomination not found",
-                                                            "code", 
TALER_EC_PAY_DENOMINATION_KEY_NOT_FOUND,
+                                                             "code", 
TALER_EC_PAY_DENOMINATION_KEY_NOT_FOUND,
                                                              "denom_pub", 
GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key),
                                                              "exchange_keys", 
TALER_EXCHANGE_get_keys_raw (mh)));
       return;
@@ -1086,7 +1085,7 @@ process_pay_with_exchange (void *cls,
                                 MHD_HTTP_BAD_REQUEST,
                                 TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:o}",
                                                              "error", "invalid 
denomination",
-                                                            "code", 
(json_int_t) TALER_EC_PAY_DENOMINATION_KEY_AUDITOR_FAILURE,
+                                                             "code", 
(json_int_t) TALER_EC_PAY_DENOMINATION_KEY_AUDITOR_FAILURE,
                                                              "denom_pub", 
GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key)));
       return;
     }
@@ -1123,7 +1122,7 @@ process_pay_with_exchange (void *cls,
                                 MHD_HTTP_UNAUTHORIZED,
                                 TMH_RESPONSE_make_json_pack ("{s:s, s:I, s:i}",
                                                              "hint", "Coin 
signature invalid.",
-                                                            "code", 
(json_int_t) TALER_EC_PAY_COIN_SIGNATURE_INVALID,
+                                                             "code", 
(json_int_t) TALER_EC_PAY_COIN_SIGNATURE_INVALID,
 
                                                              "coin_idx", i));
       return;
@@ -1150,17 +1149,17 @@ find_next_exchange (struct PayContext *pc)
     {
       pc->current_exchange = dc->exchange_url;
       pc->fo = TMH_EXCHANGES_find_exchange (pc->current_exchange,
-                                           pc->wm->wire_method,
-                                           &process_pay_with_exchange,
-                                           pc);
+                                            pc->wm->wire_method,
+                                            &process_pay_with_exchange,
+                                            pc);
       if (NULL == pc->fo)
       {
-       GNUNET_break (0);
-       resume_pay_with_error (pc,
-                              MHD_HTTP_INTERNAL_SERVER_ERROR,
-                              TALER_EC_PAY_EXCHANGE_FAILED,
-                              "Failed to lookup exchange by URL");
-       return;
+        GNUNET_break (0);
+        resume_pay_with_error (pc,
+                               MHD_HTTP_INTERNAL_SERVER_ERROR,
+                               TALER_EC_PAY_EXCHANGE_FAILED,
+                               "Failed to lookup exchange by URL");
+        return;
       }
       return;
     }
@@ -1192,9 +1191,9 @@ handle_pay_timeout (void *cls)
     pc->fo = NULL;
   }
   resume_pay_with_error (pc,
-                        MHD_HTTP_SERVICE_UNAVAILABLE,
-                        TALER_EC_PAY_EXCHANGE_TIMEOUT,
-                        "exchange not reachable");
+                         MHD_HTTP_SERVICE_UNAVAILABLE,
+                         TALER_EC_PAY_EXCHANGE_TIMEOUT,
+                         "exchange not reachable");
 }
 
 
@@ -1215,11 +1214,11 @@ static void
 check_coin_paid (void *cls,
                  const struct GNUNET_HashCode *h_contract_terms,
                  const struct TALER_CoinSpendPublicKeyP *coin_pub,
-                const char *exchange_url,
+                 const char *exchange_url,
                  const struct TALER_Amount *amount_with_fee,
                  const struct TALER_Amount *deposit_fee,
                  const struct TALER_Amount *refund_fee,
-                const struct TALER_Amount *wire_fee,
+                 const struct TALER_Amount *wire_fee,
                  const json_t *exchange_proof)
 {
   struct PayContext *pc = cls;
@@ -1250,22 +1249,22 @@ check_coin_paid (void *cls,
                 TALER_b2s (coin_pub,
                           sizeof (*coin_pub)));
     if (GNUNET_OK !=
-       TALER_amount_add (&pc->total_paid,
-                         &pc->total_paid,
-                         amount_with_fee))
+        TALER_amount_add (&pc->total_paid,
+                          &pc->total_paid,
+                          amount_with_fee))
     {
       /* We accepted this coin for payment on this contract before,
-        and now we can't even add the amount!? */
+         and now we can't even add the amount!? */
       GNUNET_break (0);
       continue;
     }
     if (GNUNET_OK !=
-       TALER_amount_add (&pc->total_fees_paid,
-                         &pc->total_fees_paid,
-                         deposit_fee))
+        TALER_amount_add (&pc->total_fees_paid,
+                          &pc->total_fees_paid,
+                          deposit_fee))
     {
       /* We accepted this coin for payment on this contract before,
-        and now we can't even add the amount!? */
+         and now we can't even add the amount!? */
       GNUNET_break (0);
       continue;
     }
@@ -1307,13 +1306,13 @@ parse_pay (struct MHD_Connection *connection,
   char *last_session_id;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_string ("mode",
-                            &mode),
+                             &mode),
     GNUNET_JSON_spec_json ("coins",
-                          &coins),
+                           &coins),
     GNUNET_JSON_spec_string ("order_id",
-                            &order_id),
+                             &order_id),
     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
-                                &merchant_pub),
+                                 &merchant_pub),
     GNUNET_JSON_spec_end()
   };
   enum GNUNET_DB_QueryStatus qs;
@@ -1348,8 +1347,8 @@ parse_pay (struct MHD_Connection *connection,
     /* 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_PAY_ERROR,
-                                             "db error to previous /pay data");
+                                              TALER_EC_PAY_DB_FETCH_PAY_ERROR,
+                                              "db error to previous /pay 
data");
 
   }
   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
@@ -1403,7 +1402,7 @@ parse_pay (struct MHD_Connection *connection,
     return GNUNET_NO;
   }
   if (0 != strcasecmp ("abort-refund",
-                      mode))
+                       mode))
     pc->mode = PC_MODE_PAY;
   else
     pc->mode = PC_MODE_ABORT_REFUND;
@@ -1415,8 +1414,8 @@ parse_pay (struct MHD_Connection *connection,
     GNUNET_JSON_parse_free (spec);
     if (MHD_NO ==
        TMH_RESPONSE_reply_not_found (connection,
-                                     TALER_EC_PAY_INSTANCE_UNKNOWN,
-                                     "Unknown instance given"))
+                                  TALER_EC_PAY_INSTANCE_UNKNOWN,
+                                  "Unknown instance given"))
     {
       GNUNET_break (0);
       return GNUNET_SYSERR;
@@ -1588,17 +1587,17 @@ parse_pay (struct MHD_Connection *connection,
     const char *exchange_url;
     struct GNUNET_JSON_Specification spec[] = {
       TALER_JSON_spec_denomination_public_key ("denom_pub",
-                                              &dc->denom),
+                                              &dc->denom),
       TALER_JSON_spec_amount ("contribution",
-                             &dc->amount_with_fee),
+                              &dc->amount_with_fee),
       GNUNET_JSON_spec_string ("exchange_url",
-                              &exchange_url),
+                               &exchange_url),
       GNUNET_JSON_spec_fixed_auto ("coin_pub",
-                                  &dc->coin_pub),
+                                   &dc->coin_pub),
       TALER_JSON_spec_denomination_signature ("ub_sig",
-                                             &dc->ub_sig),
+                                              &dc->ub_sig),
       GNUNET_JSON_spec_fixed_auto ("coin_sig",
-                                  &dc->coin_sig),
+                                   &dc->coin_sig),
       GNUNET_JSON_spec_end()
     };
 
@@ -1636,11 +1635,11 @@ parse_pay (struct MHD_Connection *connection,
  */
 static void
 check_coin_refunded (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)
+                     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 PayContext *pc = cls;
 
@@ -1650,14 +1649,14 @@ check_coin_refunded (void *cls,
 
     /* Get matching coin from results*/
     if (0 != memcmp (coin_pub,
-                    &dc->coin_pub,
-                    sizeof (struct TALER_CoinSpendPublicKeyP)))
+                     &dc->coin_pub,
+                     sizeof (struct TALER_CoinSpendPublicKeyP)))
     {
       dc->refunded = GNUNET_YES;
       GNUNET_break (GNUNET_OK ==
-                   TALER_amount_add (&pc->total_refunded,
-                                     &pc->total_refunded,
-                                     refund_amount));
+                    TALER_amount_add (&pc->total_refunded,
+                                      &pc->total_refunded,
+                                      refund_amount));
     }
   }
 }
@@ -1682,8 +1681,8 @@ begin_transaction (struct PayContext *pc)
     resume_pay_with_response (pc,
                              MHD_HTTP_INTERNAL_SERVER_ERROR,
                              TMH_RESPONSE_make_json_pack ("{s:I, s:s}",
-                                                          "code", (json_int_t) 
TALER_EC_PAY_DB_STORE_TRANSACTION_ERROR,
-                                                          "hint", "Soft 
merchant database error: retry counter exceeded"));
+                                               "code", (json_int_t) 
TALER_EC_PAY_DB_STORE_TRANSACTION_ERROR,
+                                               "hint", "Soft merchant database 
error: retry counter exceeded"));
     return;
   }
 
@@ -1691,14 +1690,14 @@ begin_transaction (struct PayContext *pc)
 
   /* Init. some price accumulators.  */
   GNUNET_break (GNUNET_OK ==
-               TALER_amount_get_zero (pc->amount.currency,
-                                      &pc->total_paid));
+                TALER_amount_get_zero (pc->amount.currency,
+                                       &pc->total_paid));
   GNUNET_break (GNUNET_OK ==
-               TALER_amount_get_zero (pc->amount.currency,
-                                      &pc->total_fees_paid));
+                TALER_amount_get_zero (pc->amount.currency,
+                                       &pc->total_fees_paid));
   GNUNET_break (GNUNET_OK ==
-               TALER_amount_get_zero (pc->amount.currency,
-                                      &pc->total_refunded));
+                TALER_amount_get_zero (pc->amount.currency,
+                                       &pc->total_refunded));
 
   /* First, try to see if we have all we need already done */
   db->preflight (db->cls);
@@ -1708,18 +1707,18 @@ begin_transaction (struct PayContext *pc)
   {
     GNUNET_break (0);
     resume_pay_with_error (pc,
-                          MHD_HTTP_INTERNAL_SERVER_ERROR,
-                          TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
-                          "Merchant database error (could not start 
transaction)");
+                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                           TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
+                           "Merchant database error (could not start 
transaction)");
     return;
   }
 
   /* Check if some of these coins already succeeded for _this_ contract.  */
   qs = db->find_payments (db->cls,
-                         &pc->h_contract_terms,
-                         &pc->mi->pubkey,
-                         &check_coin_paid,
-                         pc);
+                          &pc->h_contract_terms,
+                          &pc->mi->pubkey,
+                          &check_coin_paid,
+                          pc);
   if (0 > qs)
   {
     db->rollback (db->cls);
@@ -1731,18 +1730,18 @@ begin_transaction (struct PayContext *pc)
     /* Always report on hard error as well to enable diagnostics */
     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
     resume_pay_with_error (pc,
-                          MHD_HTTP_INTERNAL_SERVER_ERROR,
-                          TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
-                          "Merchant database error");
+                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                           TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
+                           "Merchant database error");
     return;
   }
 
   /* Check if we refunded some of the coins */
   qs = db->get_refunds_from_contract_terms_hash (db->cls,
-                                                &pc->mi->pubkey,
-                                                &pc->h_contract_terms,
-                                                &check_coin_refunded,
-                                                pc);
+                                                 &pc->mi->pubkey,
+                                                 &pc->h_contract_terms,
+                                                 &check_coin_refunded,
+                                                 pc);
   if (0 > qs)
   {
     db->rollback (db->cls);
@@ -1754,9 +1753,9 @@ begin_transaction (struct PayContext *pc)
     /* Always report on hard error as well to enable diagnostics */
     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
     resume_pay_with_error (pc,
-                          MHD_HTTP_INTERNAL_SERVER_ERROR,
-                          TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
-                          "Merchant database error");
+                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                           TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
+                           "Merchant database error");
     return;
   }
 
@@ -1787,9 +1786,9 @@ begin_transaction (struct PayContext *pc)
       /* Always report on hard error as well to enable diagnostics */
       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
       resume_pay_with_error (pc,
-                            MHD_HTTP_INTERNAL_SERVER_ERROR,
-                            TALER_EC_PAY_DB_STORE_PAY_ERROR,
-                            "Merchant database error");
+                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                             TALER_EC_PAY_DB_STORE_PAY_ERROR,
+                             "Merchant database error");
       return;
     }
     if (0 < qs)
@@ -1798,19 +1797,19 @@ begin_transaction (struct PayContext *pc)
       json_decref (terms);
       db->rollback (db->cls);
       resume_pay_with_error (pc,
-                            MHD_HTTP_FORBIDDEN,
-                            TALER_EC_PAY_ABORT_REFUND_REFUSED_PAYMENT_COMPLETE,
-                            "Payment complete, refusing to abort");
+                             MHD_HTTP_FORBIDDEN,
+                             
TALER_EC_PAY_ABORT_REFUND_REFUSED_PAYMENT_COMPLETE,
+                             "Payment complete, refusing to abort");
       return;
     }
 
     /* Store refund in DB */
     qs = db->increase_refund_for_contract (db->cls,
-                                          &pc->h_contract_terms,
-                                          &pc->mi->pubkey,
-                                          &pc->total_paid,
+                                           &pc->h_contract_terms,
+                                           &pc->mi->pubkey,
+                                           &pc->total_paid,
                                            /* justification */
-                                          "incomplete payment aborted");
+                                           "incomplete payment aborted");
     if (0 > qs)
     {
       db->rollback (db->cls);
@@ -1822,9 +1821,9 @@ begin_transaction (struct PayContext *pc)
       /* Always report on hard error as well to enable diagnostics */
       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
       resume_pay_with_error (pc,
-                            MHD_HTTP_INTERNAL_SERVER_ERROR,
-                            TALER_EC_PAY_DB_STORE_PAY_ERROR,
-                            "Merchant database error");
+                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                             TALER_EC_PAY_DB_STORE_PAY_ERROR,
+                             "Merchant database error");
       return;
     }
     qs = db->commit (db->cls);
@@ -1833,13 +1832,13 @@ begin_transaction (struct PayContext *pc)
       db->rollback (db->cls);
       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
       {
-       begin_transaction (pc);
-       return;
+        begin_transaction (pc);
+        return;
       }
       resume_pay_with_error (pc,
-                            MHD_HTTP_INTERNAL_SERVER_ERROR,
-                            TALER_EC_PAY_DB_STORE_PAY_ERROR,
-                            "Merchant database error: could not commit");
+                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                             TALER_EC_PAY_DB_STORE_PAY_ERROR,
+                             "Merchant database error: could not commit");
       return;
     }
     /* At this point, the refund got correctly committed
@@ -1850,47 +1849,47 @@ begin_transaction (struct PayContext *pc)
       refunds = json_array ();
       for (unsigned int i=0;i<pc->coins_cnt;i++)
       {
-       struct TALER_RefundRequestPS rr;
-       struct TALER_MerchantSignatureP msig;
-       uint64_t rtransactionid;
+        struct TALER_RefundRequestPS rr;
+        struct TALER_MerchantSignatureP msig;
+        uint64_t rtransactionid;
 
         /* Will only work with coins found in DB.  */
-       if (GNUNET_YES != pc->dc[i].found_in_db)
-         continue;
+        if (GNUNET_YES != pc->dc[i].found_in_db)
+          continue;
 
-       rtransactionid = 0;
+        rtransactionid = 0;
         rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
-       rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
-       rr.h_contract_terms = pc->h_contract_terms;
-       rr.coin_pub = pc->dc[i].coin_pub;
-       rr.merchant = pc->mi->pubkey;
-       rr.rtransaction_id = GNUNET_htonll (rtransactionid);
-       TALER_amount_hton (&rr.refund_amount,
-                          &pc->dc[i].amount_with_fee);
-       TALER_amount_hton (&rr.refund_fee,
-                          &pc->dc[i].refund_fee);
-
-       if (GNUNET_OK !=
-           GNUNET_CRYPTO_eddsa_sign (&pc->mi->privkey.eddsa_priv,
-                                     &rr.purpose,
-                                     &msig.eddsa_sig))
-       {
-         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                     "Failed to sign successful refund confirmation\n");
-         json_decref (refunds);
-         resume_pay_with_error (pc,
-                                MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                TALER_EC_PAY_REFUND_SIGNATURE_FAILED,
-                                "Refund approved, but failed to sign 
confirmation");
-         return;
-       }
+        rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
+        rr.h_contract_terms = pc->h_contract_terms;
+        rr.coin_pub = pc->dc[i].coin_pub;
+        rr.merchant = pc->mi->pubkey;
+        rr.rtransaction_id = GNUNET_htonll (rtransactionid);
+        TALER_amount_hton (&rr.refund_amount,
+                           &pc->dc[i].amount_with_fee);
+        TALER_amount_hton (&rr.refund_fee,
+                           &pc->dc[i].refund_fee);
+
+        if (GNUNET_OK !=
+            GNUNET_CRYPTO_eddsa_sign (&pc->mi->privkey.eddsa_priv,
+                                      &rr.purpose,
+                                      &msig.eddsa_sig))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      "Failed to sign successful refund confirmation\n");
+          json_decref (refunds);
+          resume_pay_with_error (pc,
+                                 MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                 TALER_EC_PAY_REFUND_SIGNATURE_FAILED,
+                                 "Refund approved, but failed to sign 
confirmation");
+          return;
+        }
 
         /* Pack refund for i-th coin.  */
-       json_array_append_new (refunds,
-                              json_pack ("{s:I, s:o, s:o s:o s:o}",
-                                         "rtransaction_id", (json_int_t) 
rtransactionid,
-                                         "coin_pub", 
GNUNET_JSON_from_data_auto (&rr.coin_pub),
-                                         "merchant_sig", 
GNUNET_JSON_from_data_auto (&msig),
+        json_array_append_new (refunds,
+                               json_pack ("{s:I, s:o, s:o s:o s:o}",
+                                          "rtransaction_id", (json_int_t) 
rtransactionid,
+                                          "coin_pub", 
GNUNET_JSON_from_data_auto (&rr.coin_pub),
+                                          "merchant_sig", 
GNUNET_JSON_from_data_auto (&msig),
                                           "refund_amount", 
TALER_JSON_from_amount_nbo (&rr.refund_amount),
                                           "refund_fee", 
TALER_JSON_from_amount_nbo (&rr.refund_fee)));
       }
@@ -1898,15 +1897,15 @@ begin_transaction (struct PayContext *pc)
       /* Resume and send back the response.  */
       resume_pay_with_response
         (pc,
-        MHD_HTTP_OK,
-        TMH_RESPONSE_make_json_pack
-           ("{s:o, s:o, s:o}",
-            /* Refunds pack.  */
-           "refund_permissions", refunds,
-           "merchant_pub",
-            GNUNET_JSON_from_data_auto (&pc->mi->pubkey),
-            "h_contract_terms",
-            GNUNET_JSON_from_data_auto (&pc->h_contract_terms)));
+         MHD_HTTP_OK,
+         TMH_RESPONSE_make_json_pack
+         ("{s:o, s:o, s:o}",
+          /* Refunds pack.  */
+          "refund_permissions", refunds,
+          "merchant_pub",
+          GNUNET_JSON_from_data_auto (&pc->mi->pubkey),
+          "h_contract_terms",
+          GNUNET_JSON_from_data_auto (&pc->h_contract_terms)));
     }
     return;
   }
@@ -1923,33 +1922,33 @@ begin_transaction (struct PayContext *pc)
     {
       /* Payment succeeded, commit! */
       qs = db->mark_proposal_paid (db->cls,
-                                  &pc->h_contract_terms,
-                                  &pc->mi->pubkey,
+                                   &pc->h_contract_terms,
+                                   &pc->mi->pubkey,
                                    pc->session_id);
       if (0 <= qs)
-       qs = db->commit (db->cls);
+        qs = db->commit (db->cls);
       else
         db->rollback (db->cls);
       if (0 > qs)
       {
-       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-       {
-         begin_transaction (pc);
-         return;
-       }
-       resume_pay_with_error (pc,
-                              MHD_HTTP_INTERNAL_SERVER_ERROR,
-                              TALER_EC_PAY_DB_STORE_PAYMENTS_ERROR,
-                              "Merchant database error: could not mark 
proposal as 'paid'");
-       return;
+        if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+        {
+          begin_transaction (pc);
+          return;
+        }
+        resume_pay_with_error (pc,
+                               MHD_HTTP_INTERNAL_SERVER_ERROR,
+                               TALER_EC_PAY_DB_STORE_PAYMENTS_ERROR,
+                               "Merchant database error: could not mark 
proposal as 'paid'");
+        return;
       }
       resume_pay_with_response (pc,
-                               MHD_HTTP_OK,
-                               sign_success_response (pc));
+                                MHD_HTTP_OK,
+                                sign_success_response (pc));
       return;
     }
     generate_error_response (pc,
-                            ec);
+                             ec);
     return;
   }
 
@@ -2012,10 +2011,10 @@ handler_pay_json (struct MHD_Connection *connection,
  */
 int
 MH_handler_pay (struct TMH_RequestHandler *rh,
-               struct MHD_Connection *connection,
-               void **connection_cls,
-               const char *upload_data,
-               size_t *upload_data_size)
+                struct MHD_Connection *connection,
+                void **connection_cls,
+                const char *upload_data,
+                size_t *upload_data_size)
 {
   struct PayContext *pc;
   int res;
@@ -2047,22 +2046,22 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
       return MHD_NO; /* hard error */
     }
     res = MHD_queue_response (connection,
-                             pc->response_code,
-                             pc->response);
+                              pc->response_code,
+                              pc->response);
     MHD_destroy_response (pc->response);
     pc->response = NULL;
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-               "Queueing response (%u) for /pay (%s).\n",
-               (unsigned int) pc->response_code,
-               res ? "OK" : "FAILED");
+                "Queueing response (%u) for /pay (%s).\n",
+                (unsigned int) pc->response_code,
+                res ? "OK" : "FAILED");
     return res;
   }
 
   res = TMH_PARSE_post_json (connection,
-                            &pc->json_parse_context,
-                            upload_data,
-                            upload_data_size,
-                            &root);
+                             &pc->json_parse_context,
+                             upload_data,
+                             upload_data_size,
+                             &root);
   if (GNUNET_SYSERR == res)
   {
     GNUNET_break (0);
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-pickup.c 
b/src/backend/taler-merchant-httpd_tip-pickup.c
index 3c810cb..3d96a18 100644
--- a/src/backend/taler-merchant-httpd_tip-pickup.c
+++ b/src/backend/taler-merchant-httpd_tip-pickup.c
@@ -191,9 +191,9 @@ run_pickup (struct MHD_Connection *connection,
   if (TALER_EC_NONE != pc->ec)
   {
     return TMH_RESPONSE_reply_rc (connection,
-                                 pc->response_code,
-                                 pc->ec,
-                                 pc->error_hint);
+                                  pc->response_code,
+                                  pc->ec,
+                                  pc->error_hint);
   }
   ec = db->pickup_tip_TR (db->cls,
                           &pc->total,
@@ -221,13 +221,13 @@ run_pickup (struct MHD_Connection *connection,
       break;
     }
     return TMH_RESPONSE_reply_rc (connection,
-                                 response_code,
-                                 ec,
-                                 human);
+                                  response_code,
+                                  ec,
+                                  human);
   }
   sigs = json_array ();
   GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv.eddsa_priv,
-                                     &reserve_pub.eddsa_pub);
+                                      &reserve_pub.eddsa_pub);
   for (unsigned int i=0;i<pc->planchets_len;i++)
   {
     struct PlanchetDetail *pd = &pc->planchets[i];
@@ -262,9 +262,9 @@ run_pickup (struct MHD_Connection *connection,
  */
 static void
 exchange_found_cb (void *cls,
-                  struct TALER_EXCHANGE_Handle *eh,
-                  const struct TALER_Amount *wire_fee,
-                  int exchange_trusted)
+                   struct TALER_EXCHANGE_Handle *eh,
+                   const struct TALER_Amount *wire_fee,
+                   int exchange_trusted)
 {
   struct PickupContext *pc = cls;
   const struct TALER_EXCHANGE_Keys *keys;
@@ -294,8 +294,8 @@ exchange_found_cb (void *cls,
   GNUNET_assert (0 != pc->planchets_len);
   ae = GNUNET_NO;
   memset (&total,
-         0,
-         sizeof (total));
+          0,
+          sizeof (total));
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Calculating tip amount over %u planchets!\n",
               pc->planchets_len);
@@ -307,7 +307,7 @@ exchange_found_cb (void *cls,
     struct TALER_Amount amount_with_fee;
 
     dk = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
-                                                     
&pd->wr.h_denomination_pub);
+                                                      
&pd->wr.h_denomination_pub);
     if (NULL == dk)
     {
       pc->ec = TALER_EC_TIP_PICKUP_EXCHANGE_LACKED_KEY;
@@ -318,15 +318,15 @@ exchange_found_cb (void *cls,
       return;
     }
     GNUNET_CRYPTO_hash_context_read (hc,
-                                    &pd->wr.h_denomination_pub,
-                                    sizeof (struct GNUNET_HashCode));
+                                     &pd->wr.h_denomination_pub,
+                                     sizeof (struct GNUNET_HashCode));
     GNUNET_CRYPTO_hash_context_read (hc,
-                                    pd->coin_ev,
-                                    pd->coin_ev_size);
+                                     pd->coin_ev,
+                                     pd->coin_ev_size);
     if (GNUNET_OK !=
-       TALER_amount_add (&amount_with_fee,
-                         &dk->value,
-                         &dk->fee_withdraw))
+        TALER_amount_add (&amount_with_fee,
+                          &dk->value,
+                          &dk->fee_withdraw))
     {
       ae = GNUNET_YES;
     }
@@ -337,20 +337,20 @@ exchange_found_cb (void *cls,
     else
     {
       if (GNUNET_OK !=
-         TALER_amount_add (&total,
-                           &total,
-                           &amount_with_fee))
+          TALER_amount_add (&total,
+                            &total,
+                            &amount_with_fee))
       {
-       ae = GNUNET_YES;
+        ae = GNUNET_YES;
       }
     }
     TALER_amount_hton (&pd->wr.withdraw_fee,
-                      &dk->fee_withdraw);
+                       &dk->fee_withdraw);
     TALER_amount_hton (&pd->wr.amount_with_fee,
-                      &amount_with_fee);
+                       &amount_with_fee);
   }
   GNUNET_CRYPTO_hash_context_finish (hc,
-                                    &pc->pickup_id);
+                                     &pc->pickup_id);
   if (GNUNET_YES == ae)
   {
     pc->ec = TALER_EC_TIP_PICKUP_EXCHANGE_AMOUNT_OVERFLOW;
@@ -407,21 +407,21 @@ prepare_pickup (struct PickupContext *pc)
       break;
     }
     return TMH_RESPONSE_reply_rc (pc->connection,
-                                 response_code,
-                                 ec,
-                                 "Could not determine exchange URL for the 
given tip id");
+                                  response_code,
+                                  ec,
+                                  "Could not determine exchange URL for the 
given tip id");
 
   }
   pc->fo = TMH_EXCHANGES_find_exchange (pc->exchange_url,
-                                       NULL,
-                                       &exchange_found_cb,
-                                       pc);
+                                        NULL,
+                                        &exchange_found_cb,
+                                        pc);
   if (NULL == pc->fo)
   {
     return TMH_RESPONSE_reply_rc (pc->connection,
-                                 MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                 TALER_EC_INTERNAL_INVARIANT_FAILURE,
-                                 "consult server logs");
+                                  MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                  TALER_EC_INTERNAL_INVARIANT_FAILURE,
+                                  "consult server logs");
   }
   MHD_suspend_connection (pc->connection);
   return MHD_YES;
@@ -439,29 +439,29 @@ prepare_pickup (struct PickupContext *pc)
  */
 static int
 parse_planchet (struct MHD_Connection *connection,
-               const json_t *planchet,
-               struct PlanchetDetail *pd)
+                const json_t *planchet,
+                struct PlanchetDetail *pd)
 {
   int ret;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
-                                &pd->wr.h_denomination_pub),
+                                 &pd->wr.h_denomination_pub),
     GNUNET_JSON_spec_varsize ("coin_ev",
-                             (void **) &pd->coin_ev,
-                             &pd->coin_ev_size),
+                              (void **) &pd->coin_ev,
+                              &pd->coin_ev_size),
     GNUNET_JSON_spec_end()
   };
 
   ret = TMH_PARSE_json_data (connection,
-                            planchet,
-                            spec);
+                             planchet,
+                             spec);
   if (GNUNET_OK != ret)
     return ret;
   pd->wr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
   pd->wr.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS));
   GNUNET_CRYPTO_hash (pd->coin_ev,
-                     pd->coin_ev_size,
-                     &pd->wr.h_coin_envelope);
+                      pd->coin_ev_size,
+                      &pd->wr.h_coin_envelope);
   return ret;
 }
 
@@ -512,7 +512,7 @@ MH_handler_tip_pickup (struct TMH_RequestHandler *rh,
        after an exchange /keys' request to obtain the DKs
        (and not for each request). */
     return run_pickup (connection,
-                      pc);
+                       pc);
   }
   res = TMH_PARSE_post_json (connection,
                              &pc->json_parse_context,
@@ -541,29 +541,29 @@ MH_handler_tip_pickup (struct TMH_RequestHandler *rh,
     GNUNET_JSON_parse_free (spec);
     json_decref (root);
     return TMH_RESPONSE_reply_rc (connection,
-                                 MHD_HTTP_BAD_REQUEST,
-                                 
TALER_EC_TIP_PICKUP_EXCHANGE_TOO_MANY_PLANCHETS,
-                                 "limit of 1024 planchets exceeded by 
request");
+                                  MHD_HTTP_BAD_REQUEST,
+                                  
TALER_EC_TIP_PICKUP_EXCHANGE_TOO_MANY_PLANCHETS,
+                                  "limit of 1024 planchets exceeded by 
request");
   }
   if (0 == pc->planchets_len)
   {
     GNUNET_JSON_parse_free (spec);
     json_decref (root);
     return TMH_RESPONSE_reply_rc (connection,
-                                 MHD_HTTP_BAD_REQUEST,
-                                 TALER_EC_PARAMETER_MALFORMED,
-                                 "no planchets specified");
+                                  MHD_HTTP_BAD_REQUEST,
+                                  TALER_EC_PARAMETER_MALFORMED,
+                                  "no planchets specified");
   }
   db->preflight (db->cls);
   pc->planchets = GNUNET_new_array (pc->planchets_len,
-                                   struct PlanchetDetail);
+                                    struct PlanchetDetail);
   for (unsigned int i=0;i<pc->planchets_len;i++)
   {
     if (GNUNET_OK !=
-       (res = parse_planchet (connection,
-                              json_array_get (planchets,
-                                              i),
-                              &pc->planchets[i])))
+        (res = parse_planchet (connection,
+                               json_array_get (planchets,
+                                               i),
+                               &pc->planchets[i])))
     {
       GNUNET_JSON_parse_free (spec);
       json_decref (root);
diff --git a/src/backend/taler-merchant-httpd_tip-query.c 
b/src/backend/taler-merchant-httpd_tip-query.c
index 9664eaa..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)
   {
@@ -490,10 +203,10 @@ MH_handler_tip_query (struct TMH_RequestHandler *rh,
                 "Instance `%s' not configured for tipping\n",
                 tqc->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");
   }
-  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]