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 (2a8c44f -> 4446b15)


From: gnunet
Subject: [GNUnet-SVN] [taler-merchant] branch master updated (2a8c44f -> 4446b15)
Date: Tue, 16 Jan 2018 13:14:15 +0100

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

grothoff pushed a change to branch master
in repository merchant.

    from 2a8c44f  generate contract url for wallet
     new f019979  patch for #5198
     new 55cd81c  also log error code on unexpected failure
     new 0d50ba2  fix a few memory leaks
     new 4446b15  fix test memory leaks

The 4 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:
 doc/Makefile.am                                  |   1 -
 doc/manual.texi                                  |  32 +-
 doc/taler-merchant-tip-enable.1                  |  47 ---
 src/backend/Makefile.am                          |   1 -
 src/backend/taler-merchant-httpd.c               |  22 +-
 src/backend/taler-merchant-httpd_pay.c           | 122 ++++----
 src/backend/taler-merchant-httpd_proposal.c      |  53 +++-
 src/backend/taler-merchant-httpd_refund.c        |  34 +-
 src/backend/taler-merchant-httpd_tip-authorize.c | 383 +++++++++++++++++------
 src/backenddb/plugin_merchantdb_postgres.c       |   4 +-
 src/backenddb/test_merchantdb.c                  |   6 +-
 src/include/taler_merchant_service.h             |  73 +----
 src/lib/Makefile.am                              |   1 -
 src/lib/merchant_api_tip_enable.c                | 227 --------------
 src/lib/merchant_api_tip_pickup.c                |   3 +
 src/lib/test_merchant_api.c                      | 286 +++++------------
 src/merchant-tools/Makefile.am                   |  13 +-
 src/merchant-tools/taler-merchant-tip-enable.c   | 277 ----------------
 18 files changed, 526 insertions(+), 1059 deletions(-)
 delete mode 100644 doc/taler-merchant-tip-enable.1
 delete mode 100644 src/lib/merchant_api_tip_enable.c
 delete mode 100644 src/merchant-tools/taler-merchant-tip-enable.c

diff --git a/doc/Makefile.am b/doc/Makefile.am
index 9481e41..94c0ef4 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -12,7 +12,6 @@ arch.jpg: arch.dot
 AM_MAKEINFOHTMLFLAGS = --no-split --css-ref=docstyle.css 
--css-ref=brown-paper.css
 
 man_MANS = \
-  taler-merchant-tip-enable.1 \
   taler-merchant-generate-payments.1 \
   taler-merchant-httpd.1
 
diff --git a/doc/manual.texi b/doc/manual.texi
index 3c7c3c0..b8e2c6c 100644
--- a/doc/manual.texi
+++ b/doc/manual.texi
@@ -1125,36 +1125,16 @@ Make your wire transfer and (optionally) check at
 ``https://exchange:443/reserve/status/reserve_pub=QPE24X...''
 whether your transfer has arrived at the exchange.
 @c FIXME: we should create a nicer tool to do this check!
-Once the funds have arrived, you can now enable tipping using:
 
address@hidden
-$ taler-merchant-tip-enable \
-    --amount=AMOUNT \
-    --backend=BACKEND_URI \
-    --credit-uuid=CREDIT_UUID \
-    --instance=INSTANCE \
-    --expiration=EXPIRATION
address@hidden example
-For ``AMOUNT'', specify the amount you transferred in the usual Taler
-format of ``CURRENCY:VALUE[.FRACTION]'', i.e. ``EUR:50''.  The
-``BACKEND_URI'' should be the URI where your Taler merchant backend is
-running.  For ``CREDIT_UUID'', you need to specify a unique number
-that identifies your wire transfer.  You may have gotten one from your
-bank, or you can just make one up! The important thing is that you
-must never use the same UUID twice, except to repeat a failed command.
-For INSTANCE, specify the backend instance (i.e. ``default'').
-Finally, for EXPIRATION, pick a date two weeks after the wire
-transfer, unless you know that the exchange that is being used has a
-different period for closing reserves.  The format @code{YYYY-MM-DD}
-is accepted.
-
-Note that an exchange will typically close a reserve after two weeks,
+Once the funds have arrived, you can start to use the reserve
+for tipping.
+
+Note that an exchange will typically close a reserve after four weeks,
 wiring all remaining funds back to the sender's account.  Thus, you
 should plan to wire funds corresponding to a campaign of about two
 weeks to the exchange initially. If your campaign runs longer, you
-should wire further funds to the reserve every week to prevent it from
-expiring.  You need to run the ``taler-merchant-tip-enable'' command
-each time after you wire more funds to the reserve.
+should wire further funds to the reserve every other week to prevent
+it from expiring.
 
 
 @subsection Authorize a tip
diff --git a/doc/taler-merchant-tip-enable.1 b/doc/taler-merchant-tip-enable.1
deleted file mode 100644
index b61504b..0000000
--- a/doc/taler-merchant-tip-enable.1
+++ /dev/null
@@ -1,47 +0,0 @@
-.TH TALER\-MERCHANT\-TIP\-ENABLE 1 "Nov 4, 2017" "GNU Taler"
-
-.SH NAME
-taler\-merchant\-tip\-enable \- Tell Taler merchant backend about reserve 
funding for tipping
-
-.SH SYNOPSIS
-.B taler\-merchant\-tip\-enable
-.RI [ options ]
-.br
-
-.SH DESCRIPTION
-\fBtaler\-merchant\-tip\-enable\fP is a command line tool to inform the Taler 
merchant backend that a wire transfer was made to enable tipping from the 
backend.  Note that the command cannot check that the wire transfer was made 
correctly (with the correct wire subject and the specified amount), and will 
thus just trust the operator.  Enabling tipping at an instance without a wire 
transfer may cause visitors to receive unfunded tips and experience error 
messages.  You should read the man [...]
-
-.SH OPTIONS
-.B
-.IP "\-a VALUE,  \-\-amount=VALUE"
-Which amount was transferred into the reserve at the exchange.  Must be of the 
format CUR:VALUE.FRACTION.
-.B
-.IP "\-b URI,  \-\-backend=URI"
-At which URI does the backend run that we are to inform about the availability 
of funding for tipping?
-.B
-.IP "\-C UUID,  \-\-credit-uuid=UUID"
-What is the UUID of the wire transfer.  The backend will automatically detect 
if the same UUID is used repeatedly, and ignore multiple invocations.  Pass a 
UUID generated by the wire transfer of the bank.
-.B
-.IP "\-e TIMESTAMP,  \-\-expiration=TIMESTAMP"
-When does the reserve expire.  Determining this value may today require 
information from the exchange operator.
-.B
-.IP "\-h, \-\-help"
-Print short help on options.
-.B
-.IP "\-i NAME, \-\-instance=NAME"
-Name of the instance where tipping is to be enabled.  The instance must have 
the reserve (private) key and exchange URI already configured. Note that this 
command-line tool must also have access to the same configuration with the 
instance's private key in it.
-.B
-.IP "\-v, \-\-version"
-Print version information.
-.B
-
-.SH SIGNALS
-.B
-.IP SIGTERM
-Sending a SIGTERM to the process will cause it to shutdown cleanly.
-
-.SH BUGS
-Report bugs by using Mantis <https://gnunet.org/bugs/> or by sending 
electronic mail to <address@hidden>
-
-.SH "SEE ALSO"
-\fBtaler\-merchant\-httpd\fP(1), \fBtaler.conf\fP(5)
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 648f2af..a83a185 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -23,7 +23,6 @@ taler_merchant_httpd_SOURCES = \
   taler-merchant-httpd_pay.c taler-merchant-httpd_pay.h \
   taler-merchant-httpd_history.c taler-merchant-httpd_history.h \
   taler-merchant-httpd_tip-authorize.c taler-merchant-httpd_tip-authorize.h \
