gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] 64/277: work on POST /orders/ID/claim (unfinished)


From: gnunet
Subject: [taler-merchant] 64/277: work on POST /orders/ID/claim (unfinished)
Date: Sun, 05 Jul 2020 20:49:37 +0200

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

grothoff pushed a commit to branch master
in repository merchant.

commit 255f162ddb88e932d087574a52d959a7a1d5ba80
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Mon Apr 27 05:44:17 2020 +0200

    work on POST /orders/ID/claim (unfinished)
---
 src/backend/Makefile.am                            |   4 +-
 src/backend/taler-merchant-httpd.c                 |   9 +
 .../taler-merchant-httpd_post-orders-ID-claim.c    | 304 +++++++++++----------
 .../taler-merchant-httpd_post-orders-ID-claim.h    |  31 +--
 ...taler-merchant-httpd_private-delete-orders-ID.c |   4 +
 src/include/taler_merchantdb_plugin.h              |  50 ++--
 6 files changed, 220 insertions(+), 182 deletions(-)

diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 74c3b20..316f6e3 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -50,7 +50,9 @@ taler_merchant_httpd_SOURCES = \
   taler-merchant-httpd_private-post-products-ID-lock.c \
     taler-merchant-httpd_private-post-products-ID-lock.h \
   taler-merchant-httpd_private-post-orders.c \
-    taler-merchant-httpd_private-post-orders.h
+    taler-merchant-httpd_private-post-orders.h \
+  taler-merchant-httpd_post-orders-ID-claim.c \
+    taler-merchant-httpd_post-orders-ID-claim.h
 
 DEAD = \
   taler-merchant-httpd_check-payment.c taler-merchant-httpd_check-payment.h \
diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index 2534e07..7406d1f 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -42,6 +42,7 @@
 #include "taler-merchant-httpd_private-post-products.h"
 #include "taler-merchant-httpd_private-post-products-ID-lock.h"
 #include "taler-merchant-httpd_private-post-orders.h"
+#include "taler-merchant-httpd_post-orders-ID-claim.h"
 
 
 /**
@@ -834,6 +835,14 @@ url_handler (void *cls,
       .skip_instance = true,
       .handler = &MH_handler_config
     },
+    /* POST /orders/$ID/claim: */
+    {
+      .url_prefix = "/orders/",
+      .have_id_segment = true,
+      .url_suffix = "claim",
+      .method = MHD_HTTP_METHOD_POST,
+      .handler = &TMH_post_orders_ID_claim
+    },
     {
       NULL
     }
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-claim.c 
b/src/backend/taler-merchant-httpd_post-orders-ID-claim.c
index 4720713..e58a73c 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-claim.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-claim.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  (C) 2014, 2015, 2016, 2018 Taler Systems SA
+  (C) 2014, 2015, 2016, 2018, 2020 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as
@@ -18,18 +18,16 @@
 */
 
 /**
- * @file backend/taler-merchant-httpd_proposal.c
- * @brief HTTP serving layer mainly intended to communicate
- * with the frontend
+ * @file backend/taler-merchant-httpd_post-orders-ID-claim.c
+ * @brief headers for POST /orders/$ID/claim handler
  * @author Marcello Stanisci
+ * @author Christian Grothoff
  */
 #include "platform.h"
 #include <jansson.h>
 #include <taler/taler_signatures.h>
 #include <taler/taler_json_lib.h>
-#include "taler-merchant-httpd.h"
-#include "taler-merchant-httpd_auditors.h"
-#include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_post-orders-ID-claim.h"
 
 
 /**
@@ -37,163 +35,193 @@
  */
 #define MAX_RETRIES 3
 
