View Issue Details

IDProjectCategoryView StatusLast Update
0005198Talermechant backendpublic2018-04-15 20:33
ReporterFlorian Dold Assigned ToChristian Grothoff  
PrioritynormalSeverityfeatureReproducibilityhave not tried
Status closedResolutionfixed 
Product Version0.4 
Target Version0.5Fixed in Version0.5 
Summary0005198: merchant should check a reserve's status before adding it as a tipping reserve
DescriptionThere should be some sanity check that a reserve that we add as a tipping reserve has _at least_ the amount available that is being added.

This avoids cases where the merchant gives out tips that can't be picked up, because something went wrong when topping up the reserve.

We can't check for exact amounts since we might have "re-filled" an existing reserve, and some funds may still be waiting to be picked up as a tip from a user.
TagsNo tags attached.
Attached Files
5198.diff (35,449 bytes)   
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:
 
-@example
-$ taler-merchant-tip-enable \
-    --amount=AMOUNT \
-    --backend=BACKEND_URI \
-    --credit-uuid=CREDIT_UUID \
-    --instance=INSTANCE \
-    --expiration=EXPIRATION
-@end 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/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 2bf927d..af32f27 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},
diff --git a/src/backend/taler-merchant-httpd_tip-authorize.c b/src/backend/taler-merchant-httpd_tip-authorize.c
index c55b41b..aaa62c4 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,218 @@ 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 TipAuthContext *tac = (struct TipAuthContext *) hc;
+
+  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;
+
+  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);
+    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 */
+  MHD_resume_connection (tac->connection);
+}
+
+
+/**
+ * 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 TMH_JsonParseContext *jpc = (struct TMH_JsonParseContext *) hc;
+  struct TipAuthContext *tac = cls;
+  struct TALER_ReservePublicKeyP reserve_pub;
 
-  TMH_PARSE_post_cleanup_callback (jpc->json_parse_context);
-  GNUNET_free (jpc);
+  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);
+    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 +273,63 @@ 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;
+    *connection_cls = tac;
   }
   else
   {
-    ctx = *connection_cls;
+    tac = *connection_cls;
   }
   res = TMH_PARSE_post_json (connection,
-                             &ctx->json_parse_context,
+                             &tac->json_parse_context,
                              upload_data,
                              upload_data_size,
-                             &root);
+                             &tac->root);
   if (GNUNET_SYSERR == res)
     return MHD_NO;
   /* the POST's body has to be further fetched */
   if ( (GNUNET_NO == res) ||
-       (NULL == root) )
+       (NULL == tac->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_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 +338,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;
@@ -184,43 +389,31 @@ MH_handler_tip_authorize (struct TMH_RequestHandler *rh,
       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/include/taler_merchant_service.h b/src/include/taler_merchant_service.h
index 51db135..81f1219 100644
--- a/src/include/taler_merchant_service.h
+++ b/src/include/taler_merchant_service.h
@@ -309,7 +309,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.
    */
@@ -317,7 +317,7 @@ struct TALER_MERCHANT_PayCoin
 
   /**
    * URL of the exchange that issued @e coin_priv.
-   */ 
+   */
   const char *exchange_url;
 
 };
@@ -373,12 +373,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;
 
   /**
@@ -789,71 +789,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/test_merchant_api.c b/src/lib/test_merchant_api.c
index b09d78a..a51ac2e 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.
    */
@@ -233,11 +233,6 @@ enum OpCode
    */
   OC_REFUND_LOOKUP,
 
-  /**
-   * Start a reserve for tipping.
-   */
-  OC_TIP_ENABLE,
-
   /**
    * Authorize a tip.
    */
@@ -551,12 +546,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.
        */
@@ -594,7 +589,7 @@ struct Command
        * Pay handle while operation is running.
        */
       struct TALER_MERCHANT_Pay *ph;
-      
+
     } pay_again;
 
     struct {
@@ -609,7 +604,7 @@ struct Command
        */
       struct TALER_MERCHANT_Pay *ph;
 
-      /** 
+      /**
        * Set in #pay_refund_cb to number of refunds obtained.
        */
       unsigned int num_refunds;
@@ -621,12 +616,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;
@@ -635,7 +630,7 @@ struct Command
 
       /**
        * Reference to the pay_abort command to be refunded.
-       */ 
+       */
       const char *abort_ref;
 
       /**
@@ -652,7 +647,7 @@ struct Command
        * Refund fee to expect.
        */
       const char *refund_fee;
-      
+
       /**
        * Handle to the refund operation.
        */
@@ -1870,7 +1865,7 @@ pay_again_cb (void *cls,
   }
   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;
@@ -1882,7 +1877,7 @@ pay_again_cb (void *cls,
 				   &mr.h_contract_terms),
       GNUNET_JSON_spec_end ()
     };
