gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] 91/277: implement GET /orders/ID


From: gnunet
Subject: [taler-merchant] 91/277: implement GET /orders/ID
Date: Sun, 05 Jul 2020 20:50:04 +0200

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

grothoff pushed a commit to branch master
in repository merchant.

commit 9d33dde616b0d227b76e9692304cdb22507c72bc
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Tue May 5 15:03:47 2020 +0200

    implement GET /orders/ID
---
 src/backend/Makefile.am                          |   2 +
 src/backend/legacy.c                             |  67 ----
 src/backend/taler-merchant-httpd.c               |   9 +
 src/backend/taler-merchant-httpd_get-orders-ID.c | 409 ++++++++++++-----------
 src/backenddb/merchant-0001.sql                  |   3 +
 src/backenddb/plugin_merchantdb_postgres.c       |   9 +-
 src/include/taler_merchantdb_plugin.h            | 108 +++++-
 7 files changed, 331 insertions(+), 276 deletions(-)

diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 049e0f4..55029b0 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -22,6 +22,8 @@ taler_merchant_httpd_SOURCES = \
   taler-merchant-httpd_auditors.c taler-merchant-httpd_auditors.h \
   taler-merchant-httpd_config.c taler-merchant-httpd_config.h \
   taler-merchant-httpd_exchanges.c taler-merchant-httpd_exchanges.h \
+  taler-merchant-httpd_get-orders-ID.c \
+    taler-merchant-httpd_get-orders-ID.h \
   taler-merchant-httpd_mhd.c taler-merchant-httpd_mhd.h \
   taler-merchant-httpd_private-delete-instances-ID.c \
     taler-merchant-httpd_private-delete-instances-ID.h \
diff --git a/src/backend/legacy.c b/src/backend/legacy.c
index c105ecb..2fb7baa 100644
--- a/src/backend/legacy.c
+++ b/src/backend/legacy.c
@@ -1,70 +1,3 @@
-/**
- * Create a taler://pay/ URI for the given @a con and @a order_id
- * and @a session_id and @a instance_id.
- *
- * @param con HTTP connection
- * @param order_id the order id
- * @param session_id session, may be NULL
- * @param instance_id instance, may be "default"
- * @return corresponding taler://pay/ URI, or NULL on missing "host"
- */
-char *
-TMH_make_taler_pay_uri (struct MHD_Connection *con,
-                        const char *order_id,
-                        const char *session_id,
-                        const char *instance_id)
-{
-  const char *host;
-  const char *forwarded_host;
-  const char *uri_path;
-  const char *uri_instance_id;
-  const char *query;
-  char *result;
-
-  host = MHD_lookup_connection_value (con,
-                                      MHD_HEADER_KIND,
-                                      "Host");
-  forwarded_host = MHD_lookup_connection_value (con,
-                                                MHD_HEADER_KIND,
-                                                "X-Forwarded-Host");
-
-  uri_path = MHD_lookup_connection_value (con,
-                                          MHD_HEADER_KIND,
-                                          "X-Forwarded-Prefix");
-  if (NULL == uri_path)
-    uri_path = "-";
-  if (NULL != forwarded_host)
-    host = forwarded_host;
-  if (0 == strcmp (instance_id,
-                   "default"))
-    uri_instance_id = "-";
-  else
-    uri_instance_id = instance_id;
-  if (NULL == host)
-  {
-    /* Should never happen, at least the host header should be defined */
-    GNUNET_break (0);
-    return NULL;
-  }
-
-  if (GNUNET_YES == TALER_mhd_is_https (con))
-    query = "";
-  else
-    query = "?insecure=1";
-  GNUNET_assert (NULL != order_id);
-  GNUNET_assert (0 < GNUNET_asprintf (&result,
-                                      "taler://pay/%s/%s/%s/%s%s%s%s",
-                                      host,
-                                      uri_path,
-                                      uri_instance_id,
-                                      order_id,
-                                      (NULL == session_id) ? "" : "/",
-                                      (NULL == session_id) ? "" : session_id,
-                                      query));
-  return result;
-}
-
-
 /**
  * Closure for the #wireformat_iterator_cb().
  */
diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index 27541b5..4744347 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -27,6 +27,7 @@
 #include "taler-merchant-httpd_auditors.h"
 #include "taler-merchant-httpd_config.h"
 #include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_get-orders-ID.h"
 #include "taler-merchant-httpd_mhd.h"
 #include "taler-merchant-httpd_private-delete-instances-ID.h"
 #include "taler-merchant-httpd_private-delete-products-ID.h"
@@ -36,6 +37,7 @@
 #include "taler-merchant-httpd_private-get-products.h"
 #include "taler-merchant-httpd_private-get-products-ID.h"
 #include "taler-merchant-httpd_private-get-orders.h"
