gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] 178/277: work on GET /private/orders/ID"


From: gnunet
Subject: [taler-merchant] 178/277: work on GET /private/orders/ID"
Date: Sun, 05 Jul 2020 20:51:31 +0200

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

grothoff pushed a commit to branch master
in repository merchant.

commit 78119045b5eaf622ac3752f6cc7f07d9cc5c2feb
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Thu Jun 4 21:00:32 2020 +0200

    work on GET /private/orders/ID"
---
 .../taler-merchant-httpd_private-get-orders-ID.c   | 444 ++++++++++++++++++++-
 src/backenddb/merchant-0001.sql                    |   4 +-
 2 files changed, 430 insertions(+), 18 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_private-get-orders-ID.c 
b/src/backend/taler-merchant-httpd_private-get-orders-ID.c
index 2de1d77..327ccf7 100644
--- a/src/backend/taler-merchant-httpd_private-get-orders-ID.c
+++ b/src/backend/taler-merchant-httpd_private-get-orders-ID.c
@@ -24,6 +24,68 @@
 #include <taler/taler_json_lib.h>
 #include "taler-merchant-httpd_mhd.h"
 
+/**
+ * How long do we wait on the exchange?
+ */
+#define EXCHANGE_TIMEOUT GNUNET_TIME_relative_multiply ( \
+    GNUNET_TIME_UNIT_SECONDS, 30)
+
+
+/**
+ * Data structure we keep for a check payment request.
+ */
+struct GetOrderRequestContext;
+
+
+/**
+ * Request to an exchange for details about wire transfers
+ * in response to a coin's deposit operation.
+ */
+struct TransferQuery
+{
+
+  /**
+   * Kept in a DLL.
+   */
+  struct TransferQuery *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct TransferQuery *prev;
+
+  /**
+   * Handle to query exchange about deposit status.
+   */
+  struct TALER_EXCHANGE_DepositGetHandle *dgh;
+
+  /**
+   * Handle for ongoing exchange operation.
+   */
+  struct TMH_EXCHANGES_FindOperation *fo;
+
+  /**
+   * Overall request this TQ belongs with.
+   */
+  struct GetOrderRequestContext *gorc;
+
+  /**
+   * Hash of the merchant's bank account the transfer (presumably) went to.
+   */
+  struct GNUNET_HashCode h_wire;
+
+  /**
+   * Public key of the coin this is about.
+   */
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+
+  /**
+   * Which deposit operation is this about?
+   */
+  uint64_t deposit_serial;
+
+};
+
 
 /**
  * Data structure we keep for a check payment request.
@@ -53,12 +115,53 @@ struct GetOrderRequestContext
    */
   const char *fulfillment_url;
 
+  /**
+   * Kept in a DLL while suspended on exchange.
+   */
+  struct GetOrderRequestContext *next;
+
+  /**
+   * Kept in a DLL while suspended on exchange.
+   */
+  struct GetOrderRequestContext *prev;
+
+  /**
+   * Handle to the exchange, only valid while the @e fo succeeds.
+   */
+  struct TALER_EXCHANGE_Handle *eh;
+
+  /**
+   * Head of DLL of individual queries for transfer data.
+   */
+  struct TransferQuery *tq_head;
+
+  /**
+   * Tail of DLL of individual queries for transfer data.
+   */
+  struct TransferQuery *tq_tail;
+
+  /**
+   * Timeout task while waiting on exchange.
+   */
+  struct GNUNET_SCHEDULER_Task *tt;
+
   /**
    * Contract terms of the payment we are checking. NULL when they
    * are not (yet) known.
    */
   json_t *contract_terms;
 
+  /**
+   * Wire details for the payment, to be returned to the query. NULL
+   * if not available.
+   */
+  json_t *wire_details;
+
+  /**
+   * Hash over the @e contract_terms.
+   */
+  struct GNUNET_HashCode h_contract_terms;
+
   /**
    * Serial ID of the order.
    */
@@ -70,6 +173,24 @@ struct GetOrderRequestContext
    */
   struct TALER_Amount refund_amount;
 