+
 /**
- * Manage a GET /proposal request. Query the db and returns the
- * proposal's data related to the transaction id given as the URL's
- * parameter.
+ * Run transaction to claim @a order_id for @a nonce.
  *
- * Binds the proposal to a nonce.
+ * @param instance_id instance to claim order at
+ * @param order_id order to claim
+ * @param nonce nonce to use for the claim
+ * @param[out] contract_terms set to the resulting contract terms
+ *             (for any non-negative result;
+ * @return transaction status code
+ *         #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the order was claimed by a 
different
+ *         nonce (@a contract_terms set to non-NULL)
+ *                OR if the order is is unknown (@a contract_terms is NULL)
+ *         #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the order was successfully 
claimed
+ */
+static enum GNUNET_DB_QueryStatus
+claim_order (const char *instance_id,
+             const char *order_id,
+             const char *nonce,
+             json_t **contract_terms)
+{
+  enum GNUNET_DB_QueryStatus qs;
+
+  if (GNUNET_OK !=
+      TMH_db->start (TMH_db->cls,
+                     "claim order"))
+  {
+    GNUNET_break (0);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  qs = TMH_db->lookup_contract_terms (TMH_db->cls,
+                                      order_id,
+                                      instance_id,
+                                      contract_terms);
+  if (0 > qs)
+  {
+    TMH_db->rollback (TMH_db->cls);
+    return qs;
+  }
+
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+  {
+    /* see if we have this order in our table of unclaimed orders */
+    qs = TMH_db->lookup_order (TMH_db->cls,
+                               instance_id,
+                               order_id,
+                               contract_terms);
+    if (0 >= qs)
+    {
+      TMH_db->rollback (TMH_db->cls);
+      return qs;
+    }
+    GNUNET_assert (NULL != contract_terms);
+    GNUNET_assert (0 ==
+                   json_object_set_new (*contract_terms,
+                                        "nonce",
+                                        json_string (nonce)));
+    qs = TMH_db->insert_contract_terms (TMH_db->cls,
+                                        instance_id,
+                                        order_id,
+                                        *contract_terms);
+    if (0 > qs)
+    {
+      TMH_db->rollback (TMH_db->cls);
+      json_decref (*contract_terms);
+      *contract_terms = NULL;
+      return qs;
+    }
+    // FIXME: should we remove the ORDER from the order table here?
+    qs = TMH_db->commit (TMH_db->cls);
+    if (0 > qs)
+      return qs;
+    return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+  }
+  else
+  {
+    const char *stored_nonce;
+
+    TMH_db->rollback (TMH_db->cls);
+    GNUNET_assert (NULL != *contract_terms);
+    stored_nonce
+      = json_string_value (json_object_get (*contract_terms,
+                                            "nonce"));
+    if (NULL == stored_nonce)
+    {
+      /* this should not be possible: contract_terms should always
+         have a nonce! */
+      GNUNET_break (0);
+      return GNUNET_DB_STATUS_HARD_ERROR;
+    }
+    if (0 != strcmp (stored_nonce,
+                     nonce))
+    {
+      return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+    }
+    return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+  }
+}
+
+
+/**
+ * Manage a POST /orders/$ID/claim request.  Allows the client to
+ * claim the order (unless already claims) and creates the respective
+ * contract.  Returns the contract terms.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL
+ * @param[in,out] hc context with further information about the request
  * @return MHD result code
  */
 MHD_RESULT
-MH_handler_proposal_lookup (struct TMH_RequestHandler *rh,
-                            struct MHD_Connection *connection,
-                            void **connection_cls,
-                            const char *upload_data,
-                            size_t *upload_data_size,
-                            struct MerchantInstance *mi)
+TMH_post_orders_ID_claim (const struct TMH_RequestHandler *rh,
+                          struct MHD_Connection *connection,
+                          struct TMH_HandlerContext *hc)
 {
-  const char *order_id;
+  const char *order_id = hc->infix;
   const char *nonce;
   enum GNUNET_DB_QueryStatus qs;
   json_t *contract_terms;
-  struct GNUNET_CRYPTO_EddsaSignature merchant_sig;
-  const char *stored_nonce;
 
-  order_id = MHD_lookup_connection_value (connection,
-                                          MHD_GET_ARGUMENT_KIND,
-                                          "order_id");
-  if (NULL == order_id)
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_BAD_REQUEST,
-                                       TALER_EC_PARAMETER_MISSING,
-                                       "order_id");
-  nonce = MHD_lookup_connection_value (connection,
-                                       MHD_GET_ARGUMENT_KIND,
-                                       "nonce");
-  if (NULL == nonce)
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_BAD_REQUEST,
-                                       TALER_EC_PARAMETER_MISSING,
-                                       "nonce");
-  db->preflight (db->cls);
-  qs = db->find_contract_terms (db->cls,
-                                &contract_terms,
-                                order_id,
-                                &mi->pubkey);
-  if (0 > qs)
-  {
-    /* single, read-only SQL statements should never cause
-       serialization problems */
-    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != 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_PROPOSAL_LOOKUP_DB_ERROR,
-                                       "An error occurred while retrieving 
proposal data from db");
-  }
-  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
-    struct GNUNET_TIME_Absolute timestamp;
     struct GNUNET_JSON_Specification spec[] = {
-      GNUNET_JSON_spec_absolute_time ("timestamp", &timestamp),
+      GNUNET_JSON_spec_string ("nonce",
+                               &nonce),
       GNUNET_JSON_spec_end ()
     };
     enum GNUNET_GenericReturnValue res;
 