+// #include "taler-merchant-httpd_private-get-orders-ID.h"
 #include "taler-merchant-httpd_private-patch-instances-ID.h"
 #include "taler-merchant-httpd_private-patch-products-ID.h"
 #include "taler-merchant-httpd_private-post-instances.h"
@@ -879,6 +881,13 @@ url_handler (void *cls,
       .method = MHD_HTTP_METHOD_POST,
       .handler = &TMH_post_orders_ID_pay
     },
+    /* GET /orders/$ID: */
+    {
+      .url_prefix = "/orders/",
+      .method = MHD_HTTP_METHOD_GET,
+      .have_id_segment = true,
+      .handler = &TMH_get_orders_ID
+    },
     {
       NULL
     }
diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.c 
b/src/backend/taler-merchant-httpd_get-orders-ID.c
index ba0d354..0fae0a9 100644
--- a/src/backend/taler-merchant-httpd_get-orders-ID.c
+++ b/src/backend/taler-merchant-httpd_get-orders-ID.c
@@ -24,9 +24,8 @@
 #include <taler/taler_signatures.h>
 #include <taler/taler_json_lib.h>
 #include <taler/taler_exchange_service.h>
-#include "taler-merchant-httpd.h"
 #include "taler-merchant-httpd_exchanges.h"
-#include "taler-merchant-httpd_refund.h"
+#include "taler-merchant-httpd_get-orders-ID.h"
 
 /**
  * How often do we retry DB transactions on serialization failures?
@@ -69,6 +68,12 @@ struct CoinRefund
    */
   char *exchange_url;
 
+  /**
+   * Fully reply from the exchange, only possibly set if
+   * we got a JSON reply and a non-#MHD_HTTP_OK error code
+   */
+  json_t *exchange_reply;
+
   /**
    * Coin to refund.
    */
@@ -80,14 +85,14 @@ struct CoinRefund
   uint64_t rtransaction_id;
 
   /**
-   * Amount to refund.
+   * Unique serial number identifying the refund.
    */
-  struct TALER_Amount refund_amount;
+  uint64_t refund_serial;
 
   /**
-   * Applicable refund transaction fee.
+   * Amount to refund.
    */
-  struct TALER_Amount refund_fee;
+  struct TALER_Amount refund_amount;
 
   /**
    * Public key of the exchange affirming the refund.
@@ -110,12 +115,6 @@ struct CoinRefund
    */
   enum TALER_ErrorCode exchange_code;
 
-  /**
-   * Fully reply from the exchange, only possibly set if
-   * we got a JSON reply and a non-#MHD_HTTP_OK error code
-   */
-  json_t *exchange_reply;
-
 };
 
 
@@ -141,19 +140,19 @@ struct GetOrderData
   struct GetOrderData *prev;
 
   /**
-   * Context of the request.
+   * Refunds for this order. Head of DLL.
    */
-  struct TMH_HandlerContext *hc;
+  struct CoinRefund *cr_head;
 
   /**
-   * Did we suspend @a connection?
+   * Refunds for this order. Tail of DLL.
    */
-  int suspended;
+  struct CoinRefund *cr_tail;
 
   /**
-   * Return code: #TALER_EC_NONE if successful.
+   * Context of the request.
    */
-  enum TALER_ErrorCode ec;
+  struct TMH_HandlerContext *hc;
 
   /**
    * Entry in the #resume_timeout_heap for this check payment, if we are
@@ -166,11 +165,6 @@ struct GetOrderData
    */
   struct MerchantInstance *mi;
 
-  /**
-   * URL where the final contract can be found for this payment.
-   */
-  char *final_contract_url;
-
   /**
    * order ID for the payment
    */
@@ -198,18 +192,22 @@ struct GetOrderData
    */
   json_t *contract_terms;
 
-  /**
-   * Hash of @e contract_terms, set only once @e contract_terms
-   * is available.
-   */
-  struct GNUNET_HashCode h_contract_terms;
-
   /**
    * Total refunds granted for this payment. Only initialized
    * if @e refunded is set to true.
    */
   struct TALER_Amount refund_amount;
 
+  /**
+   * Did we suspend @a connection?
+   */
+  int suspended;
+
+  /**
+   * Return code: #TALER_EC_NONE if successful.
+   */
+  enum TALER_ErrorCode ec;
+
   /**
    * Set to true if this payment has been refunded and
    * @e refund_amount is initialized.
@@ -227,14 +225,14 @@ struct GetOrderData
 
 
 /**
- * HEad of DLL of (suspended) requests.
+ * Head of DLL of (suspended) requests.
  */
-static struct GetOrderData *prd_head;
+static struct GetOrderData *god_head;
 
 /**
  * Tail of DLL of (suspended) requests.
  */