-  taler-merchant-httpd_tip-enable.c taler-merchant-httpd_tip-enable.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_track-transaction.c 
taler-merchant-httpd_track-transaction.h \
diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index 998d3b0..0ae5d39 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -245,12 +245,6 @@ url_handler (void *cls,
       { "/tip-pickup", NULL, "application/json",
         "Only POST is allowed", 0,
         &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED},
-      { "/tip-enable", MHD_HTTP_METHOD_POST, "text/plain",
-        NULL, 0,
-        &MH_handler_tip_enable, MHD_HTTP_OK},
-      { "/tip-enable", NULL, "application/json",
-        "Only POST is allowed", 0,
-        &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED},
       { "/tip-query", MHD_HTTP_METHOD_GET, "text/plain",
         NULL, 0,
         &MH_handler_tip_query, MHD_HTTP_OK},
@@ -321,6 +315,7 @@ hashmap_free (void *cls,
   json_decref (mi->j_wire);
   GNUNET_free (mi->id);
   GNUNET_free (mi->keyfile);
+  GNUNET_free (mi->name);
   GNUNET_free_non_null (mi->tip_exchange);
   GNUNET_free (mi);
   return GNUNET_YES;
@@ -558,7 +553,10 @@ instances_iterator_cb (void *cls,
   mi = GNUNET_new (struct MerchantInstance);
 
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (iic->config, section, "NAME", 
&mi->name))
+      GNUNET_CONFIGURATION_get_value_string (iic->config,
+                                             section,
+                                             "NAME",
+                                             &mi->name))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                section,
@@ -577,6 +575,7 @@ instances_iterator_cb (void *cls,
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                section,
                                "KEYFILE");
+    GNUNET_free (mi->name);
     GNUNET_free (mi);
     GNUNET_SCHEDULER_shutdown ();
     return;
@@ -599,6 +598,8 @@ instances_iterator_cb (void *cls,
       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                  section,
                                  "TIP_RESERVE_PRIV_FILENAME");
+      GNUNET_free (mi->keyfile);
+      GNUNET_free (mi->name);
       GNUNET_free (mi);
       GNUNET_SCHEDULER_shutdown ();
       return;
@@ -611,6 +612,8 @@ instances_iterator_cb (void *cls,
                                  "TIP_RESERVE_PRIV_FILENAME",
                                  "Failed to read private key");
       GNUNET_free (tip_reserves);
+      GNUNET_free (mi->keyfile);
+      GNUNET_free (mi->name);
       GNUNET_free (mi);
       GNUNET_SCHEDULER_shutdown ();
       return;
@@ -630,6 +633,7 @@ instances_iterator_cb (void *cls,
   {
     GNUNET_break (0);
     GNUNET_free (mi->keyfile);
+    GNUNET_free (mi->name);
     GNUNET_free (mi);
     GNUNET_SCHEDULER_shutdown ();
     return;
@@ -1301,7 +1305,9 @@ main (int argc,
  * @returns the URL, must be freed with #GNUNET_free
  */
 char *
-TMH_make_absolute_backend_url (struct MHD_Connection *connection, char *path, 
...)
+TMH_make_absolute_backend_url (struct MHD_Connection *connection,
+                               char *path,
+                               ...)
 {
   static CURL *curl = NULL;
   if (NULL == curl)
diff --git a/src/backend/taler-merchant-httpd_pay.c 
b/src/backend/taler-merchant-httpd_pay.c
index a635c65..8f248a2 100644
--- a/src/backend/taler-merchant-httpd_pay.c
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -70,7 +70,7 @@ struct DepositConfirmation
    * URL of the exchange that issued this coin.
    */
   char *exchange_url;
-  
+
   /**
    * Denomination of this coin.
    */
@@ -127,7 +127,7 @@ struct DepositConfirmation
    * #GNUNET_YES if this coin was refunded.
    */
   int refunded;
-  
+
 };
 
 
@@ -203,7 +203,7 @@ struct PayContext
    * URL of the exchange used for the last @e fo.
    */
   const char *current_exchange;
-  
+
   /**
    * Placeholder for #TMH_PARSE_post_json() to keep its internal state.
    */
@@ -272,7 +272,7 @@ struct PayContext
    * set, what is the total amount we already refunded?
    */
   struct TALER_Amount total_refunded;
-  
+
   /**
    * Wire transfer deadline. How soon would the merchant like the
    * wire transfer to be executed? (Can be given by the frontend
@@ -314,7 +314,7 @@ struct PayContext
    * How often have we retried the 'main' transaction?
    */
   unsigned int retry_counter;
-  
+
   /**
    * Number of transactions still pending.  Initially set to
    * @e coins_cnt, decremented on each transaction that
@@ -462,56 +462,61 @@ sign_success_response (struct PayContext *pc)
   const char *errmsg;
   struct GNUNET_CRYPTO_EddsaSignature sig;
   struct PaymentResponsePS mr;
-
+  json_t *resp;
+  struct MHD_Response *mret;
 
   refunds = TM_get_refund_json (pc->mi,
                                &pc->h_contract_terms,
                                &ec,
                                &errmsg);
 
-  if (NULL == refunds) 
+  if (NULL == refunds)
     return TMH_RESPONSE_make_error (ec,
                                    errmsg);
-  
+
   mr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK);
   mr.purpose.size = htonl (sizeof (mr));
   mr.h_contract_terms = pc->h_contract_terms;
-
   GNUNET_CRYPTO_eddsa_sign (&pc->mi->privkey.eddsa_priv,
                             &mr.purpose,
                            &sig);
-
-  json_t *resp = json_pack ("{s:O, s:o, s:o, s:o}",
-                           "contract_terms",
-                           pc->contract_terms,
-                           "sig",
-                           GNUNET_JSON_from_data_auto (&sig),
-                           "h_contract_terms",
-                           GNUNET_JSON_from_data (&pc->h_contract_terms,
-                                                  sizeof (struct 
GNUNET_HashCode)),
-                           "refund_permissions",
-                           refunds);
-
-
-  if (NULL != pc->session_id) {
+  resp = json_pack ("{s:O, s:o, s:o, s:o}",
+                    "contract_terms",
+                    pc->contract_terms,
+                    "sig",
+                    GNUNET_JSON_from_data_auto (&sig),
+                    "h_contract_terms",
+                    GNUNET_JSON_from_data (&pc->h_contract_terms,
+                                           sizeof (struct GNUNET_HashCode)),
+                    "refund_permissions",
+                    refunds);
+
+  if (NULL != pc->session_id)
+  {
     struct GNUNET_CRYPTO_EddsaSignature session_sig;
     struct TALER_MerchantPaySessionSigPS mps;
 
     GNUNET_assert (NULL != pc->order_id);
-
     mps.purpose.size = htonl (sizeof (struct TALER_MerchantPaySessionSigPS));
     mps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAY_SESSION);
-    GNUNET_CRYPTO_hash (pc->order_id, strlen (pc->order_id), &mps.h_order_id);
-    GNUNET_CRYPTO_hash (pc->session_id, strlen (pc->session_id), 
&mps.h_session_id);
+    GNUNET_CRYPTO_hash (pc->order_id,
+                        strlen (pc->order_id),
+                        &mps.h_order_id);
+    GNUNET_CRYPTO_hash (pc->session_id,
+                        strlen (pc->session_id),
+                        &mps.h_session_id);
 
     GNUNET_CRYPTO_eddsa_sign (&pc->mi->privkey.eddsa_priv,
                               &mps.purpose,
                               &session_sig);
-
-    json_object_set (resp, "session_sig", GNUNET_JSON_from_data_auto 
(&session_sig));
+    json_object_set (resp,
+                     "session_sig",
+                     GNUNET_JSON_from_data_auto (&session_sig));
   }
 
-  return TMH_RESPONSE_make_json (resp);
+  mret = TMH_RESPONSE_make_json (resp);
+  json_decref (resp);
+  return mret;
 }
 
 
@@ -522,7 +527,7 @@ sign_success_response (struct PayContext *pc)
  * @param http_status http status code to return
  * @param ec taler error code to return
  * @param msg human readable error message
- */ 
+ */
 static void
 resume_pay_with_error (struct PayContext *pc,
                       unsigned int http_status,
@@ -669,11 +674,11 @@ check_payment_sufficient (struct PayContext *pc)
   }
 
 
-  
+
   /* Now compare exchange wire fee compared to what we are willing to
      pay */
   if (GNUNET_YES !=
-      TALER_amount_cmp_currency (&total_wire_fee,  
+      TALER_amount_cmp_currency (&total_wire_fee,
                                  &pc->max_wire_fee))
   {
     GNUNET_break (0);
@@ -682,7 +687,7 @@ check_payment_sufficient (struct PayContext *pc)
 
   if (GNUNET_OK ==
       TALER_amount_subtract (&wire_fee_delta,
-                             &total_wire_fee, 
+                             &total_wire_fee,
                              &pc->max_wire_fee))
   {
     /* Actual wire fee is indeed higher than our maximum, compute
@@ -704,7 +709,7 @@ check_payment_sufficient (struct PayContext *pc)
                 TALER_amount_subtract (&acc_amount,
                                        &acc_amount,
                                        &pc->total_refunded));
-  
+
   /* Now check that the customer paid enough for the full contract */
   if (-1 == TALER_amount_cmp (&pc->max_fee,
                               &acc_fee))
@@ -1070,7 +1075,7 @@ process_pay_with_exchange (void *cls,
     dc->deposit_fee = denom_details->fee_deposit;
     dc->refund_fee = denom_details->fee_refund;
     dc->wire_fee = *wire_fee;
-    
+
     GNUNET_assert (NULL != pc->mi->j_wire);
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Timing for this payment, wire_deadline: %llu, 
refund_deadline: %llu\n",
@@ -1210,7 +1215,7 @@ check_coin_paid (void *cls,
   for (unsigned int i=0;i<pc->coins_cnt;i++)
   {
     struct DepositConfirmation *dc = &pc->dc[i];
-    
+
     if (GNUNET_YES == dc->found_in_db)
       continue; /* processed earlier */
     /* Get matching coin from results*/
@@ -1246,7 +1251,7 @@ check_coin_paid (void *cls,
     }
     dc->deposit_fee = *deposit_fee;
     dc->refund_fee = *refund_fee;
-    dc->wire_fee = *wire_fee; 
+    dc->wire_fee = *wire_fee;
     dc->amount_with_fee = *amount_with_fee;
     dc->found_in_db = GNUNET_YES;
     pc->pending--;
@@ -1281,7 +1286,7 @@ parse_pay (struct MHD_Connection *connection,
   int res;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_string ("mode",
-                            &mode),                       
+                            &mode),
     GNUNET_JSON_spec_json ("coins",
                           &coins),
     GNUNET_JSON_spec_string ("order_id",
@@ -1301,9 +1306,10 @@ parse_pay (struct MHD_Connection *connection,
     return res;
   }
 
-  pc->session_id = json_string_value (json_object_get (root, "session_id"));
+  pc->session_id = json_string_value (json_object_get (root,
+                                                       "session_id"));
   pc->order_id = order_id;
-
+  GNUNET_assert (NULL == pc->contract_terms);
   qs = db->find_contract_terms (db->cls,
                                 &pc->contract_terms,
                                 order_id,
@@ -1391,10 +1397,8 @@ parse_pay (struct MHD_Connection *connection,
   }
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "/pay: picked instance %s with key %s\n",
-              pc->mi->id,
-              GNUNET_STRINGS_data_to_string_alloc (&pc->mi->pubkey,
-                                                   sizeof (pc->mi->pubkey)));
+              "/pay: picked instance %s\n",
+              pc->mi->id);
 
   {
     struct GNUNET_JSON_Specification espec[] = {
@@ -1582,7 +1586,7 @@ check_coin_refunded (void *cls,
   for (unsigned int i=0;i<pc->coins_cnt;i++)
   {
     struct DepositConfirmation *dc = &pc->dc[i];
-    
+
     /* Get matching coin from results*/
     if (0 != memcmp (coin_pub,
                     &dc->coin_pub,
@@ -1594,7 +1598,7 @@ check_coin_refunded (void *cls,
                                      &pc->total_refunded,
                                      refund_amount));
     }
-  }  
+  }
 }
 
 
@@ -1607,7 +1611,7 @@ check_coin_refunded (void *cls,
  */
 static void
 begin_transaction (struct PayContext *pc)
-{  
+{
   enum GNUNET_DB_QueryStatus qs;
 
   /* Avoid re-trying transactions on soft errors forever! */
@@ -1621,7 +1625,7 @@ begin_transaction (struct PayContext *pc)
                                                           "hint", "Soft 
merchant database error: retry counter exceeded"));
     return;
   }
-  
+
   GNUNET_assert (GNUNET_YES == pc->suspended);
 
   /* First, try to see if we have all we need already done */
@@ -1643,7 +1647,7 @@ begin_transaction (struct PayContext *pc)
   GNUNET_break (GNUNET_OK ==
                TALER_amount_get_zero (pc->amount.currency,
                                       &pc->total_refunded));
-                        
+
   /* Check if some of these coins already succeeded */
   qs = db->find_payments (db->cls,
                          &pc->h_contract_terms,
@@ -1731,7 +1735,7 @@ begin_transaction (struct PayContext *pc)
                             "Merchant database error: could not commit");
       return;
     }
-    
+
     {
       json_t *refunds;
 
@@ -1755,7 +1759,7 @@ begin_transaction (struct PayContext *pc)
                           &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,
@@ -1774,7 +1778,7 @@ begin_transaction (struct PayContext *pc)
                               json_pack ("{s:I, 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)));                    
+                                         "merchant_sig", 
GNUNET_JSON_from_data_auto (&msig)));
       }
       resume_pay_with_response (pc,
                                MHD_HTTP_OK,
@@ -1786,7 +1790,7 @@ begin_transaction (struct PayContext *pc)
   }
   /* Default PC_MODE_PAY mode */
 
-  /* Final termination case: all coins already known, just 
+  /* Final termination case: all coins already known, just
      generate ultimate outcome. */
   if (0 == pc->pending)
   {
@@ -1802,7 +1806,7 @@ begin_transaction (struct PayContext *pc)
       if (0 <= qs)
        qs = db->commit (db->cls);
       if (0 > qs)
-      {    
+      {
        if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
        {
          begin_transaction (pc);
@@ -1824,14 +1828,14 @@ begin_transaction (struct PayContext *pc)
     return;
   }
 
-  
+
   /* Check if transaction is already known, if not store it. */
   {
     struct GNUNET_HashCode h_xwire;
     struct GNUNET_TIME_Absolute xtimestamp;
     struct GNUNET_TIME_Absolute xrefund;
     struct TALER_Amount xtotal_amount;
-    
+
     qs = db->find_transaction (db->cls,
                               &pc->h_contract_terms,
                               &pc->mi->pubkey,
@@ -1876,7 +1880,7 @@ begin_transaction (struct PayContext *pc)
       return;
     }
   }
-  
+
   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
     struct GNUNET_TIME_Absolute now;
@@ -1971,8 +1975,8 @@ begin_transaction (struct PayContext *pc)
        so we can just rollback */
     db->rollback (db->cls);
   }
-  
-  /* Ok, we need to first go to the network. 
+
+  /* Ok, we need to first go to the network.
      Do that interaction in *tiny* transactions. */
   find_next_exchange (pc);
 }
diff --git a/src/backend/taler-merchant-httpd_proposal.c 
b/src/backend/taler-merchant-httpd_proposal.c
index 278a28e..31e3fb3 100644
--- a/src/backend/taler-merchant-httpd_proposal.c
+++ b/src/backend/taler-merchant-httpd_proposal.c
@@ -156,6 +156,7 @@ proposal_put (struct MHD_Connection *connection,
     GNUNET_JSON_spec_end ()
   };
   enum GNUNET_DB_QueryStatus qs;
+  const char *instance;
 
   /* Add order_id if it doesn't exist. */
   if (NULL ==
@@ -246,39 +247,65 @@ proposal_put (struct MHD_Connection *connection,
                          json_integer ((json_int_t) 
default_wire_fee_amortization));
   }
 
-  if (NULL == json_object_get (order, "pay_url"))
+  if (NULL == json_object_get (order,
+                               "pay_url"))
   {
     char *url;
-    url = TMH_make_absolute_backend_url (connection, "pay", NULL);
-    json_object_set_new (order, "pay_url", json_string (url));
+
+    url = TMH_make_absolute_backend_url (connection,
+                                         "pay",
+                                         NULL);
+    json_object_set_new (order,
+                         "pay_url",
+                         json_string (url));
+    GNUNET_free (url);
   }
 
-  if (NULL == json_object_get (order, "products"))
+  if (NULL == json_object_get (order,
+                               "products"))
   {
     // FIXME:  When there is no explicit product,
     // should we create a singleton product list?
-    json_object_set_new (order, "products", json_array ());
+    json_object_set_new (order,
+                         "products",
+                         json_array ());
   }
 
-  const char *instance = json_string_value (json_object_get (order, 
"instance"));
+  instance = json_string_value (json_object_get (order,
+                                                 "instance"));
   if (NULL != instance)
   {
     // The frontend either fully specifieds the "merchant" field, or just gives
     // the backend the "instance" name and lets it fill out.
     struct MerchantInstance *mi = TMH_lookup_instance (instance);
+    json_t *merchant;
+
     if (NULL == mi)
     {
       return TMH_RESPONSE_reply_internal_error (connection,
                                                 
TALER_EC_PROPOSAL_ORDER_PARSE_ERROR,
                                                 "merchant instance not found");
     }
-    json_t *merchant = json_object ();
-    json_object_set_new (merchant, "instance", json_string (instance));
-    json_object_set_new (merchant, "name", json_string (mi->name));
-    json_object_set_new (merchant, "jurisdiction", json_string ("none"));
-    json_object_set_new (merchant, "address", json_string ("none"));
-    json_object_set_new (order, "merchant", merchant);
-    json_object_del (order, "instance");
+    merchant = json_object ();
+    /* FIXME: should the 'instance' field really be included in the
+       contract? This is really internal to the business! */
+    json_object_set_new (merchant,
+                         "instance",
+                         json_string (instance));
+    json_object_set_new (merchant,
+                         "name",
+                         json_string (mi->name));
+    json_object_set_new (merchant,
+                         "jurisdiction",
+                         json_string ("none"));
+    json_object_set_new (merchant,
+                         "address",
+                         json_string ("none"));
+    json_object_set_new (order,
+                         "merchant",
+                         merchant);
+    json_object_del (order,
+                     "instance");
   }
 
   /* extract fields we need to sign separately */
diff --git a/src/backend/taler-merchant-httpd_refund.c 
b/src/backend/taler-merchant-httpd_refund.c
index 8df1ea4..c81be52 100644
--- a/src/backend/taler-merchant-httpd_refund.c
+++ b/src/backend/taler-merchant-httpd_refund.c
@@ -429,6 +429,7 @@ MH_handler_refund_lookup (struct TMH_RequestHandler *rh,
   }
 
   /* Convert order id to h_contract_terms */
+  contract_terms = NULL;
   qs = db->find_contract_terms (db->cls,
                                &contract_terms,
                                order_id,
@@ -462,25 +463,32 @@ MH_handler_refund_lookup (struct TMH_RequestHandler *rh,
     GNUNET_break (0);
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Could not hash contract terms\n");
+    json_decref (contract_terms);
     return TMH_RESPONSE_reply_internal_error (connection,
                                               TALER_EC_INTERNAL_LOGIC_ERROR,
                                               "Could not hash contract terms");
   }
+  json_decref (contract_terms);
 
-  json_t *response;
-  enum TALER_ErrorCode ec;
-  const char *errmsg;
-
-  response = TM_get_refund_json (mi, &h_contract_terms, &ec, &errmsg);
-
-  if (NULL == response) {
-    return TMH_RESPONSE_reply_internal_error (connection, ec, errmsg);
+  {
+    json_t *response;
+    enum TALER_ErrorCode ec;
+    const char *errmsg;
+
+    response = TM_get_refund_json (mi,
+                                   &h_contract_terms,
+                                   &ec,
+                                   &errmsg);
+    if (NULL == response)
+      return TMH_RESPONSE_reply_internal_error (connection,
+                                                ec,
+                                                errmsg);
+    return TMH_RESPONSE_reply_json_pack (connection,
+                                         MHD_HTTP_OK,
+                                         "{s:o}",
+                                         "refund_permissions",
+                                         response);
   }
-
-  return TMH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:o}",
-                                       "refund_permissions", response);
 }
 
 
diff --git a/src/backend/taler-merchant-httpd_tip-authorize.c 
b/src/backend/taler-merchant-httpd_tip-authorize.c
index c55b41b..89f198d 100644
--- a/src/backend/taler-merchant-httpd_tip-authorize.c
+++ b/src/backend/taler-merchant-httpd_tip-authorize.c
@@ -29,13 +29,8 @@
 #include "taler-merchant-httpd_tip-authorize.h"
 
 
-/**
- * Information we keep for individual calls
- * to requests that parse JSON, but keep no other state.
- */
-struct TMH_JsonParseContext
+struct TipAuthContext
 {
-
   /**
    * This field MUST be first.
    * FIXME: Explain why!
@@ -46,21 +41,224 @@ struct TMH_JsonParseContext
    * Placeholder for #TMH_PARSE_post_json() to keep its internal state.
    */
   void *json_parse_context;
+
+  /**
+   * HTTP connection we are handling.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * Tip amount requested.
+   */
+  struct TALER_Amount amount;
+
+  /**
+   * Merchant instance to use.
+   */
+  const char *instance;
+
+  /**
+   * Justification to use.
+   */
+  const char *justification;
+
+  /**
+   * Pickup URL to use.
+   */
+  const char *pickup_url;
+
+  /**
+   * URL to navigate to after tip.
+   */
+  const char *next_url;
+
+  /**
+   * JSON request received.
+   */
+  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;
+
+  /**
+   * 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.
+   */
+  int checked_status;
+
+  /**
+   * Flag set to #GNUNET_YES when we have parsed the incoming JSON already.
+   */
+  int parsed_json;
 };
 
 
 /**
- * Custom cleanup routine for a `struct TMH_JsonParseContext`.
+ * Custom cleanup routine for a `struct TipAuthContext`.
  *
  * @param hc the `struct TMH_JsonParseContext` to clean up.
  */
 static void
-json_parse_cleanup (struct TM_HandlerContext *hc)
+cleanup_tac (struct TM_HandlerContext *hc)
 {
-  struct TMH_JsonParseContext *jpc = (struct TMH_JsonParseContext *) hc;
+  struct TipAuthContext *tac = (struct TipAuthContext *) hc;
 
-  TMH_PARSE_post_cleanup_callback (jpc->json_parse_context);
-  GNUNET_free (jpc);
+  if (NULL != tac->root)
+  {
+    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_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;
+  struct GNUNET_TIME_Relative idle_reserve_expiration_time;
+
+  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);
+    MHD_resume_connection (tac->connection);
+    TMH_trigger_daemon ();
+    return;
+  }
+
+  /* TODO: get this from the exchange! (See #5254.) */
+  idle_reserve_expiration_time = GNUNET_TIME_relative_multiply 
(GNUNET_TIME_UNIT_WEEKS,
+                                                                4);
+
+  /* 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,
+                                               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 (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);
+        }
+      }
+    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;
+
+  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;
+  }
+  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);
 }
 
 
@@ -81,66 +279,64 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
                           const char *upload_data,
                           size_t *upload_data_size)
 {
-  struct MerchantInstance *mi;
+  struct TipAuthContext *tac;
   int res;
-  struct TALER_Amount amount;
-  const char *instance;
-  const char *justification;
-  const char *pickup_url;
-  const char *next_url;
-  struct GNUNET_JSON_Specification spec[] = {
-    TALER_JSON_spec_amount ("amount", &amount),
-    GNUNET_JSON_spec_string ("instance", &instance),
-    GNUNET_JSON_spec_string ("justification", &justification),
-    GNUNET_JSON_spec_string ("pickup_url", &pickup_url),
-    GNUNET_JSON_spec_string ("next_url", &next_url),
-    GNUNET_JSON_spec_end()
-  };
-  json_t *root;
+  struct MerchantInstance *mi;
+  enum TALER_ErrorCode ec;
   struct GNUNET_TIME_Absolute expiration;
   struct GNUNET_HashCode tip_id;
-  struct TMH_JsonParseContext *ctx;
-  enum TALER_ErrorCode ec;
 
   if (NULL == *connection_cls)
   {
-    ctx = GNUNET_new (struct TMH_JsonParseContext);
-    ctx->hc.cc = &json_parse_cleanup;
-    *connection_cls = ctx;
+    tac = GNUNET_new (struct TipAuthContext);
+    tac->hc.cc = &cleanup_tac;
+    tac->connection = connection;
+    *connection_cls = tac;
   }
   else
   {
-    ctx = *connection_cls;
+    tac = *connection_cls;
   }
-  res = TMH_PARSE_post_json (connection,
-                             &ctx->json_parse_context,
-                             upload_data,
-                             upload_data_size,
-                             &root);
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  /* the POST's body has to be further fetched */
-  if ( (GNUNET_NO == res) ||
-       (NULL == root) )
-    return MHD_YES;
 
-  res = TMH_PARSE_json_data (connection,
-                             root,
-                             spec);
-  if (GNUNET_YES != res)
+  if (GNUNET_NO == tac->parsed_json)
   {
-    GNUNET_break_op (0);
-    json_decref (root);
-    return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+    struct GNUNET_JSON_Specification spec[] = {
+      TALER_JSON_spec_amount ("amount", &tac->amount),
+      GNUNET_JSON_spec_string ("instance", &tac->instance),
+      GNUNET_JSON_spec_string ("justification", &tac->justification),
+      GNUNET_JSON_spec_string ("pickup_url", &tac->pickup_url),
+      GNUNET_JSON_spec_string ("next_url", &tac->next_url),
+      GNUNET_JSON_spec_end()
+    };
+
+    res = TMH_PARSE_post_json (connection,
+                               &tac->json_parse_context,
+                               upload_data,
+                               upload_data_size,
+                               &tac->root);
+    if (GNUNET_SYSERR == res)
+      return MHD_NO;
+    /* the POST's body has to be further fetched */
+    if ( (GNUNET_NO == res) ||
+         (NULL == tac->root) )
+      return MHD_YES;
+    res = TMH_PARSE_json_data (connection,
+                               tac->root,
+                               spec);
+    if (GNUNET_YES != res)
+    {
+      GNUNET_break_op (0);
+      return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+    }
+    tac->parsed_json = GNUNET_YES;
   }
 
-  mi = TMH_lookup_instance (instance);
+  mi = TMH_lookup_instance (tac->instance);
   if (NULL == mi)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Instance `%s' not configured\n",
-                instance);
-    json_decref (root);  
+                tac->instance);
     return TMH_RESPONSE_reply_not_found (connection,
                                         
TALER_EC_TIP_AUTHORIZE_INSTANCE_UNKNOWN,
                                         "unknown instance");
@@ -149,19 +345,35 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Instance `%s' not configured for tipping\n",
-                instance);
-    json_decref (root);
+                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");
   }
+  tac->reserve_priv = mi->tip_reserve;
   ec = db->authorize_tip (db->cls,
-                          justification,
-                          &amount,
+                          tac->justification,
+                          &tac->amount,
                           &mi->tip_reserve,
                          mi->tip_exchange,
                           &expiration,
                           &tip_id);
+  /* If we have insufficient funds according to OUR database,
+     check with exchange to see if the reserve has been topped up
+     in the meantime (or if tips were not withdrawn yet). */
+  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);
+    return MHD_YES;
+  }
+
+  /* handle irrecoverable errors */
   if (TALER_EC_NONE != ec)
   {
     unsigned int rc;
@@ -177,50 +389,35 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
     case TALER_EC_TIP_AUTHORIZE_RESERVE_UNKNOWN:
       rc = MHD_HTTP_NOT_FOUND;
       break;
-    case TALER_EC_TIP_AUTHORIZE_RESERVE_NOT_ENABLED:
-      rc = MHD_HTTP_NOT_FOUND;
-      break;
     default:
       rc = MHD_HTTP_INTERNAL_SERVER_ERROR;
       break;
     }
-    json_decref (root);      
     return TMH_RESPONSE_reply_rc (connection,
-                                 rc,
-                                 ec,
-                                 "Database error approving tip");
+                                  rc,
+                                  ec,
+                                  "Database error approving tip");
   }
-  if (0)
+
+  /* generate success response */
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Insufficient funds to authorize tip over `%s' at instance 
`%s'\n",
-                TALER_amount2s (&amount),
-                instance);
-    json_decref (root);
-    return TMH_RESPONSE_reply_rc (connection,
-                                  MHD_HTTP_PRECONDITION_FAILED,
-                                  TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS,
-                                  "Insufficient funds for tip");
+    json_t *tip_token;
+
+    tip_token = json_pack ("{s:o, s:o, s:o, s:s, s:s, s:s}",
+                           "tip_id", GNUNET_JSON_from_data_auto (&tip_id),
+                           "expiration", GNUNET_JSON_from_time_abs 
(expiration),
+                           "amount", TALER_JSON_from_amount (&tac->amount),
+                           "exchange_url", mi->tip_exchange,
+                           "next_url", tac->next_url,
+                           "pickup_url", tac->pickup_url);
+    return TMH_RESPONSE_reply_json_pack (connection,
+                                         MHD_HTTP_OK,
+                                         "{s:o, s:o, s:s, s:o}",
+                                         "tip_id", GNUNET_JSON_from_data_auto 
(&tip_id),
+                                         "expiration", 
GNUNET_JSON_from_time_abs (expiration),
+                                         "exchange_url", mi->tip_exchange,
+                                         "tip_token", tip_token);
   }
-  json_t *tip_token = json_pack ("{s:o, s:o, s:o, s:s, s:s, s:s}",
-                                 "tip_id", GNUNET_JSON_from_data_auto 
(&tip_id),
-                                 "expiration", GNUNET_JSON_from_time_abs 
(expiration),
-                                 "amount", TALER_JSON_from_amount (&amount),
-                                 "exchange_url", mi->tip_exchange,
-                                 "next_url", next_url,
-                                 "pickup_url", pickup_url);
-  char *tip_token_str = json_dumps (tip_token,  JSON_ENSURE_ASCII | 
JSON_COMPACT);
-  json_decref (tip_token);
-  json_decref (root);
-  int ret = TMH_RESPONSE_reply_json_pack (connection,
-                                          MHD_HTTP_OK,
-                                          "{s:o, s:o, s:s, s:s}",
-                                          "tip_id", GNUNET_JSON_from_data_auto 
(&tip_id),
-                                          "expiration", 
GNUNET_JSON_from_time_abs (expiration),
-                                          "exchange_url", mi->tip_exchange,
-                                          "tip_token", tip_token_str);
-  GNUNET_free (tip_token_str);
-  return ret;
 }
 
 /* end of taler-merchant-httpd_tip-authorize.c */
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index 66a3529..57f11ec 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -1115,7 +1115,7 @@ postgres_store_deposit (void *cls,
               "Storing payment for h_contract_terms `%s', coin_pub: `%s', 
amount_with_fee: %s\n",
               GNUNET_h2s (h_contract_terms),
               TALER_B2S (coin_pub),
-              TALER_amount_to_string (amount_with_fee));
+              TALER_amount2s (amount_with_fee));
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Merchant pub is `%s'\n",
               TALER_B2S (merchant_pub));
@@ -2974,7 +2974,7 @@ postgres_authorize_tip (void *cls,
     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
       goto RETRY;
     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-      return TALER_EC_TIP_AUTHORIZE_RESERVE_NOT_ENABLED;
+      return TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
     return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
   }
   if (0 == GNUNET_TIME_absolute_get_remaining (old_expiration).rel_value_us)
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
index a5dcc35..d912838 100644
--- a/src/backenddb/test_merchantdb.c
+++ b/src/backenddb/test_merchantdb.c
@@ -506,7 +506,7 @@ test_tipping ()
   char *url;
 
   RND_BLK (&tip_reserve_priv);
-  if (TALER_EC_TIP_AUTHORIZE_RESERVE_NOT_ENABLED !=
+  if (TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS !=
       plugin->authorize_tip (plugin->cls,
                              "testing tips reserve unknown",
                              &amount,
@@ -912,7 +912,7 @@ run (void *cls)
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->store_transaction (plugin->cls,
                                      &h_contract_terms,
-                                    &merchant_pub,                             
       
+                                    &merchant_pub,
                                      &h_wire,
                                      timestamp,
                                      refund_deadline,
@@ -964,7 +964,7 @@ run (void *cls)
                                   &amount_with_fee));
   }
 
-  
+
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->find_payments (plugin->cls,
                                  &h_contract_terms,
diff --git a/src/include/taler_merchant_service.h 
b/src/include/taler_merchant_service.h
index 63c396a..adcf006 100644
--- a/src/include/taler_merchant_service.h
+++ b/src/include/taler_merchant_service.h
@@ -311,7 +311,7 @@ struct TALER_MERCHANT_PayCoin
    * Amount this coin is to contribute (without fee).
    */
   struct TALER_Amount amount_without_fee;
-  
+
   /**
    * Fee the exchange charges for refunds of this coin.
    */
@@ -319,7 +319,7 @@ struct TALER_MERCHANT_PayCoin
 
   /**
    * URL of the exchange that issued @e coin_priv.
-   */ 
+   */
   const char *exchange_url;
 
 };
@@ -375,12 +375,12 @@ struct TALER_MERCHANT_RefundEntry
 {
   /**
    * Merchant signature affirming the refund.
-   */ 
+   */
   struct TALER_MerchantSignatureP merchant_sig;
 
   /**
    * Public key of the refunded coin.
-   */ 
+   */
   struct TALER_CoinSpendPublicKeyP coin_pub;
 
   /**
@@ -791,71 +791,6 @@ void
 TALER_MERCHANT_history_cancel (struct TALER_MERCHANT_HistoryOperation *ho);
 
 
-/* ********************** /tip-enable ************************* */
-
-
-/**
- * Handle for a /tip-enable operation.
- */
-struct TALER_MERCHANT_TipEnableOperation;
-
-
-/**
- * Callback for a /tip-enable request.  Returns the result of
- * the operation.
- *
- * @param cls closure
- * @param http_status HTTP status returned by the merchant backend
- * @param ec taler-specific error code
- */
-typedef void
-(*TALER_MERCHANT_TipEnableCallback) (void *cls,
-                                     unsigned int http_status,
-                                     enum TALER_ErrorCode ec);
-
-
-/**
- * Issue a /tip-enable request to the backend.  Informs the backend
- * that a reserve is now available for tipping.  Note that the
- * respective @a reserve_priv must also be bound to one or more
- * instances (together with the URL of the exchange) via the backend's
- * configuration file before it can be used.  Usually, the process
- * is that one first configures an exchange and a @a reserve_priv for
- * an instance, and then enables (or re-enables) the reserve by
- * performing wire transfers and informs the backend about it using
- * this API.
- *
- * @param ctx execution context
- * @param backend_url base URL of the merchant backend
- * @param amount amount that was credited to the reserve
- * @param expiration when will the reserve expire
- * @param reserve_priv private key of the reserve
- * @param credit_uuid unique ID of the wire transfer
- * @param enable_cb callback which will work the response gotten from the 
backend
- * @param enable_cb_cls closure to pass to @a enable_cb
- * @return handle for this operation, NULL upon errors
- */
-struct TALER_MERCHANT_TipEnableOperation *
-TALER_MERCHANT_tip_enable (struct GNUNET_CURL_Context *ctx,
-                           const char *backend_url,
-                           const struct TALER_Amount *amount,
-                           struct GNUNET_TIME_Absolute expiration,
-                           const struct TALER_ReservePrivateKeyP *reserve_priv,
-                           const struct GNUNET_HashCode *credit_uuid,
-                           TALER_MERCHANT_TipEnableCallback enable_cb,
-                           void *enable_cb_cls);
-
-
-
-/**
- * Cancel a pending /tip-enable request
- *
- * @param teo handle from the operation to cancel
- */
-void
-TALER_MERCHANT_tip_enable_cancel (struct TALER_MERCHANT_TipEnableOperation 
*teo);
-
-
 /* ********************** /tip-authorize ********************** */
 
 /**
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 5f46122..4c6d5f8 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -18,7 +18,6 @@ libtalermerchant_la_SOURCES = \
   merchant_api_proposal.c \
   merchant_api_pay.c \
   merchant_api_tip_authorize.c \
-  merchant_api_tip_enable.c \
   merchant_api_tip_pickup.c \
   merchant_api_track_transaction.c \
   merchant_api_track_transfer.c \
diff --git a/src/lib/merchant_api_tip_enable.c 
b/src/lib/merchant_api_tip_enable.c
deleted file mode 100644
index eb7dadf..0000000
--- a/src/lib/merchant_api_tip_enable.c
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014-2017 Taler Systems SA
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU Lesser General Public License as published by the Free 
Software
-  Foundation; either version 2.1, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
details.
-
-  You should have received a copy of the GNU Lesser General Public License 
along with
-  TALER; see the file COPYING.LGPL.  If not, see
-  <http://www.gnu.org/licenses/>
-*/
-/**
- * @file lib/merchant_api_tip_enable.c
- * @brief Implementation of the /tip-enable request of the merchant's HTTP API
- * @author Marcello Stanisci
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <curl/curl.h>
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler_merchant_service.h"
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include "merchant_api_common.h"
-
-
-/**
- * @brief A handle for tracking transactions.
- */
-struct TALER_MERCHANT_TipEnableOperation
-{
-
-  /**
-   * The url for this request.
-   */
-  char *url;
-
-  /**
-   * JSON encoding of the request to POST.
-   */
-  char *json_enc;
-
-  /**
-   * Handle for the request.
-   */
-  struct GNUNET_CURL_Job *job;
-
-  /**
-   * Function to call with the result.
-   */
-  TALER_MERCHANT_TipEnableCallback cb;
-
-  /**
-   * Closure for @a cb.
-   */
-  void *cb_cls;
-
-  /**
-   * Reference to the execution context.
-   */
-  struct GNUNET_CURL_Context *ctx;
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP /track/transaction request.
- *
- * @param cls the `struct TALER_MERCHANT_TipEnableOperation`
- * @param response_code HTTP response code, 0 on error
- * @param json response body, NULL if not in JSON
- */
-static void
-handle_tip_enable_finished (void *cls,
-                            long response_code,
-                            const json_t *json)
-{
-  struct TALER_MERCHANT_TipEnableOperation *teo = cls;
-
-  teo->job = NULL;
-  switch (response_code)
-  {
-  case MHD_HTTP_OK:
-    break;
-  case MHD_HTTP_INTERNAL_SERVER_ERROR:
-    /* Server had an internal issue; we should retry, but this API
-       leaves this to the application */
-    break;
-  default:
-    /* unexpected response code */
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u\n",
-                (unsigned int) response_code);
-    GNUNET_break (0);
-    response_code = 0;
-    break;
-  }
-  teo->cb (teo->cb_cls,
-           response_code,
-          TALER_JSON_get_error_code (json));
-  TALER_MERCHANT_tip_enable_cancel (teo);
-}
-
-
-/**
- * Issue a /tip-enable request to the backend.  Informs the backend
- * that a reserve is now available for tipping.  Note that the
- * respective @a reserve_priv must also be bound to one or more
- * instances (together with the URI of the exchange) via the backend's
- * configuration file before it can be used.  Usually, the process
- * is that one first configures an exchange and a @a reserve_priv for
- * an instance, and then enables (or re-enables) the reserve by
- * performing wire transfers and informs the backend about it using
- * this API.
- *
- * @param ctx execution context
- * @param backend_uri base URL of the merchant backend
- * @param amount amount that was credited to the reserve
- * @param expiration when will the reserve expire
- * @param reserve_priv private key of the reserve
- * @param credit_uuid unique ID of the wire transfer
- * @param enable_cb callback which will work the response gotten from the 
backend
- * @param enable_cb_cls closure to pass to @a enable_cb
- * @return handle for this operation, NULL upon errors
- */
-struct TALER_MERCHANT_TipEnableOperation *
-TALER_MERCHANT_tip_enable (struct GNUNET_CURL_Context *ctx,
-                           const char *backend_uri,
-                           const struct TALER_Amount *amount,
-                           struct GNUNET_TIME_Absolute expiration,
-                           const struct TALER_ReservePrivateKeyP *reserve_priv,
-                           const struct GNUNET_HashCode *credit_uuid,
-                           TALER_MERCHANT_TipEnableCallback enable_cb,
-                           void *enable_cb_cls)
-{
-  struct TALER_MERCHANT_TipEnableOperation *teo;
-  CURL *eh;
-  json_t *te_obj;
-
-  (void) GNUNET_TIME_round_abs (&expiration);
-  teo = GNUNET_new (struct TALER_MERCHANT_TipEnableOperation);
-  teo->ctx = ctx;
-  teo->cb = enable_cb;
-  teo->cb_cls = enable_cb_cls;
-  teo->url = MAH_path_to_url_ (backend_uri,
-                               "/tip-enable");
-  te_obj = json_pack ("{"
-                      " s:o," /* amount */
-                      " s:o," /* expiration */
-                      " s:o," /* credit_uuid */
-                      " s:o," /* reserve_priv */
-                      "}",
-                      "credit", TALER_JSON_from_amount (amount),
-                      "expiration", GNUNET_JSON_from_time_abs (expiration),
-                      "credit_uuid", GNUNET_JSON_from_data_auto (credit_uuid),
-                      "reserve_priv", GNUNET_JSON_from_data_auto 
(reserve_priv));
-  if (NULL == te_obj)
-  {
-    GNUNET_break (0);
-    GNUNET_free (teo->url);
-    GNUNET_free (teo);
-    return NULL;
-  }
-  if (NULL == (teo->json_enc =
-               json_dumps (te_obj,
-                           JSON_COMPACT)))
-  {
-    GNUNET_break (0);
-    json_decref (te_obj);
-    GNUNET_free (teo->url);
-    GNUNET_free (teo);
-    return NULL;
-  }
-  json_decref (te_obj);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Requesting URI '%s'\n",
-              teo->url);
-  eh = curl_easy_init ();
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_URL,
-                                   teo->url));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDS,
-                                   teo->json_enc));
-  GNUNET_assert (CURLE_OK ==
-                 curl_easy_setopt (eh,
-                                   CURLOPT_POSTFIELDSIZE,
-                                   strlen (teo->json_enc)));
-  teo->job = GNUNET_CURL_job_add (ctx,
-                                  eh,
-                                  GNUNET_YES,
-                                  &handle_tip_enable_finished,
-                                  teo);
-  return teo;
-}
-
-
-/**
- * Cancel a /track/transaction request.  This function cannot be used
- * on a request handle if a response is already served for it.
- *
- * @param teo handle to the tracking operation being cancelled
- */
-void
-TALER_MERCHANT_tip_enable_cancel (struct TALER_MERCHANT_TipEnableOperation 
*teo)
-{
-  if (NULL != teo->job)
-  {
-    GNUNET_CURL_job_cancel (teo->job);
-    teo->job = NULL;
-  }
-  GNUNET_free_non_null (teo->json_enc);
-  GNUNET_free (teo->url);
-  GNUNET_free (teo);
-}
-
-/* end of merchant_api_tip_enable.c */
diff --git a/src/lib/merchant_api_tip_pickup.c 
b/src/lib/merchant_api_tip_pickup.c
index e967588..cc84cea 100644
--- a/src/lib/merchant_api_tip_pickup.c
+++ b/src/lib/merchant_api_tip_pickup.c
@@ -189,6 +189,7 @@ handle_tip_pickup_finished (void *cls,
     break;
   }
   if (NULL != tpo->cb)
+  {
     tpo->cb (tpo->cb_cls,
              response_code,
              TALER_JSON_get_error_code (json),
@@ -196,6 +197,8 @@ handle_tip_pickup_finished (void *cls,
              0,
              NULL,
              json);
+    tpo->cb = NULL;
+  }
   TALER_MERCHANT_tip_pickup_cancel (tpo);
 }
 
diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c
index ea119a1..219b407 100644
--- a/src/lib/test_merchant_api.c
+++ b/src/lib/test_merchant_api.c
@@ -175,14 +175,14 @@ enum OpCode
 
   /**
    * Resume pay operation with additional coins.
-   */ 
+   */
   OC_PAY_AGAIN,
-  
+
   /**
    * Abort payment with coins, requesting refund.
    */
   OC_PAY_ABORT,
-  
+
   /**
    * Abort payment with coins, executing refund.
    */
@@ -234,11 +234,6 @@ enum OpCode
   OC_REFUND_LOOKUP,
 
   /**
-   * Start a reserve for tipping.
-   */
-  OC_TIP_ENABLE,
-
-  /**
    * Authorize a tip.
    */
   OC_TIP_AUTHORIZE,
@@ -562,12 +557,12 @@ struct Command
        */
       const char *amount_without_fee;
 
-      /** 
+      /**
        * Refund fee to use for each coin (only relevant if we
        * exercise /pay's abort functionality).
        */
       const char *refund_fee;
-      
+
       /**
        * Pay handle while operation is running.
        */
@@ -605,7 +600,7 @@ struct Command
        * Pay handle while operation is running.
        */
       struct TALER_MERCHANT_Pay *ph;
-      
+
     } pay_again;
 
     struct {
@@ -620,7 +615,7 @@ struct Command
        */
       struct TALER_MERCHANT_Pay *ph;
 
-      /** 
+      /**
        * Set in #pay_refund_cb to number of refunds obtained.
        */
       unsigned int num_refunds;
@@ -632,12 +627,12 @@ struct Command
 
       /**
        * Set to the hash of the original contract.
-       */ 
-      struct GNUNET_HashCode h_contract;      
+       */
+      struct GNUNET_HashCode h_contract;
 
       /**
        * Set to the merchant's public key.
-       */ 
+       */
       struct TALER_MerchantPublicKeyP merchant_pub;
 
     } pay_abort;
@@ -646,7 +641,7 @@ struct Command
 
       /**
        * Reference to the pay_abort command to be refunded.
-       */ 
+       */
       const char *abort_ref;
 
       /**
@@ -663,7 +658,7 @@ struct Command
        * Refund fee to expect.
        */
       const char *refund_fee;
-      
+
       /**
        * Handle to the refund operation.
        */
@@ -1389,8 +1384,9 @@ reserve_status_cb (void *cls,
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
+                ec,
                 cmd->label);
     GNUNET_break (0);
     json_dumpf (json, stderr, 0);
@@ -1513,8 +1509,9 @@ reserve_withdraw_cb (void *cls,
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
+                ec,
                 cmd->label);
     fail (is);
     return;
@@ -1613,8 +1610,9 @@ proposal_cb (void *cls,
   default: {
     char *s = json_dumps (obj, JSON_COMPACT);
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected status code from /proposal: %u. Step %u, response: 
%s\n",
+                "Unexpected status code from /proposal: %u (%d). Step %u, 
response: %s\n",
                 http_status,
+                ec,
                 is->ip,
                 s);
     GNUNET_free_non_null (s);
@@ -1847,8 +1845,9 @@ pay_cb (void *cls,
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
+                ec,
                 cmd->label);
     fail (is);
     return;
@@ -1914,15 +1913,16 @@ pay_again_cb (void *cls,
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
+                ec,
                 cmd->label);
     fail (is);
     return;
   }
   GNUNET_assert (NULL != (pref = find_command
                          (is,
-                          cmd->details.pay_again.pay_ref)));    
+                          cmd->details.pay_again.pay_ref)));
   if (MHD_HTTP_OK == http_status)
   {
     struct PaymentResponsePS mr;
@@ -1934,7 +1934,7 @@ pay_again_cb (void *cls,
                                   &mr.h_contract_terms),
       GNUNET_JSON_spec_end ()
     };
-    
+
     GNUNET_assert (GNUNET_OK ==
         GNUNET_JSON_parse (obj,
                            spec,
@@ -2034,8 +2034,9 @@ track_transfer_cb (void *cls,
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
+                ec,
                 cmd->label);
     fail (is);
     return;
@@ -2099,8 +2100,9 @@ track_transaction_cb (void *cls,
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
+                ec,
                 cmd->label);
     fail (is);
     return;
@@ -2112,45 +2114,6 @@ track_transaction_cb (void *cls,
 
 
 /**
- * Callback for a /tip-enable request.  Returns the result of
- * the operation.
- *
- * @param cls closure
- * @param http_status HTTP status returned by the merchant backend
- * @param ec taler-specific error code
- */
-static void
-tip_enable_cb (void *cls,
-               unsigned int http_status,
-               enum TALER_ErrorCode ec)
-{
-  struct InterpreterState *is = cls;
-  struct Command *cmd = &is->commands[is->ip];
-
-  cmd->details.tip_enable.teo = NULL;
-  if (cmd->expected_response_code != http_status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
-                http_status,
-                cmd->label);
-    fail (is);
-    return;
-  }
-  if (cmd->details.tip_enable.expected_ec != ec)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected error code %u to command %s\n",
-                ec,
-                cmd->label);
-    fail (is);
-    return;
-  }
-  next_command (is);
-}
-
-
-/**
  * Callback for a /tip-authorize request.  Returns the result of
  * the operation.
  *
@@ -2176,8 +2139,9 @@ tip_authorize_cb (void *cls,
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
+                ec,
                 cmd->label);
     fail (is);
     return;
@@ -2185,8 +2149,9 @@ tip_authorize_cb (void *cls,
   if (cmd->details.tip_authorize.expected_ec != ec)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected error code %u to command %s\n",
+                "Unexpected error code %d (%u) to command %s\n",
                 ec,
+                http_status,
                 cmd->label);
     fail (is);
     return;
@@ -2235,7 +2200,8 @@ pickup_withdraw_cb (void *cls,
 
   wh->wsh = NULL;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Withdraw operation completed with %u/%u\n",
+              "Withdraw operation %u completed with %u (%d)\n",
+              wh->off,
               http_status,
               ec);
   GNUNET_assert (wh->off < cmd->details.tip_pickup.num_coins);
@@ -2243,7 +2209,7 @@ pickup_withdraw_cb (void *cls,
        (TALER_EC_NONE != ec) )
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u/%u to command %s when 
withdrawing\n",
+                "Unexpected response code %u (%d) to command %s when 
withdrawing\n",
                 http_status,
                 ec,
                 cmd->label);
@@ -2253,6 +2219,7 @@ pickup_withdraw_cb (void *cls,
   if (NULL == cmd->details.tip_pickup.sigs)
     cmd->details.tip_pickup.sigs = GNUNET_new_array 
(cmd->details.tip_pickup.num_coins,
                                                      struct 
TALER_DenominationSignature);
+  GNUNET_assert (NULL == cmd->details.tip_pickup.sigs[wh->off].rsa_signature);
   cmd->details.tip_pickup.sigs[wh->off].rsa_signature
     = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature);
   for (unsigned int i=0;i<cmd->details.tip_pickup.num_coins;i++)
@@ -2292,7 +2259,7 @@ pickup_cb (void *cls,
   if (http_status != cmd->expected_response_code)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u/%u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
                 ec,
                 cmd->label);
@@ -2323,6 +2290,7 @@ pickup_cb (void *cls,
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Obtained %u signatures for withdrawal from picking up a tip\n",
               num_reserve_sigs);
+  GNUNET_assert (NULL == cmd->details.tip_pickup.withdraws);
   cmd->details.tip_pickup.withdraws
     = GNUNET_new_array (num_reserve_sigs,
                         struct WithdrawHandle);
@@ -2332,6 +2300,9 @@ pickup_cb (void *cls,
 
     wh->off = i;
     wh->is = is;
+    GNUNET_assert ( (NULL == wh->wsh) &&
+                    ( (NULL == cmd->details.tip_pickup.sigs) ||
+                      (NULL == 
cmd->details.tip_pickup.sigs[wh->off].rsa_signature) ) );
     wh->wsh = TALER_EXCHANGE_reserve_withdraw2 (exchange,
                                                 cmd->details.tip_pickup.dks[i],
                                                 &reserve_sigs[i],
@@ -2616,13 +2587,6 @@ cleanup_state (struct InterpreterState *is)
         cmd->details.refund_lookup.rlo = NULL;
       }
       break;
-    case OC_TIP_ENABLE:
-      if (NULL != cmd->details.tip_enable.teo)
-      {
-        TALER_MERCHANT_tip_enable_cancel (cmd->details.tip_enable.teo);
-        cmd->details.tip_enable.teo = NULL;
-      }
-      break;
     case OC_TIP_AUTHORIZE:
       if (NULL != cmd->details.tip_authorize.tao)
       {
@@ -2665,12 +2629,14 @@ cleanup_state (struct InterpreterState *is)
       {
         for (unsigned int j=0;j<cmd->details.tip_pickup.num_coins;j++)
         {
-          if (NULL != cmd->details.reserve_withdraw.sig.rsa_signature)
+          if (NULL != cmd->details.tip_pickup.sigs[j].rsa_signature)
           {
             GNUNET_CRYPTO_rsa_signature_free 
(cmd->details.tip_pickup.sigs[j].rsa_signature);
             cmd->details.tip_pickup.sigs[j].rsa_signature = NULL;
           }
         }
+        GNUNET_free (cmd->details.tip_pickup.sigs);
+        cmd->details.tip_pickup.sigs = NULL;
       }
       break;
     default:
@@ -2689,7 +2655,7 @@ cleanup_state (struct InterpreterState *is)
  * Parse the @a coins specification and grow the @a pc
  * array with the coins found, updating @a npc.
  *
- * @param[in,out] pc pointer to array of coins found 
+ * @param[in,out] pc pointer to array of coins found
  * @param[in,out] npc length of array at @a pc
  * @param[in] coins string specifying coins to add to @a pc,
  *            clobbered in the process
@@ -2704,8 +2670,8 @@ build_coins (struct TALER_MERCHANT_PayCoin **pc,
             struct InterpreterState *is,
             const struct Command *pref)
 {
-  char *token;  
-  
+  char *token;
+
   for (token = strtok (coins, ";");
        NULL != token;
        token = strtok (NULL, ";"))
@@ -2714,7 +2680,7 @@ build_coins (struct TALER_MERCHANT_PayCoin **pc,
     char *ctok;
     unsigned int ci;
     struct TALER_MERCHANT_PayCoin *icoin;
-    
+
     /* Token syntax is "LABEL[/NUMBER]" */
     ctok = strchr (token, '/');
     ci = 0;
@@ -2755,7 +2721,7 @@ build_coins (struct TALER_MERCHANT_PayCoin **pc,
     default:
       GNUNET_assert (0);
     }
-    
+
     GNUNET_assert (GNUNET_OK ==
                   TALER_string_to_amount (pref->details.pay.amount_with_fee,
                                           &icoin->amount_with_fee));
@@ -2798,13 +2764,14 @@ pay_refund_cb (void *cls,
 {
   struct InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
-  
+
   cmd->details.pay_abort.ph = NULL;
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
+                ec,
                 cmd->label);
     fail (is);
     return;
@@ -2847,13 +2814,14 @@ abort_refund_cb (void *cls,
 {
   struct InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
-  
+
   cmd->details.pay_abort_refund.rh = NULL;
   if (cmd->expected_response_code != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u to command %s\n",
+                "Unexpected response code %u (%d) to command %s\n",
                 http_status,
+                ec,
                 cmd->label);
     fail (is);
     return;
@@ -3358,7 +3326,7 @@ interpreter_run (void *cls)
       GNUNET_break (0);
       fail (is);
     }
-    break;    
+    break;
   case OC_PAY_ABORT:
     {
       struct TALER_MERCHANT_PayCoin *pc;
@@ -3489,7 +3457,7 @@ interpreter_run (void *cls)
       GNUNET_assert (ref->details.pay_abort.num_refunds >
                     cmd->details.pay_abort_refund.num_coin);
       re = &ref->details.pay_abort.res[cmd->details.pay_abort_refund.num_coin];
-                    
+
       GNUNET_assert (GNUNET_OK ==
                     TALER_string_to_amount
                     (cmd->details.pay_abort_refund.refund_amount,
@@ -3719,83 +3687,6 @@ interpreter_run (void *cls)
       }
       break;
     }
-  case OC_TIP_ENABLE:
-    {
-      const struct Command *uuid_ref;
-      struct TALER_ReservePrivateKeyP reserve_priv;
-      struct GNUNET_TIME_Absolute expiration;
-
-      if (NULL != cmd->details.tip_enable.admin_add_incoming_ref)
-      {
-        ref = find_command (is,
-                            cmd->details.tip_enable.admin_add_incoming_ref);
-        GNUNET_assert (NULL != ref);
-        GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
-      }
-      else
-      {
-        ref = NULL;
-      }
-
-      /* Initialize 'credit_uuid' */
-      if (NULL != cmd->details.tip_enable.uuid_ref)
-      {
-        uuid_ref = find_command (is,
-                                 cmd->details.tip_enable.uuid_ref);
-        GNUNET_assert (NULL != uuid_ref);
-        GNUNET_assert (OC_TIP_ENABLE == uuid_ref->oc);
-        cmd->details.tip_enable.credit_uuid
-          = uuid_ref->details.tip_enable.credit_uuid;
-      }
-      else
-      {
-        uuid_ref = NULL;
-        GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
-                                    &cmd->details.tip_enable.credit_uuid,
-                                    sizeof 
(cmd->details.tip_enable.credit_uuid));
-      }
-
-      /* Initialize 'amount' */
-      if ( (NULL != ref) &&
-           (NULL == cmd->details.tip_enable.amount) )
-      {
-        GNUNET_assert (GNUNET_OK ==
-                       TALER_string_to_amount 
(ref->details.admin_add_incoming.amount,
-                                               &amount));
-      }
-      else
-      {
-        GNUNET_assert (NULL != cmd->details.tip_enable.amount);
-        GNUNET_assert (GNUNET_OK ==
-                       TALER_string_to_amount (cmd->details.tip_enable.amount,
-                                               &amount));
-      }
-      if (NULL == ref)
-        GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
-                                    &reserve_priv,
-                                    sizeof (reserve_priv));
-      /* Simply picked long enough for the test (we do not test expiration
-         behavior for now), should be short enough so that the reserve
-        expires before the test is run again, so that we avoid old
-        state messing up fresh runs. */
-      expiration = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
-
-      if (NULL == (cmd->details.tip_enable.teo
-                   = TALER_MERCHANT_tip_enable
-                   (ctx,
-                    MERCHANT_URL,
-                    &amount,
-                    expiration,
-                    (ref != NULL) ? 
&ref->details.admin_add_incoming.reserve_priv : &reserve_priv,
-                    &cmd->details.tip_enable.credit_uuid,
-                    &tip_enable_cb,
-                    is)))
-      {
-        GNUNET_break (0);
-        fail (is);
-      }
-      break;
-    }
   case OC_TIP_AUTHORIZE:
     {
       GNUNET_assert (NULL != cmd->details.tip_authorize.amount);
@@ -3865,14 +3756,13 @@ interpreter_run (void *cls)
           }
         }
         if (NULL == (cmd->details.tip_pickup.tpo
-                     = TALER_MERCHANT_tip_pickup
-                     (ctx,
-                      MERCHANT_URL,
-                      &ref->details.tip_authorize.tip_id,
-                      num_planchets,
-                      planchets,
-                      &pickup_cb,
-                      is)))
+                     = TALER_MERCHANT_tip_pickup (ctx,
+                                                  MERCHANT_URL,
+                                                  
&ref->details.tip_authorize.tip_id,
+                                                  num_planchets,
+                                                  planchets,
+                                                  &pickup_cb,
+                                                  is)))
         {
           GNUNET_break (0);
           fail (is);
@@ -4387,17 +4277,6 @@ run (void *cls)
       .details.check_bank_transfer.account_debit = 62,
       .details.check_bank_transfer.account_credit = EXCHANGE_ACCOUNT_NO
     },
-    { .oc = OC_TIP_ENABLE,
-      .label = "enable-tip-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.tip_enable.admin_add_incoming_ref = "create-reserve-tip-1",
-      .details.tip_enable.amount = "EUR:5.01" },
-    /* Test incrementing active reserve balance */
-    { .oc = OC_TIP_ENABLE,
-      .label = "enable-tip-2",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.tip_enable.admin_add_incoming_ref = "create-reserve-tip-1",
-      .details.tip_enable.amount = "EUR:5.01" },
     /* Authorize two tips */
     { .oc = OC_TIP_AUTHORIZE,
       .label = "authorize-tip-1",
@@ -4411,11 +4290,22 @@ run (void *cls)
       .details.tip_authorize.instance = "tip",
       .details.tip_authorize.justification = "tip 2",
       .details.tip_authorize.amount = "EUR:5.01" },
+    /* Withdraw tip */
+    { .oc = OC_TIP_PICKUP,
+      .label = "pickup-tip-1",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.tip_pickup.authorize_ref = "authorize-tip-1",
+      .details.tip_pickup.amounts = pickup_amounts_1 },
+    { .oc = OC_TIP_PICKUP,
+      .label = "pickup-tip-2",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.tip_pickup.authorize_ref = "authorize-tip-2",
+      .details.tip_pickup.amounts = pickup_amounts_1 },
     /* Test authorization failure modes */
     { .oc = OC_TIP_AUTHORIZE,
       .label = "authorize-tip-3-insufficient-funds",
       .expected_response_code = MHD_HTTP_PRECONDITION_FAILED,
-      .details.tip_authorize.instance = "tip",
+      .details.tip_authorize.instance = "dtip",
       .details.tip_authorize.justification = "tip 3",
       .details.tip_authorize.amount = "EUR:5.01",
       .details.tip_authorize.expected_ec = 
TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS },
@@ -4433,24 +4323,6 @@ run (void *cls)
       .details.tip_authorize.justification = "tip 5",
       .details.tip_authorize.amount = "EUR:5.01",
       .details.tip_authorize.expected_ec = 
TALER_EC_TIP_AUTHORIZE_INSTANCE_DOES_NOT_TIP },
-    { .oc = OC_TIP_AUTHORIZE,
-      .label = "authorize-tip-6-not-enabled-instance",
-      .expected_response_code = MHD_HTTP_NOT_FOUND,
-      .details.tip_authorize.instance = "dtip",
-      .details.tip_authorize.justification = "tip 6",
-      .details.tip_authorize.amount = "EUR:5.01",
-      .details.tip_authorize.expected_ec = 
TALER_EC_TIP_AUTHORIZE_RESERVE_NOT_ENABLED },
-    /* Withdraw tip */
-    { .oc = OC_TIP_PICKUP,
-      .label = "pickup-tip-1",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.tip_pickup.authorize_ref = "authorize-tip-1",
-      .details.tip_pickup.amounts = pickup_amounts_1 },
-    { .oc = OC_TIP_PICKUP,
-      .label = "pickup-tip-2",
-      .expected_response_code = MHD_HTTP_OK,
-      .details.tip_pickup.authorize_ref = "authorize-tip-2",
-      .details.tip_pickup.amounts = pickup_amounts_1 },
     { .oc = OC_TIP_PICKUP,
       .label = "pickup-tip-3-too-much",
       .expected_response_code = MHD_HTTP_SERVICE_UNAVAILABLE,
@@ -4588,7 +4460,7 @@ run (void *cls)
       .details.pay.refund_fee = "EUR:0.01",
       .details.pay_again.pay_ref = "pay-fail-partial-double-10",
       .details.pay_again.coin_ref = "withdraw-coin-10a;withdraw-coin-10b" },
-    
+
     /* Run transfers. */
     { .oc = OC_RUN_AGGREGATOR,
       .label = "run-aggregator-10" },
@@ -4701,14 +4573,14 @@ run (void *cls)
       .details.pay_abort_refund.num_coin = 0,
       .details.pay_abort_refund.refund_amount = "EUR:5",
       .details.pay_abort_refund.refund_fee = "EUR:0.01" },
-    
+
     /* Run transfers. */
     { .oc = OC_RUN_AGGREGATOR,
       .label = "run-aggregator-11" },
     /* Check that there are no other unusual transfers */
     { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
       .label = "check_bank_empty-11" },
-    
+
     /* end of testcase */
     { .oc = OC_END }
   };
diff --git a/src/merchant-tools/Makefile.am b/src/merchant-tools/Makefile.am
index 7811d04..4e68837 100644
--- a/src/merchant-tools/Makefile.am
+++ b/src/merchant-tools/Makefile.am
@@ -3,8 +3,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/include
 
 bin_PROGRAMS = \
   taler-merchant-dbinit \
-  taler-merchant-generate-payments \
-  taler-merchant-tip-enable
+  taler-merchant-generate-payments
 
 taler_merchant_dbinit_SOURCES = \
   taler-merchant-dbinit.c
@@ -16,16 +15,6 @@ taler_merchant_dbinit_LDADD = \
   -ltalerutil \
   -ltalerpq
 
-taler_merchant_tip_enable_SOURCES = \
-  taler-merchant-tip-enable.c
-
-taler_merchant_tip_enable_LDADD = \
-  $(LIBGCRYPT_LIBS) \
-  $(top_builddir)/src/lib/libtalermerchant.la \
-  -lgnunetcurl \
-  -lgnunetutil \
-  -ltalerutil
-
 taler_merchant_generate_payments_SOURCES = \
   taler-merchant-generate-payments.c
 
diff --git a/src/merchant-tools/taler-merchant-tip-enable.c 
b/src/merchant-tools/taler-merchant-tip-enable.c
deleted file mode 100644
index e6e5201..0000000
--- a/src/merchant-tools/taler-merchant-tip-enable.c
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2017 Taler Systems SA
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along with
-  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file merchant-tools/taler-merchant-tip-enable.c
- * @brief enable tips by telling the backend that a reserve was charged
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <taler/taler_util.h>
-#include <gnunet/gnunet_util_lib.h>
-#include "taler_merchant_service.h"
-#include <microhttpd.h> /* just for HTTP status code, no need to link against 
*/
-
-/**
- * Return value from main().
- */
-static int global_ret;
-
-/**
- * -a option: how much money was deposited into the reserve
- */
-static struct TALER_Amount amount;
-
-/**
- * For which instance did we charge the reserve?
- */
-static char *instance;
-
-/**
- * Under which URI does the backend run?
- */
-static char *backend_uri;
-
-/**
- * UUID of the wire transfer.
- */
-static char *credit_uuid;
-
-/**
- * Expiration time for the reserve.
- */
-static struct GNUNET_TIME_Absolute expiration;
-
-/**
- * Main execution context for the main loop of the exchange.
- */
-static struct GNUNET_CURL_Context *ctx;
-
-/**
- * Context for running the #ctx's event loop.
- */
-static struct GNUNET_CURL_RescheduleContext *rc;
-
-/**
- * Handle for the /tip-enable operation.
- */
-static struct TALER_MERCHANT_TipEnableOperation *teo;
-
-
-/**
- * Function run when the test terminates (good or bad).
- * Cleans up our state.
- *
- * @param cls the interpreter state.
- */
-static void
-do_shutdown (void *cls)
-{
-  if (NULL != teo)
-  {
-    TALER_MERCHANT_tip_enable_cancel (teo);
-    teo = NULL;
-  }
-  if (NULL != ctx)
-  {
-    GNUNET_CURL_fini (ctx);
-    ctx = NULL;
-  }
-  if (NULL != rc)
-  {
-    GNUNET_CURL_gnunet_rc_destroy (rc);
-    rc = NULL;
-  }
-}
-
-
-/**
- * Callback for a /tip-enable request.  Returns the result of
- * the operation.
- *
- * @param cls closure
- * @param http_status HTTP status returned by the merchant backend
- * @param ec taler-specific error code
- */
-static void
-enable_cb (void *cls,
-           unsigned int http_status,
-           enum TALER_ErrorCode ec)
-{
-  teo = NULL;
-  GNUNET_SCHEDULER_shutdown ();
-  if ( (MHD_HTTP_OK == http_status) &&
-       (TALER_EC_NONE == ec) )
-  {
-    global_ret = 0;
-    return;
-  }
-  fprintf (stderr,
-           "Failed with HTTP status %u and error code %u\n",
-           http_status,
-           ec);
-  global_ret = 3;
-}
-
-
-/**
- * Main function that will be run.
- *
- * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be 
NULL!)
- * @param cfg configuration
- */
-static void
-run (void *cls,
-     char *const *args,
-     const char *cfgfile,
-     const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
-  struct TALER_ReservePrivateKeyP reserve_priv;
-  struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
-  char *section;
-  char *tip_reserves;
-  struct GNUNET_HashCode hcredit_uuid;
-  struct GNUNET_CURL_Context *ctx;
-
-  GNUNET_asprintf (&section,
-                   "merchant-instance-%s",
-                   instance);
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (cfg,
-                                               section,
-                                               "TIP_RESERVE_PRIV_FILENAME",
-                                               &tip_reserves))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               section,
-                               "TIP_RESERVE_PRIV_FILENAME");
-    GNUNET_free (section);
-    global_ret = 1;
-    return;
-  }
-  pk = GNUNET_CRYPTO_eddsa_key_create_from_file (tip_reserves);
-  if (NULL == pk)
-  {
-    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                               section,
-                               "TIP_RESERVE_PRIV_FILENAME",
-                               "Failed to read private key");
-    GNUNET_free (section);
-    GNUNET_free (tip_reserves);
-    global_ret = 1;
-    return;
-  }
-  GNUNET_free (tip_reserves);
-  GNUNET_free (section);
-
-  GNUNET_CRYPTO_hash (credit_uuid,
-                      strlen (credit_uuid),
-                      &hcredit_uuid);
-
-  ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
-                          &rc);
-  if (NULL == ctx)
-  {
-    GNUNET_break (0);
-    global_ret = 1;
-    return;
-  }
-  rc = GNUNET_CURL_gnunet_rc_create (ctx);
-  reserve_priv.eddsa_priv = *pk;
-  GNUNET_free (pk);
-  teo = TALER_MERCHANT_tip_enable (ctx,
-                                   backend_uri,
-                                   &amount,
-                                   expiration,
-                                   &reserve_priv,
-                                   &hcredit_uuid,
-                                   &enable_cb,
-                                   NULL);
-  GNUNET_assert (NULL != teo);
-  GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
-                                 NULL);
-}
-
-
-/**
- * The main function of the database initialization tool.
- * Used to initialize the Taler Exchange's database.
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
- */
-int
-main (int argc,
-      char *const *argv)
-{
-  struct GNUNET_GETOPT_CommandLineOption options[] = {
-    GNUNET_GETOPT_option_mandatory
-    (TALER_getopt_get_amount ('a',
-                              "amount",
-                              "VALUE",
-                              "value that was added to the reserve",
-                              &amount)),
-    GNUNET_GETOPT_option_mandatory
-    (GNUNET_GETOPT_option_string ('b',
-                                  "backend",
-                                  "URI",
-                                  "URI of the backend to use",
-                                  &backend_uri)),
-    GNUNET_GETOPT_option_mandatory
-    (GNUNET_GETOPT_option_string ('C',
-                                  "credit-uuid",
-                                  "UUID",
-                                  "unique identifier of the wire transfer (to 
detect duplicate invocations)",
-                                  &credit_uuid)),
-    GNUNET_GETOPT_option_mandatory
-    (GNUNET_GETOPT_option_absolute_time ('e',
-                                         "expiration",
-                                         "TIMESTAMP",
-                                         "when does the reserve expire",
-                                         &expiration)),
-    GNUNET_GETOPT_option_mandatory
-    (GNUNET_GETOPT_option_string ('i',
-                                  "instance",
-                                  "NAME",
-                                  "name of the instance of which the reserve 
was charged",
-                                  &instance)),
-    GNUNET_GETOPT_OPTION_END
-  };
-
-  /* force linker to link against libtalerutil; if we do
-     not do this, the linker may "optimize" libtalerutil
-     away and skip #TALER_OS_init(), which we do need */
-  (void) TALER_project_data_default ();
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_log_setup ("taler-merchant-tip-enable",
-                                   "INFO",
-                                   NULL));
-  global_ret = 2;
-  if (GNUNET_OK !=
-      GNUNET_PROGRAM_run (argc, argv,
-                          "taler-merchant-tip-enable",
-                         "Enable tipping by telling the backend that a reserve 
was charged",
-                         options,
-                         &run,
-                          NULL))
-    return 1;
-  return global_ret;
-}
-
-
-/* end of taler-exchange-tip-enable.c */

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



reply via email to

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