-    
+
     GNUNET_assert (GNUNET_OK ==
         GNUNET_JSON_parse (obj,
                            spec,
@@ -2056,45 +2051,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.
@@ -2553,13 +2509,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)
       {
@@ -2626,7 +2575,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
@@ -2641,8 +2590,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, ";"))
@@ -2651,7 +2600,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;
@@ -2692,7 +2641,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));
@@ -2735,7 +2684,7 @@ 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)
   {
@@ -2784,7 +2733,7 @@ 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)
   {
@@ -3289,7 +3238,7 @@ interpreter_run (void *cls)
       GNUNET_break (0);
       fail (is);
     }
-    break;    
+    break;
   case OC_PAY_ABORT:
     {
       struct TALER_MERCHANT_PayCoin *pc;
@@ -3420,7 +3369,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,
@@ -3650,83 +3599,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);
@@ -4318,17 +4190,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",
@@ -4519,7 +4380,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" },
@@ -4632,14 +4493,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
 
5198.diff (35,449 bytes)   

Relationships

related to 0005245 closedFlorian Dold make /proposal easier to use 

Activities

Christian Grothoff

2017-12-09 11:13

manager   ~0012641

Actually, while we can't check exact amounts, I think with some user-specified "buffer" we ought to be OK, especially as this helps us better estimate the amount left. And we can that way eliminate the entire interaction where the merchant has to tell us how much he deposited: the merchant backend should simply check if there is enough left in the reserve if/when it's accounting shows that the reserve _may_ be low on funds.

Christian Grothoff

2018-01-15 15:35

manager   ~0012786

I've attached a preliminary, untested patch (blocked on 0005245 regression).

Christian Grothoff

2018-01-16 13:14

manager   ~0012791

Implemented in 2a8c44f..4446b15

Issue History

Date Modified Username Field Change
2017-12-08 14:56 Florian Dold New Issue
2017-12-08 14:56 Florian Dold Status new => assigned
2017-12-08 14:56 Florian Dold Assigned To => Christian Grothoff
2017-12-08 14:58 Florian Dold Product Version => 0.4
2017-12-08 14:58 Florian Dold Target Version => 0.5
2017-12-09 11:10 Christian Grothoff Severity tweak => feature
2017-12-09 11:13 Christian Grothoff Note Added: 0012641
2017-12-10 16:07 Christian Grothoff Target Version 0.5 => 0.7.1
2018-01-15 15:35 Christian Grothoff File Added: 5198.diff
2018-01-15 15:35 Christian Grothoff Note Added: 0012786
2018-01-15 15:35 Christian Grothoff Relationship added related to 0005245
2018-01-15 15:56 Christian Grothoff Target Version 0.7.1 => 0.5
2018-01-16 13:14 Christian Grothoff Status assigned => resolved
2018-01-16 13:14 Christian Grothoff Resolution open => fixed
2018-01-16 13:14 Christian Grothoff Fixed in Version => 0.5
2018-01-16 13:14 Christian Grothoff Note Added: 0012791
2018-04-15 20:33 Christian Grothoff Status resolved => closed