-static struct GetOrderData *prd_tail;
+static struct GetOrderData *god_tail;
 
 
 /**
@@ -252,15 +250,105 @@ TMH_force_wallet_get_order_resume (void)
                                  god);
     GNUNET_assert (god->suspended);
     god->suspended = GNUNET_NO;
-    MHD_resume_connection (god->connection);
+    MHD_resume_connection (god->sc.con);
   }
 }
 
 
+/**
+ * Suspend this @a god until the trigger is satisfied.
+ *
+ * @param god request to suspend
+ */
+static void
+suspend_god (struct GetOrderData *god)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Suspending GET /orders/%s\n",
+              god->order_id);
+  TMH_long_poll_suspend (god->order_id,
+                         god->hc->instance,
+                         &god->sc,
+                         (god->sc.awaiting_refund)
+                         ? &god->sc.refund_expected
+                         : NULL);
+}
+
+
+/**
+ * Create a taler://pay/ URI for the given @a con and @a order_id
+ * and @a session_id and @a instance_id.
+ *
+ * @param con HTTP connection
+ * @param order_id the order id
+ * @param session_id session, may be NULL
+ * @param instance_id instance, may be "default"
+ * @return corresponding taler://pay/ URI, or NULL on missing "host"
+ */
+char *
+TMH_make_taler_pay_uri (struct MHD_Connection *con,
+                        const char *order_id,
+                        const char *session_id,
+                        const char *instance_id)
+{
+  const char *host;
+  const char *forwarded_host;
+  const char *uri_path;
+  const char *uri_instance_id;
+  const char *query;
+  char *result;
+
+  host = MHD_lookup_connection_value (con,
+                                      MHD_HEADER_KIND,
+                                      "Host");
+  forwarded_host = MHD_lookup_connection_value (con,
+                                                MHD_HEADER_KIND,
+                                                "X-Forwarded-Host");
+
+  uri_path = MHD_lookup_connection_value (con,
+                                          MHD_HEADER_KIND,
+                                          "X-Forwarded-Prefix");
+  if (NULL == uri_path)
+    uri_path = "-";
+  if (NULL != forwarded_host)
+    host = forwarded_host;
+  if (0 == strcmp (instance_id,
+                   "default"))
+    uri_instance_id = "-";
+  else
+    uri_instance_id = instance_id;
+  if (NULL == host)
+  {
+    /* Should never happen, at least the host header should be defined */
+    GNUNET_break (0);
+    return NULL;
+  }
+
+  if (GNUNET_YES == TALER_mhd_is_https (con))
+    query = "";
+  else
+    query = "?insecure=1";
+  GNUNET_assert (NULL != order_id);
+  GNUNET_assert (0 < GNUNET_asprintf (&result,
+                                      "taler://pay/%s/%s/%s/%s%s%s%s",
+                                      host,
+                                      uri_path,
+                                      uri_instance_id,
+                                      order_id,
+                                      (NULL == session_id) ? "" : "/",
+                                      (NULL == session_id) ? "" : session_id,
+                                      query));
+  return result;
+}
+
+
 /**
  * The client did not yet pay, send it the payment request.
  *
  * @param god check pay request context
+ * @param already_paid_order_id if for the fulfillment URI there is
+ *          already a paid order, this is the order ID to redirect
+ *          the wallet to; NULL if not applicable
  * @return #MHD_YES on success
  */
 static MHD_RESULT
@@ -287,13 +375,12 @@ send_pay_request (struct GetOrderData *god,
   taler_pay_uri = TMH_make_taler_pay_uri (god->sc.con,
                                           god->order_id,
                                           god->session_id,
-                                          god->mi->id);
+                                          god->hc->instance->settings.id);
   ret = TALER_MHD_reply_json_pack (god->sc.con,
                                    MHD_HTTP_OK,
-                                   "{s:s, s:s, s:b, s:s?}",
+                                   "{s:s, s:b, s:s?}",
                                    "taler_pay_uri", taler_pay_uri,
-                                   "contract_url", god->final_contract_url,
-                                   "paid", 0,
+                                   "paid", false,
                                    "already_paid_order_id",
                                    already_paid_order_id);
   GNUNET_free (taler_pay_uri);
@@ -305,25 +392,20 @@ send_pay_request (struct GetOrderData *god,
  * Check if @a god has sub-activities still pending.
  *
  * @param god request to check
- * @return #GNUNET_YES if activities are still pending
+ * @return true if activities are still pending
  */
-static int
+static bool
 god_pending (struct GetOrderData *god)
 {
-  int pending = GNUNET_NO;
-
   for (struct CoinRefund *cr = god->cr_head;
        NULL != cr;
        cr = cr->next)
   {
     if ( (NULL != cr->fo) ||
          (NULL != cr->rh) )
-    {
-      pending = GNUNET_YES;
-      break;
-    }
+      return true;
   }
-  return pending;
+  return false;
 }
 
 
@@ -342,7 +424,7 @@ check_resume_god (struct GetOrderData *god)
                                god);
   GNUNET_assert (god->suspended);
   god->suspended = GNUNET_NO;