-    db->preflight (db->cls);
-    qs = db->find_order (db->cls,
-                         &contract_terms,
-                         order_id,
-                         &mi->pubkey);
-    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-    {
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_NOT_FOUND,
-                                         TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND,
-                                         "unknown order id");
-    }
-    GNUNET_assert (NULL != contract_terms);
-    json_object_set_new (contract_terms,
-                         "nonce",
-                         json_string (nonce));
-
-    /* extract fields we need to sign separately */
     res = TALER_MHD_parse_json_data (connection,
-                                     contract_terms,
+                                     hc->request_body,
                                      spec);
+    /* json is malformed */
     if (GNUNET_NO == res)
     {
+      GNUNET_break_op (0);
       return MHD_YES;
     }
+    /* other internal errors might have occurred */
     if (GNUNET_SYSERR == res)
-    {
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         TALER_EC_PROPOSAL_ORDER_PARSE_ERROR,
-                                         "Impossible to parse the order");
-    }
-
-    for (unsigned int i = 0; i<MAX_RETRIES; i++)
-    {
-      db->preflight (db->cls);
-      qs = db->insert_contract_terms (db->cls,
-                                      order_id,
-                                      &mi->pubkey,
-                                      timestamp,
-                                      contract_terms);
-      if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
-        break;
-    }
-    if (0 > qs)
-    {
-      /* Special report if retries insufficient */
-      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != 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_PROPOSAL_STORE_DB_ERROR,
-                                         "db error: could not store this 
proposal's data into db");
-    }
-    // FIXME: now we can delete (merchant_pub, order_id) from the 
merchant_orders table
+                                         MHD_HTTP_BAD_REQUEST,
+                                         TALER_EC_PARAMETER_MISSING,
+                                         "nonce");
   }
-
-  GNUNET_assert (NULL != contract_terms);
-
-  stored_nonce
-    = json_string_value (json_object_get (contract_terms,
-                                          "nonce"));
-
-  if (NULL == stored_nonce)
+  contract_terms = NULL;
+  for (unsigned int i = 0; i<MAX_RETRIES; i++)
   {
-    GNUNET_break (0);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       TALER_EC_PROPOSAL_ORDER_PARSE_ERROR,
-                                       "existing proposal has no nonce");
+    TMH_db->preflight (TMH_db->cls);
+    qs = claim_order (hc->instance->settings.id,
+                      order_id,
+                      nonce,
+                      &contract_terms);
+    if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
+      break;
   }