+  /**
+   * Exchange HTTP error code encountered while trying to determine wire 
transfer
+   * details. #TALER_EC_NONE for no error encountered.
+   */
+  unsigned int exchange_hc;
+
+  /**
+   * Exchange error code encountered while trying to determine wire transfer
+   * details. #TALER_EC_NONE for no error encountered.
+   */
+  enum TALER_ErrorCode exchange_ec;
+
+  /**
+   * Error code encountered while trying to determine wire transfer
+   * details. #TALER_EC_NONE for no error encountered.
+   */
+  enum TALER_ErrorCode wire_ec;
+
   /**
    * Set to true if this payment has been refunded and
    * @e refund_amount is initialized.
@@ -85,6 +206,236 @@ struct GetOrderRequestContext
 };
 
 
+/**
+ * Head of list of suspended requests waiting on the exchange.
+ */
+static struct GetOrderRequestContext *gorc_head;
+
+/**
+ * Tail of list of suspended requests waiting on the exchange.
+ */
+static struct GetOrderRequestContext *gorc_tail;
+
+
+/**
+ *
+ * @param gorc request to resume
+ * @param ec error code for the request, #TALER_EC_NONE on success
+ * @param exchange_hr details from exchange, NULL if exchange is blameless
+ */
+static void
+gorc_resume (struct GetOrderRequestContext *gorc,
+             enum TALER_ErrorCode ec,
+             const struct TALER_EXCHANGE_HttpResponse *exchange_hr)
+{
+  struct TransferQuery *tq;
+
+  if (NULL != gorc->tt)
+  {
+    GNUNET_SCHEDULER_cancel (gorc->tt);
+    gorc->tt = NULL;
+  }
+  while (NULL != (tq = gorc->tq_head))
+  {
+    if (NULL != tq->fo)
+    {
+      TMH_EXCHANGES_find_exchange_cancel (tq->fo);
+      tq->fo = NULL;
+    }
+    if (NULL != tq->dgh)
+    {
+      TALER_EXCHANGE_deposits_get_cancel (tq->dgh);
+      tq->dgh = NULL;
+    }
+  }
+  GNUNET_CONTAINER_DLL_remove (gorc_head,
+                               gorc_tail,
+                               gorc);
+  MHD_resume_connection (gorc->sc.connection);
+  GNUNET_CONTAINER_DLL_remove (gorc_head,
+                               gorc_tail,
+                               gorc);
+  gorc->wire_ec = ec;
+  if (NULL != exchange_hr)
+  {
+    gorc->exchange_hc = exchange_hr->http_status;
+    gorc->exchange_ec = exchange_hr->error_code;
+  }
+  MHD_resume_connection (gorc->sc.connection);
+}
+
+
+/**
+ * Timeout trying to get current wire transfer data from the exchange.
+ * Clean up and continue.
+ *
+ * @param cls closure, must be a `struct GetOrderRequestContext *`
+ */
+static void
+exchange_timeout_cb (void *cls)
+{
+  struct GetOrderRequestContext *gorc = cls;
+
+  gorc->tt = NULL;
+  gorc_resume (gorc,
+               42, // FIXME: EC
+               NULL);
+}
+
+
+/**
+ * Function called with detailed wire transfer data.
+ *
+ * @param cls closure with a `struct TransferQuery *`
+ * @param hr HTTP response data
+ * @param dd details about the deposit (NULL on errors)
+ */
+static void
+deposit_get_cb (void *cls,
+                const struct TALER_EXCHANGE_HttpResponse *hr,
+                const struct TALER_EXCHANGE_DepositData *dd)
+{
+  struct TransferQuery *tq = cls;
+  struct GetOrderRequestContext *gorc = tq->gorc;
+
+  GNUNET_CONTAINER_DLL_remove (gorc->tq_head,
+                               gorc->tq_tail,
+                               tq);
+  if (NULL == dd)
+  {
+    gorc_resume (gorc,
+                 42, // FIXME: EC
+                 hr);
+    GNUNET_free (tq);
+    return;
+  }
+  else
+  {
+    enum GNUNET_DB_QueryStatus qs;
+
+    qs = TMH_db->insert_deposit_to_transfer (TMH_db->cls,
+                                             tq->deposit_serial,
+                                             dd);
+    if (qs < 0)
+    {
+      gorc_resume (gorc,
+                   42, // FIXME: EC
+                   NULL);
+      GNUNET_free (tq);
+      return;
+    }
+  }
+  GNUNET_free (tq);
+  if (NULL != gorc->tq_head)
+    return;
+  /* *all* are done, resume! */
+  gorc_resume (gorc,
+               TALER_EC_NONE,
+               NULL);
+}
+
+
+/**
+ * Function called with the result of a #TMH_EXCHANGES_find_exchange()
+ * operation.
+ *
+ * @param cls closure with a `struct GetOrderRequestContext *`
+ * @param hr HTTP response details
+ * @param eh handle to the exchange context
+ * @param payto_uri payto://-URI of the exchange
+ * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if 
not available
+ * @param exchange_trusted true if this exchange is trusted by config
+ */
+static void
+exchange_found_cb (void *cls,
+                   const struct TALER_EXCHANGE_HttpResponse *hr,
+                   struct TALER_EXCHANGE_Handle *eh,
+                   const char *payto_uri,
+                   const struct TALER_Amount *wire_fee,
+                   bool exchange_trusted)
+{
+  struct TransferQuery *tq = cls;
+  struct GetOrderRequestContext *gorc = tq->gorc;
+
+  tq->fo = NULL;
+  if (NULL == eh)
+  {
+    /* failed */
+    GNUNET_CONTAINER_DLL_remove (gorc->tq_head,
+                                 gorc->tq_tail,
+                                 tq);
+    GNUNET_free (tq);
+    gorc_resume (gorc,
+                 42, // FIXME: EC!
+                 hr);
+    return;
+  }
+  tq->dgh = TALER_EXCHANGE_deposits_get (eh,
+                                         &gorc->hc->instance.merchant_priv,
+                                         &tq->h_wire,
+                                         &gorc->h_contract_terms,
+                                         &tq->coin_pub,
+                                         &deposit_get_callback,
+                                         tq);
+  if (NULL == tq->dgh)
+  {
+    GNUNET_CONTAINER_DLL_remove (gorc->tq_head,
+                                 gorc->tq_tail,
+                                 tq);
+    GNUNET_free (tq);
+    gorc_resume (gorc,
+                 42,
+                 NULL);
+  }
+}
+
+
+/**
+ * Function called with each @a coin_pub that was deposited into the
+ * @a h_wire account of the merchant for the @a deposit_serial as part
+ * of the payment for the order identified by @a cls.
+ *
+ * Queries the exchange for the payment status associated with the
+ * given coin.
+ *
+ * @param cls a `struct GetOrderRequestContext`
+ * @param deposit_serial identifies the deposit operation
+ * @param exchange_url URL of the exchange that issued @a coin_pub
+ * @param h_wire hash of the merchant's wire account into which the deposit 
was made
+ * @param coin_pub public key of the deposited coin
+ */
+static void
+deposit_cb (void *cls,
+            uint64_t deposit_serial,
+            const char *exchange_url,
+            const struct GNUNET_HashCode *h_wire,
+            const struct TALER_CoinSpendPublicKeyP *coin_pub)
+{
+  struct GetOrderRequestContext *gorc = cls;
+  struct TransferQuery *tq;
+
+  tq = GNUNET_new (struct TransferQuery);
+  tq->gorc = gorc;
+  tq->deposit_serial = deposit_serial;
+  GNUNET_CONTAINER_DLL_insert (gorc->tq_head,
+                               gorc->tq_tail,
+                               tq);
+  tq->coin_pub = *coin_pub;
+  tq->h_wire = *h_wire;
+  tq->fo = TMH_EXCHANGES_find_exchange (exchange_url,
+                                        NULL,
+                                        GNUNET_NO,
+                                        &exchange_found_cb,
+                                        tq);
+  if (NULL = tq->fo)
+  {
+    gorc_resume (gorc,
+                 42, // FIXME: EC
+                 NULL);
+  }
+}
+
+
 /**
  * Clean up the session state for a GET /private/order/ID request.
  *
@@ -97,6 +448,8 @@ gorc_cleanup (void *cls)
 
   if (NULL != gorc->contract_terms)
     json_decref (gorc->contract_terms);
+  GNUNET_assert (NULL == gorc->tt);
+  GNUNET_assert (NULL == gorc->fo);
   GNUNET_free (gorc);
 }
 
@@ -140,6 +493,30 @@ process_refunds_cb (void *cls,
 }
 
 
+/**
+ * Function called with available wire details, to be added to
+ * the response.
+ *
+ * @param cls a `struct GetOrderRequestContext`
+ */
+static void
+process_wire_details (void *cls,
+                      ...)
+{
+  struct GetOrderRequestContext *gorc = cls;
+  json_t *wire_details = gorc->wire_details;
+
+
+  GNUNET_assert (0 ==
+                 json_array_append_new (wire_details,
+                                        json_pack ("{s:s, s:b}",
+                                                   "blah",
+                                                   "stuff",
+                                                   "FIXME",
+                                                   false)));
+}
+
+
 /**
  * Manages a GET /private/orders/ID call, checking the status of a payment and
  * refunds and, if necessary, constructing the URL for a payment redirect URL.
@@ -235,9 +612,14 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler 
*rh,
                                          
TALER_EC_CHECK_PAYMENT_DB_FETCH_CONTRACT_TERMS_ERROR,
                                          "db error fetching contract terms");
     }
-    // FIXME: handle qs!
-    // => mainly: if 0 return 404!
-
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_NOT_FOUND,
+                                         TALER_EC_ORDERS_GET_ORDER_NOT_FOUND,
+                                         "Did not find order in DB");
+    }
 
     /* extract the fulfillment URL from the contract terms! */
     {
@@ -260,6 +642,19 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler 
*rh,
           "Merchant database error (contract terms corrupted)");
       }
     }