-  MHD_resume_connection (god->connection);
+  MHD_resume_connection (god->sc.con);
   TMH_trigger_daemon ();
 }
 
@@ -381,13 +463,10 @@ refund_cb (void *cls,
 
     cr->exchange_pub = *exchange_pub;
     cr->exchange_sig = *exchange_sig;
-    qs = db->put_refund_proof (db->cls,
-                               &cr->god->merchant->pubkey,
-                               &cr->god->h_contract_terms,
-                               &cr->coin_pub,
-                               cr->rtransaction_id,
-                               exchange_pub,
-                               exchange_sig);
+    qs = TMH_db->insert_refund_proof (TMH_db->cls,
+                                      cr->refund_serial,
+                                      exchange_sig,
+                                      exchange_pub);
     if (0 >= qs)
     {
       /* generally, this is relatively harmless for the merchant, but let's at
@@ -425,11 +504,10 @@ exchange_found_cb (void *cls,
   {
     cr->rh = TALER_EXCHANGE_refund (eh,
                                     &cr->refund_amount,
-                                    &cr->refund_fee,
                                     &cr->god->h_contract_terms,
                                     &cr->coin_pub,
                                     cr->rtransaction_id,
-                                    &cr->god->merchant->privkey,
+                                    &cr->god->hc->instance->merchant_priv,
                                     &refund_cb,
                                     cr);
     return;
@@ -442,25 +520,27 @@ exchange_found_cb (void *cls,
 
 
 /**
- * Function called with information about a refund.
+ * Function called with detailed information about a refund.
  * It is responsible for packing up the data to return.
  *
  * @param cls closure
+ * @param refund_serial unique serial number of the refund
+ * @param timestamp time of the refund (for grouping of refunds in the wallet 
UI)
  * @param coin_pub public coin from which the refund comes from
  * @param exchange_url URL of the exchange that issued @a coin_pub
  * @param rtransaction_id identificator of the refund
  * @param reason human-readable explanation of the refund
  * @param refund_amount refund amount which is being taken from @a coin_pub
- * @param refund_fee cost of this refund operation
  */
 static void
 process_refunds_cb (void *cls,
+                    uint64_t refund_serial,
+                    struct GNUNET_TIME_Absolute timestamp,
                     const struct TALER_CoinSpendPublicKeyP *coin_pub,
                     const char *exchange_url,
                     uint64_t rtransaction_id,
                     const char *reason,
-                    const struct TALER_Amount *refund_amount,
-                    const struct TALER_Amount *refund_fee)
+                    const struct TALER_Amount *refund_amount)
 {
   struct GetOrderData *god = cls;
   struct CoinRefund *cr;
@@ -471,12 +551,12 @@ process_refunds_cb (void *cls,
               TALER_amount2s (refund_amount),
               reason);
   cr = GNUNET_new (struct CoinRefund);
+  cr->refund_serial = refund_serial;
   cr->exchange_url = GNUNET_strdup (exchange_url);
   cr->god = god;
   cr->coin_pub = *coin_pub;
   cr->rtransaction_id = rtransaction_id;
   cr->refund_amount = *refund_amount;
-  cr->refund_fee = *refund_fee;
   GNUNET_CONTAINER_DLL_insert (god->cr_head,
                                god->cr_tail,
                                cr);
@@ -493,27 +573,6 @@ process_refunds_cb (void *cls,
 }
 
 
-/**
- * Suspend this @a god until the trigger is satisfied.
- *
- * @param ppr
- */
-static void
-suspend_god (struct GetOrderData *god)
-{
-  TMH_compute_pay_key (god->order_id,
-                       &god->mi->pubkey,
-                       &god->sc.key);
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Suspending /poll-payment on key %s\n",
-              GNUNET_h2s (&god->sc.key));
-  TMH_long_poll_suspend (&god->sc,
-                         (god->sc.awaiting_refund)
-                         ? &god->sc.refund_expected
-                         : NULL);
-}
-
-
 /**
  * Clean up the session state for a GET /orders/$ID request.
  *
@@ -523,6 +582,7 @@ static void
 god_cleanup (void *cls)
 {
   struct GetOrderData *god = cls;
+  struct CoinRefund *cr;
 
   while (NULL != (cr = god->cr_head))
   {
@@ -550,7 +610,6 @@ god_cleanup (void *cls)
 
   if (NULL != god->contract_terms)
     json_decref (god->contract_terms);
-  GNUNET_free_non_null (god->final_contract_url);
   GNUNET_free (god);
 }
 
@@ -576,10 +635,9 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
   {
     god = GNUNET_new (struct GetOrderData);
     hc->ctx = god;
-    god->hc.cc = &god_cleanup;
+    hc->cc = &god_cleanup;
     god->sc.con = connection;
     god->ec = TALER_EC_NONE;
-    god->connection = connection;
     god->hc = hc;
     god->ret = GNUNET_SYSERR;
 
@@ -666,36 +724,16 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
       }
     }
 
-    // FIXME: what is this about again???
-    god->contract_url = MHD_lookup_connection_value (connection,
-                                                     MHD_GET_ARGUMENT_KIND,
-                                                     "contract_url");
-    if (NULL == god->contract_url)
-    {
-      // FIXME: this can't be right anymore...
-      god->final_contract_url = TALER_url_absolute_mhd (connection,
-                                                        "/public/proposal",
-                                                        "instance", mi->id,
-                                                        "order_id",
-                                                        god->order_id,
-                                                        NULL);
-      GNUNET_assert (NULL != god->final_contract_url);
-    }
-    else
-    {
-      god->final_contract_url = GNUNET_strdup (god->contract_url);
-    }
-
     god->session_id = MHD_lookup_connection_value (connection,
                                                    MHD_GET_ARGUMENT_KIND,
                                                    "session_id");
 
-    /* Convert order id to h_contract_terms */
-    db->preflight (db->cls);
-    qs = db->find_contract_terms (db->cls,
-                                  hc->instance->settings.id,
-                                  order_id,
-                                  &god->contract_terms);
+    /* Convert order_id to h_contract_terms */
+    TMH_db->preflight (TMH_db->cls);
+    qs = TMH_db->lookup_contract_terms (TMH_db->cls,
+                                        hc->instance->settings.id,
+                                        order_id,
+                                        &god->contract_terms);
     if (0 > qs)
     {
       /* single, read-only SQL statements should never cause
@@ -705,7 +743,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         TALER_EC_REFUND_LOOKUP_DB_ERROR,
+                                         TALER_EC_GET_ORDERS_DB_LOOKUP_ERROR,
                                          "database error looking up contract");
     }
     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
@@ -715,7 +753,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
                   order_id);
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_NOT_FOUND,
-                                         TALER_EC_ORDER_ID_UNKNOWN,
+                                         TALER_EC_GET_ORDERS_ID_UNKNOWN,
                                          "order_id not found in database");
     }
 
@@ -741,7 +779,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
         GNUNET_break_op (0);
         return TALER_MHD_reply_with_error (connection,
                                            MHD_HTTP_FORBIDDEN,
-                                           TALER_EC_WRONG_CONTRACT,
+                                           TALER_EC_GET_ORDERS_WRONG_CONTRACT,
                                            "Contract hash does not match 
order");
       }
     }
@@ -761,7 +799,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
         GNUNET_break (0);
         return TALER_MHD_reply_with_error (connection,
                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                           
TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
+                                           TALER_EC_GET_ORDERS_DB_LOOKUP_ERROR,
                                            "Merchant database error (contract 
terms corrupted)");
       }
     }
@@ -773,11 +811,11 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
     /* Check if paid within a session. */
     char *already_paid_order_id = NULL;
 
-    qs = db->find_session_info (db->cls,
-                                &already_paid_order_id,
-                                god->session_id,
-                                god->fulfillment_url,
-                                &mi->pubkey);
+    qs = TMH_db->lookup_order_by_fulfillment (TMH_db->cls,
+                                              hc->instance->settings.id,
+                                              god->fulfillment_url,
+                                              god->session_id,
+                                              &already_paid_order_id);
     if (qs < 0)
     {
       /* single, read-only SQL statements should never cause
@@ -787,17 +825,21 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         
TALER_EC_CHECK_PAYMENT_DB_FETCH_ORDER_ERROR,
+                                         TALER_EC_GET_ORDERS_DB_LOOKUP_ERROR,
                                          "db error fetching pay session info");
     }
-    else if (0 == qs)
+    if (0 == qs)
     {
+      MHD_RESULT ret;
+
       ret = send_pay_request (god,
                               already_paid_order_id);
       GNUNET_free_non_null (already_paid_order_id);
       return ret;
     }
     GNUNET_break (1 == qs);
+    // FIXME: eh, what? shouldn't we be possibly REDIRECTING to
+    // already_paid_order_id?
     GNUNET_break (0 == strcmp (order_id,
                                already_paid_order_id));
     GNUNET_free_non_null (already_paid_order_id);
@@ -805,48 +847,47 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
   else
   {
     /* Check if paid regardless of session. */
-    json_t *xcontract_terms = NULL;
-
-    qs = db->find_paid_contract_terms_from_hash (db->cls,
-                                                 &xcontract_terms,
-                                                 &god->h_contract_terms,
-                                                 &mi->pubkey);
-    if (0 > qs)
+    struct GNUNET_HashCode h_contract;
+    bool paid;
+
+    qs = TMH_db->lookup_order_status (TMH_db->cls,
+                                      hc->instance->settings.id,
+                                      order_id,
+                                      &h_contract,
+                                      &paid);
+    if (0 >= qs)
     {
       /* Always report on hard error as well to enable diagnostics */
       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
+                                         TALER_EC_GET_ORDERS_DB_LOOKUP_ERROR,
                                          "Merchant database error");
     }
-    if (0 == qs)
+    GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
+    GNUNET_break (0 ==
+                  GNUNET_memcmp (&h_contract,
+                                 &god->h_contract_terms));
+    if (! paid)
     {
       return send_pay_request (god,
                                NULL);
     }
-    GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
-    GNUNET_assert (NULL != xcontract_terms);
-    json_decref (xcontract_terms);
   }
 
-
-  for (unsigned int i = 0; i<MAX_RETRIES; i++)
-  {
-    qs = db->get_refunds_from_contract_terms_hash (db->cls,
-                                                   hc->instance->settings.id,
-                                                   &god->h_contract_terms,
-                                                   &process_refunds_cb,
-                                                   god);
-    if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
-      break;
-  }
+  /* At this point, we know the contract was paid. Let's check for
+     refunds */
+  qs = TMH_db->lookup_refunds_detailed (TMH_db->cls,
+                                        hc->instance->settings.id,
+                                        &god->h_contract_terms,
+                                        &process_refunds_cb,
+                                        god);
   if (0 > qs)
   {
     GNUNET_break (0);
     return TALER_MHD_reply_with_error (connection,
                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       TALER_EC_REFUND_LOOKUP_DB_ERROR,
+                                       TALER_EC_GET_ORDERS_DB_LOOKUP_ERROR,
                                        "Failed to lookup refunds for 
contract");
   }
 
@@ -858,28 +899,37 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
   {
     enum GNUNET_DB_QueryStatus qs;
 
-    qs = db->get_refund_proof (db->cls,
-                               &cr->god->merchant->pubkey,
-                               &cr->god->h_contract_terms,
-                               &cr->coin_pub,
-                               cr->rtransaction_id,
-                               &cr->exchange_pub,
-                               &cr->exchange_sig);
-    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+    qs = TMH_db->lookup_refund_proof (TMH_db->cls,
+                                      cr->refund_serial,
+                                      &cr->exchange_sig,
+                                      &cr->exchange_pub);
+    switch (qs)
     {
+    case GNUNET_DB_STATUS_HARD_ERROR:
+    case GNUNET_DB_STATUS_SOFT_ERROR:
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         TALER_EC_GET_ORDERS_DB_LOOKUP_ERROR,
+                                         "Merchant database error");
+    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
       /* We need to talk to the exchange */
       cr->fo = TMH_EXCHANGES_find_exchange (cr->exchange_url,
                                             NULL,
                                             GNUNET_NO,
                                             &exchange_found_cb,
                                             cr);
+      break;
+    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+      /* We got a reply earlier, set status accordingly */
+      cr->exchange_status = MHD_HTTP_OK;
+      break;
     }
   }
 
-  if ( (god->awaiting_refund) &&
+  if ( (god->sc.awaiting_refund) &&
        ( (! god->refunded) ||
          (1 != TALER_amount_cmp (&god->refund_amount,
-                                 &god->min_refund)) ) )
+                                 &god->sc.refund_expected)) ) )
   {
     /* Client is waiting for a refund larger than what we have, suspend
        until timeout */
@@ -891,15 +941,14 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
       /* yes, indeed suspend */
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   "Awaiting refund exceeding %s\n",
-                  TALER_amount2s (&god->min_refund));
+                  TALER_amount2s (&god->sc.refund_expected));
       suspend_god (god);
       return MHD_YES;
     }
   }
 