-
-  if (0 != strcmp (stored_nonce,
-                   nonce))
+  switch (qs)
   {
+  case GNUNET_DB_STATUS_HARD_ERROR:
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_ORDERS_CLAIM_HARD_DB_ERROR,
+                                       "Failed to run DB transaction to claim 
order");
+  case GNUNET_DB_STATUS_SOFT_ERROR:
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_ORDERS_CLAIM_SOFT_DB_ERROR,
+                                       "Failed to serialize DB transaction to 
claim order");
+  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+    if (NULL == contract_terms)
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_NOT_FOUND,
+                                         TALER_EC_ORDERS_CLAIM_NOT_FOUND,
+                                         "unknown order id");
+    /* already claimed! */
+    json_decref (contract_terms);
     return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_BAD_REQUEST,
-                                       TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND,
-                                       "mismatched nonce");
+                                       MHD_HTTP_CONFLICT,
+                                       TALER_EC_ORDERS_ALREADY_CLAIMED,
+                                       "order already claimed");
+  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+    GNUNET_assert (NULL != contract_terms);
+    break; /* Good! return signature (below) */
   }
 
-
   /* create proposal signature */
   {
+    struct GNUNET_CRYPTO_EddsaSignature merchant_sig;
     struct TALER_ProposalDataPS pdps = {
       .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT),
       .purpose.size = htonl (sizeof (pdps))
@@ -210,19 +238,19 @@ MH_handler_proposal_lookup (struct TMH_RequestHandler *rh,
                                          "Could not hash order");
     }
 
-    GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv,
+    GNUNET_CRYPTO_eddsa_sign (&hc->instance->merchant_priv.eddsa_priv,
                               &pdps,
                               &merchant_sig);
+    return TALER_MHD_reply_json_pack (connection,
+                                      MHD_HTTP_OK,
+                                      "{ s:o, s:o }",
+                                      "contract_terms",
+                                      contract_terms,
+                                      "sig",
+                                      GNUNET_JSON_from_data_auto (
+                                        &merchant_sig));
   }
-  return TALER_MHD_reply_json_pack (connection,
-                                    MHD_HTTP_OK,
-                                    "{ s:o, s:o }",
-                                    "contract_terms",
-                                    contract_terms,
-                                    "sig",
-                                    GNUNET_JSON_from_data_auto (
-                                      &merchant_sig));
 }
 
 
-/* end of taler-merchant-httpd_proposal.c */
+/* end of taler-merchant-httpd_post-orders-ID-claim.c */
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-claim.h 
b/src/backend/taler-merchant-httpd_post-orders-ID-claim.h
index 677fee0..5da1607 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-claim.h
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-claim.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  (C) 2014, 2015 INRIA
+  (C) 2014, 2015, 2020 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
@@ -14,34 +14,29 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file backend/taler-merchant-httpd_propose.h
- * @brief headers for /contract handler
+ * @file backend/taler-merchant-httpd_post-orders-ID-claim.h
+ * @brief headers for POST /orders/$ID/claim handler
  * @author Marcello Stanisci
+ * @author Christian Grothoff
  */
-#ifndef TALER_MERCHANT_HTTPD_CONTRACT_H
-#define TALER_MERCHANT_HTTPD_CONTRACT_H
+#ifndef TALER_MERCHANT_HTTPD_POST_ORDERS_ID_CLAIM_H
+#define TALER_MERCHANT_HTTPD_POST_ORDERS_ID_CLAIM_H
 #include <microhttpd.h>
 #include "taler-merchant-httpd.h"
 
 /**
- * Manage a GET /proposal request. Query the db and returns the
- * proposal's data related to the transaction id given as the URL's
- * parameter.
+ * Manage a POST /orders/$ID/claim request.  Allows the client to
+ * claim the order (unless already claims) and creates the respective
+ * contract.  Returns the contract terms.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL
+ * @param[in,out] hc context with further information about the request
  * @return MHD result code
  */
 MHD_RESULT