+    if (GNUNET_OK !=
+        TALER_JSON_hash (gorc->contract_terms,
+                         &gorc->h_contract_terms))
+    {
+      GNUNET_break (0);
+      return (MHD_YES ==
+              TALER_MHD_reply_with_error (connection,
+                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                          
TALER_EC_ORDERS_GET_FAILED_COMPUTE_PROPOSAL_HASH,
+                                          "Failed to hash contract terms"))
+             ? GNUNET_NO
+             : GNUNET_SYSERR;
+    }
   }
 
   GNUNET_assert (NULL != gorc->contract_terms);
@@ -333,17 +728,35 @@ TMH_private_get_orders_ID (const struct 
TMH_RequestHandler *rh,
         (! wired) &&
         gorc->transfer_status_requested)
     {
-      // FIXME: suspend connection, go to exhange to ask for the
-      // wire transfer status, and resume once we have it!
-      // (make sure to set gorc->transfer_status_requested to FALSE
-      // while we are at it!)
-      return MHD_YES;
+      /* suspend connection, wait for exchange to check wire transfer status 
there */
+      gorc->transfer_status_requested = false; /* only try ONCE */
+      TMH_db->lookup_deposits (TMH_db->cls,
+                               gorc->order_serial,
+                               &deposit_cb,
+                               gorc);
+      if (NULL != gorc->tq)
+      {
+        GNUNET_CONTAINER_DLL_insert (gorc_head,
+                                     gorc_tail,
+                                     gorc);
+        gorc->tt = GNUNET_SCHEDULER_add_delayed (EXCHANGE_TIMEOUT,
+                                                 &exchange_timeout_cb,
+                                                 gorc);
+        MHD_suspend_connection (connection);
+        return MHD_YES;
+      }
     }
 
     if ( (! paid) &&
          (0 != gorc.sc.long_poll_timeout.abs_value_us) )
     {
-      // FIXME: suspend connection, wait for payment!
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Suspending GET /private/orders/%s\n",
+                  hc->infix);
+      TMH_long_poll_suspend (hc->infix,
+                             hc->instance,
+                             &gorc->sc,
+                             NULL);
       return MHD_YES;
     }
 