-
   /* Check if there are still exchange operations pending */
-  if (GNUNET_YES == god_pending (god))
+  if (god_pending (god))
   {
     if (! god->suspended)
     {
@@ -928,14 +977,12 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
           ra,
           (MHD_HTTP_OK != cr->exchange_status)
           ? json_pack ((NULL != cr->exchange_reply)
-                       ? "{s:o,s:o,s:o,s:I,s:I,s:I,s:O}"
-                       : "{s:o,s:o,s:o,s:I,s:I:s:I}",
+                       ? "{s:o,s:o,s:I,s:I,s:I,s:O}"
+                       : "{s:o,s:o,s:I,s:I:s:I}",
                        "coin_pub",
                        GNUNET_JSON_from_data_auto (&cr->coin_pub),
                        "refund_amount",
                        TALER_JSON_from_amount (&cr->refund_amount),
-                       "refund_fee",
-                       TALER_JSON_from_amount (&cr->refund_fee),
                        "exchange_http_status",
                        (json_int_t) cr->exchange_status,
                        "rtransaction_id",
@@ -944,13 +991,11 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
                        (json_int_t) cr->exchange_code,
                        "exchange_reply",
                        cr->exchange_reply)
-          : json_pack ("{s:o,s:o,s:o,s:I,s:I,s:o,s:o}",
+          : json_pack ("{s:o,s:o,s:I,s:I,s:o,s:o}",
                        "coin_pub",
                        GNUNET_JSON_from_data_auto (&cr->coin_pub),
                        "refund_amount",
                        TALER_JSON_from_amount (&cr->refund_amount),
-                       "refund_fee",
-                       TALER_JSON_from_amount (&cr->refund_fee),
                        "exchange_http_status",
                        (json_int_t) cr->exchange_status,
                        "rtransaction_id",
@@ -962,32 +1007,16 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
                        )));
     }
 