-MH_handler_proposal_lookup (struct TMH_RequestHandler *rh,
-                            struct MHD_Connection *connection,
-                            void **connection_cls,
-                            const char *upload_data,
-                            size_t *upload_data_size,
-                            struct MerchantInstance *mi);
+TMH_post_orders_ID_claim (const struct TMH_RequestHandler *rh,
+                          struct MHD_Connection *connection,
+                          struct TMH_HandlerContext *hc);
 
 #endif
diff --git a/src/backend/taler-merchant-httpd_private-delete-orders-ID.c 
b/src/backend/taler-merchant-httpd_private-delete-orders-ID.c
index cddbc07..f699bdc 100644
--- a/src/backend/taler-merchant-httpd_private-delete-orders-ID.c
+++ b/src/backend/taler-merchant-httpd_private-delete-orders-ID.c
@@ -40,6 +40,10 @@ TMH_private_delete_orders_ID (const struct 
TMH_RequestHandler *rh,
   enum GNUNET_DB_QueryStatus qs;
 
   GNUNET_assert (NULL != mi);
+  // FIXME: do we delete ORDERS or (claimed) contract_terms?
+  // FIXME: what SHOULD be the semantics here?
+  // NOTE: We MAY need the delete_order() DB API to
+  //       clean up the order table when claiming orders...
   qs = TMH_db->delete_order (TMH_db->cls,
                              mi->settings.id,
                              hc->infix);
diff --git a/src/include/taler_merchantdb_plugin.h 
b/src/include/taler_merchantdb_plugin.h
index 3d36a1c..fa255e6 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -796,25 +796,40 @@ struct TALER_MERCHANTDB_Plugin
                        uint32_t quantity);
 
 
-  /* ****************** OLD API ******************** */
+  /**
+   * Retrieve contract terms given its @a order_id
+   *
+   * @param cls closure
+   * @param instance_id instance's identifier
+   * @param order_id order_id used to lookup.
+   * @param[out] contract_terms where to store the result
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*lookup_contract_terms)(void *cls,
+                           const char *instance_id,
+                           const char *order_id,
+                           json_t **contract_terms);
+
 
   /**
-   * Insert proposal data into db; the routine will internally hash and
-   * insert the proposal data's hashcode into the same row.
+   * Store contract terms given its @a order_id
    *
    * @param cls closure
-   * @param order_id alphanumeric string that uniquely identifies the proposal
-   * @param merchant_pub merchant's public key
-   * @param timestamp timestamp of this proposal data
-   * @param contract_terms proposal data to store
+   * @param instance_id instance's identifier
+   * @param order_id order_id used to store
+   * @param[out] contract_terms contract to store
    * @return transaction status
    */
   enum GNUNET_DB_QueryStatus
   (*insert_contract_terms)(void *cls,
+                           const char *instance_id,
                            const char *order_id,
-                           const struct TALER_MerchantPublicKeyP *merchant_pub,
-                           struct GNUNET_TIME_Absolute timestamp,
-                           const json_t *contract_terms);
+                           json_t *contract_terms);
+
+
+  /* ****************** OLD API ******************** */
+
 
   /**
    * Mark contract terms as paid.  Needed by /history as only paid
@@ -873,21 +888,6 @@ struct TALER_MERCHANTDB_Plugin
                        const char *fulfillment_url,
                        const struct TALER_MerchantPublicKeyP *merchant_pub);
 
-  /**
-   * Retrieve proposal data given its order ID.
-   *
-   * @param cls closure
-   * @param[out] contract_terms where to store the result
-   * @param order_id order_id used to lookup.
-   * @param merchant_pub instance's public key.
-   * @return transaction status
-   */
-  enum GNUNET_DB_QueryStatus
-  (*find_contract_terms)(void *cls,
-                         json_t **contract_terms,
-                         const char *order_id,
-                         const struct TALER_MerchantPublicKeyP *merchant_pub);
-
 
   /**
    * Retrieve proposal data given its hashcode

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