@@ -372,9 +785,7 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler 
*rh,
 
     /* Accumulate refunds, if any. */
     {
-      struct GNUNET_HashCode h_contract_terms;
 
-      // FIXME: init h_contract_terms!
       qs = TMH_db->lookup_refunds (TMH_db->cls,
                                    hc->instance.settings->id,
                                    &h_contract_terms,
@@ -386,20 +797,20 @@ TMH_private_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_PAY_DB_FETCH_TRANSACTION_ERROR,
+                                         
TALER_EC_ORDERS_GET_DB_FETCH_TRANSACTION_ERROR,
                                          "Merchant database error");
     }
   } /* end of scope of 'paid/wired' */
 
   /* Generate final reply, including wire details if we have them */
   {
-    json_t *wire_details = NULL;
-
+    gorc->wire_details = json_array_new ();
+    GNUNET_assert (NULL != gorc->wire_details);
 #if FIXME
     qs = TMH_db->select_wire_details_by_order (TMH_db->cls,
                                                gorc->order_serial,
                                                &process_wire_details,
-                                               &wire_details);
+                                               gorc);
 #endif
     if (0 > qs)
     {
@@ -409,6 +820,7 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler 
*rh,
                                          
TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR,
                                          "Merchant database error");
     }
+    // FIXME: also return gorc->ec/exchange_(hc,ec) codes!
     return TALER_MHD_reply_json_pack (connection,
                                       MHD_HTTP_OK,
                                       "{s:O, s:b, s:b, s:o?, s:o?}",
@@ -424,6 +836,6 @@ TMH_private_get_orders_ID (const struct TMH_RequestHandler 
*rh,
                                         &gorc->refund_amount)
                                       : NULL,
                                       "wire_details",
-                                      wire_details);
+                                      gorc->wire_details);
   }
 }
diff --git a/src/backenddb/merchant-0001.sql b/src/backenddb/merchant-0001.sql
index d74b852..34e2cb4 100644
--- a/src/backenddb/merchant-0001.sql
+++ b/src/backenddb/merchant-0001.sql
@@ -470,8 +470,8 @@ CREATE TABLE IF NOT EXISTS merchant_tips
   ,expiration INT8 NOT NULL
   ,amount_val INT8 NOT NULL
   ,amount_frac INT4 NOT NULL
-  ,picked_up_val INT8 NOT NULL
-  ,picked_up_frac INT4 NOT NULL
+  ,picked_up_val INT8 NOT NULL DEFAULT 0
+  ,picked_up_frac INT4 NOT NULL DEFAULT 0
   ,was_picked_up BOOLEAN NOT NULL DEFAULT FALSE
   );
 CREATE INDEX IF NOT EXISTS merchant_tips_by_pickup_and_expiration

-- 
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]