-    if (god->refunded)
-      return TALER_MHD_reply_json_pack (connection,
-                                        MHD_HTTP_OK,
-                                        "{s:b, s:b, s:o}",
-                                        "paid", 1,
-                                        "refunded", god->refunded,
-                                        "refund_amount",
-                                        TALER_JSON_from_amount (
-                                          &god->refund_amount));
     return TALER_MHD_reply_json_pack (connection,
                                       MHD_HTTP_OK,
-                                      "{s:b, s:b }",
-                                      "paid", 1,
-                                      "refunded", 0);
-
-
-    return TALER_MHD_reply_json_pack (
-      connection,
-      MHD_HTTP_OK,
-      "{s:o, s:o, s:o}",
-      "refunds",
-      ra,
-      "merchant_pub",
-      GNUNET_JSON_from_data_auto (&mi->pubkey),
-      "h_contract_terms",
-      GNUNET_JSON_from_data_auto (&god->h_contract_terms));
+                                      "{s:b, s:b, s:o, s:o}",
+                                      "paid", true,
+                                      "refunded", god->refunded,
+                                      "refund_amount",
+                                      TALER_JSON_from_amount (
+                                        &god->refund_amount),
+                                      "refunds",
+                                      ra);
   }
 }
 
diff --git a/src/backenddb/merchant-0001.sql b/src/backenddb/merchant-0001.sql
index 36eb67b..31903f8 100644
--- a/src/backenddb/merchant-0001.sql
+++ b/src/backenddb/merchant-0001.sql
@@ -313,6 +313,7 @@ CREATE TABLE IF NOT EXISTS merchant_refunds
   ,order_serial BIGINT NOT NULL
      REFERENCES merchant_contract_terms (order_serial) ON DELETE CASCADE
   ,rtransaction_id BIGINT NOT NULL
+  ,refund_timestamp INT8 NOT NULL
   ,coin_pub BYTEA NOT NULL
   ,reason VARCHAR NOT NULL
   ,refund_amount_val INT8 NOT NULL
@@ -323,6 +324,8 @@ COMMENT ON TABLE merchant_deposits
   IS 'Refunds approved by the merchant (backoffice) logic, excludes abort 
refunds';
 COMMENT ON COLUMN merchant_refunds.rtransaction_id
   IS 'Needed for uniqueness in case a refund is increased for the same order';
+COMMENT ON COLUMN merchant_refunds.refund_timestamp
+  IS 'Needed for grouping of refunds in the wallet UI; has no semantics in the 
protocol (only for UX), but should be from the time when the merchant 
internally approved the refund';
 CREATE INDEX IF NOT EXISTS merchant_refunds_by_coin_and_order
   ON merchant_refunds
   (coin_pub,order_serial);
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index 784650a..f95f346 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -2158,7 +2158,10 @@ process_deposits_for_refund_cb (void *cls,
   struct PostgresClosure *pg = ctx->pg;
   struct TALER_Amount current_refund;
   struct RefundCoinData rcd[GNUNET_NZL (num_results)];
+  struct GNUNET_TIME_Absolute now;
 
+  now = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&now);
   GNUNET_assert (GNUNET_OK ==
                  TALER_amount_get_zero (ctx->refund->currency,
                                         &current_refund));
@@ -2327,6 +2330,7 @@ process_deposits_for_refund_cb (void *cls,
       struct GNUNET_PQ_QueryParam params[] = {
         GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial),
         GNUNET_PQ_query_param_uint64 (&rcd[i].max_rtransaction_id), /* already 
inc'ed */
+        GNUNET_PQ_query_param_absolute_time (&now),
         GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub),
         GNUNET_PQ_query_param_string (ctx->reason),
         TALER_PQ_query_param_amount (increment),
@@ -5604,13 +5608,14 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
                             "INSERT INTO merchant_refunds"
                             "(order_serial"
                             ",rtransaction_id"
+                            ",refund_timestamp"
                             ",coin_pub"
                             ",reason"
                             ",refund_amount_val"
                             ",refund_amount_frac"
                             ") VALUES"
-                            "($1, $2, $3, $4, $5, $6)",
-                            6),
+                            "($1, $2, $3, $4, $5, $6, $7)",
+                            7),
     /* for postgres_increase_refund() */
     GNUNET_PQ_make_prepare ("find_deposits_for_refund",
                             "SELECT"
diff --git a/src/include/taler_merchantdb_plugin.h 
b/src/include/taler_merchantdb_plugin.h
index a58cb7d..cefc373 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -335,6 +335,30 @@ typedef void
   const struct TALER_Amount *refund_amount);
 
 
+/**
+ * Function called with detailed information about a refund.
+ *
+ * @param cls closure
+ * @param refund_serial unique serial number of the refund
+ * @param timestamp time of the refund (for grouping of refunds in the wallet 
UI)
+ * @param coin_pub public coin from which the refund comes from
+ * @param exchange_url URL of the exchange that issued @a coin_pub
+ * @param rtransaction_id identificator of the refund
+ * @param reason human-readable explanation of the refund
+ * @param refund_amount refund amount which is being taken from @a coin_pub
+ */
+typedef void
+(*TALER_MERCHANTDB_RefundDetailCallback)(
+  void *cls,
+  uint64_t refund_serial,
+  struct GNUNET_TIME_Absolute timestamp,
+  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  const char *exchange_url,
+  uint64_t rtransaction_id,
+  const char *reason,
+  const struct TALER_Amount *refund_amount);
+
+
 /**
  * Results from trying to increase a refund.
  */
@@ -1100,45 +1124,95 @@ struct TALER_MERCHANTDB_Plugin
                      const char *reason);
 
 
-  /* ****************** OLD API ******************** */
+  /**
+   * Obtain detailed refund data associated with a contract.
+   *
+   * @param cls closure, typically a connection to the db
+   * @param instance_id instance to lookup refunds for
+   * @param h_contract_terms hash code of the contract
+   * @param rc function to call for each coin on which there is a refund
+   * @param rc_cls closure for @a rc
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*lookup_refunds_detailed)(void *cls,
+                             const char *instance_id,
+                             const struct GNUNET_HashCode *h_contract_terms,
+                             TALER_MERCHANTDB_RefundDetailCallback rc,
+                             void *rc_cls);
+
+  /**
+   * Insert refund proof data from the exchange into the database.
+   *
+   * @param cls closure
+   * @param refund_serial serial number of the refund
+   * @param exchange_sig signature from exchange that coin was refunded
+   * @param exchange_pub signing key that was used for @a exchange_sig
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*insert_refund_proof)(void *cls,
+                         uint64_t refund_serial,
+                         const struct TALER_ExchangeSignatureP *exchange_sig,
+                         const struct TALER_ExchangePublicKeyP *exchange_pub);
 
 
   /**
-   * Store the order ID that was used to pay for a resource within a session.
+   * Lookup refund proof data.
    *
    * @param cls closure
-   * @param session_id session id
-   * @param fulfillment_url URL that canonically identifies the resource
-   *        being paid for
-   * @param order_id the order ID that was used when paying for the resource 
URL
-   * @param merchant_pub public key of the merchant, identifying the instance
+   * @param refund_serial serial number of the refund
+   * @param[out] exchange_sig set to signature from exchange
+   * @param[out] exchange_pub signing key that was used for @a exchange_sig
    * @return transaction status
    */
   enum GNUNET_DB_QueryStatus
-  (*insert_session_info)(void *cls,
-                         const char *session_id,
-                         const char *fulfillment_url,
-                         const char *order_id,
-                         const struct TALER_MerchantPublicKeyP *merchant_pub);
+  (*lookup_refund_proof)(void *cls,
+                         uint64_t refund_serial,
+                         struct TALER_ExchangeSignatureP *exchange_sig,
+                         struct TALER_ExchangePublicKeyP *exchange_pub);
+
 
   /**
    * Retrieve the order ID that was used to pay for a resource within a 
session.
    *
    * @param cls closure
+   * @param instance_id instance to lookup the order from
+   * @param fulfillment_url URL that canonically identifies the resource
+   *        being paid for
+   * @param session_id session id
    * @param[out] order_id location to store the order ID that was used when
    *             paying for the resource URL
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*lookup_order_by_fulfillment)(void *cls,
+                                 const char *instance_id,
+                                 const char *fulfillment_url,
+                                 const char *session_id,
+                                 char **order_id);
+
+
+  /* ****************** OLD API ******************** */
+
+
+  /**
+   * Store the order ID that was used to pay for a resource within a session.
+   *
+   * @param cls closure
    * @param session_id session id
    * @param fulfillment_url URL that canonically identifies the resource
    *        being paid for
+   * @param order_id the order ID that was used when paying for the resource 
URL
    * @param merchant_pub public key of the merchant, identifying the instance
    * @return transaction status
    */
   enum GNUNET_DB_QueryStatus
-  (*find_session_info)(void *cls,
-                       char **order_id,
-                       const char *session_id,
-                       const char *fulfillment_url,
-                       const struct TALER_MerchantPublicKeyP *merchant_pub);
+  (*insert_session_info)(void *cls,
+                         const char *session_id,
+                         const char *fulfillment_url,
+                         const char *order_id,
+                         const struct TALER_MerchantPublicKeyP *merchant_pub);
 
 
   /**

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

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