gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] branch master updated: implement & test POST /paid


From: gnunet
Subject: [taler-merchant] branch master updated: implement & test POST /paid
Date: Fri, 24 Jul 2020 09:15:26 +0200

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

jonathan-buchanan pushed a commit to branch master
in repository merchant.

The following commit(s) were added to refs/heads/master by this push:
     new 974b2b2  implement & test POST /paid
974b2b2 is described below

commit 974b2b2c3077c929850c1598af1e1e124f530a7f
Author: Jonathan Buchanan <jonathan.russ.buchanan@gmail.com>
AuthorDate: Fri Jul 24 03:15:16 2020 -0400

    implement & test POST /paid
---
 src/backend/taler-merchant-httpd.c                 |  11 +
 .../taler-merchant-httpd_post-orders-ID-paid.c     | 112 +++++++--
 src/include/taler_merchant_testing_lib.h           |  19 ++
 src/testing/Makefile.am                            |   1 +
 src/testing/test_merchant_api.c                    |   5 +
 src/testing/testing_api_cmd_post_orders_paid.c     | 258 +++++++++++++++++++++
 6 files changed, 388 insertions(+), 18 deletions(-)

diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index 26c6bb9..b6cb1cc 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -1048,6 +1048,17 @@ url_handler (void *cls,
          to set a conservative bound for sane wallets */
       .max_upload = 1024 * 1024
     },
+    /* POST /orders/$ID/paid: */
+    {
+      .url_prefix = "/orders/",
+      .have_id_segment = true,
+      .url_suffix = "paid",
+      .method = MHD_HTTP_METHOD_POST,
+      .handler = &TMH_post_orders_ID_paid,
+      /* the body should be pretty small, allow 1 MB of upload
+         to set a conservative bound for sane wallets */
+      .max_upload = 1024 * 1024
+    },
     /* GET /orders/$ID: */
     {
       .url_prefix = "/orders/",
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-paid.c 
b/src/backend/taler-merchant-httpd_post-orders-ID-paid.c
index 6e6f64a..623b276 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-paid.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-paid.c
@@ -46,15 +46,18 @@ TMH_post_orders_ID_paid (const struct TMH_RequestHandler 
*rh,
     .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK),
     .purpose.size = htonl (sizeof (pr))
   };
+  const char *order_id = hc->infix;
   struct TALER_MerchantSignatureP merchant_sig;
   const char *session_id;
+  json_t *contract_terms;
+  enum GNUNET_DB_QueryStatus qs;
 
   {
     struct GNUNET_JSON_Specification spec[] = {
-      GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
-                                   &pr.h_contract_terms),
-      GNUNET_JSON_spec_fixed_auto ("merchant_sig",
+      GNUNET_JSON_spec_fixed_auto ("sig",
                                    &merchant_sig),
+      GNUNET_JSON_spec_fixed_auto ("h_contract",
+                                   &pr.h_contract_terms),
       GNUNET_JSON_spec_string ("session_id",
                                &session_id),
       GNUNET_JSON_spec_end ()
@@ -73,7 +76,7 @@ TMH_post_orders_ID_paid (const struct TMH_RequestHandler *rh,
     }
   }
 
-#if FIXME
+
   if (GNUNET_OK !=
       GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_PAYMENT_OK,
                                   &pr,
@@ -86,31 +89,104 @@ TMH_post_orders_ID_paid (const struct TMH_RequestHandler 
*rh,
       MHD_HTTP_FORBIDDEN,
       "{s:s, s:I}",
       "hint", "deposit signature invalid",
-      "code", (json_int_t) TALER_EC_PAID_SIGNATURE_INVALID);
+      "code", (json_int_t) TALER_EC_PAID_COIN_SIGNATURE_INVALID);
   }
 
-  // FIXME: check that h_contract_terms matches
-  // this order-id (and that the order is known),
-  // and if it does, update 'session_id' (if non-NULL)
-  if (0)
+  TMH_db->preflight (TMH_db->cls);
+  {
+    uint64_t order_serial;
+    qs = TMH_db->lookup_contract_terms (TMH_db->cls,
+                                        hc->instance->settings.id,
+                                        order_id,
+                                        &contract_terms,
+                                        &order_serial);
+  }
+  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_PAID_DB_ERROR,
+                                       "database error looking up contract");
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Unknown order id given: `%s'\n",
+                order_id);
     return TALER_MHD_reply_json_pack (
       connection,
       MHD_HTTP_NOT_FOUND,
       "{s:s, s:I}",
-      "hint", "order unknwown",
+      "hint", "order unknown",
       "code", (json_int_t) TALER_EC_PAID_ORDER_UNKNOWN);
   }
-  if (0)
+
   {
-    return TALER_MHD_reply_json_pack (
-      connection,
-      MHD_HTTP_CONFLICT,
-      "{s:s, s:I}",
-      "hint", "contract hash does not match this order",
-      "code", (json_int_t) TALER_EC_PAID_CONTRACT_HASH_MISMATCH);
+    struct GNUNET_HashCode h_contract_terms;
+
+    if (GNUNET_OK !=
+        TALER_JSON_contract_hash (contract_terms,
+                                  &h_contract_terms))
+    {
+      GNUNET_break (0);
+      json_decref (contract_terms);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         TALER_EC_INTERNAL_LOGIC_ERROR,
+                                         "Could not hash contract terms");
+    }
+    if (0 != GNUNET_memcmp (&pr.h_contract_terms,
+                            &h_contract_terms))
+    {
+      json_decref (contract_terms);
+      return TALER_MHD_reply_json_pack (
+        connection,
+        MHD_HTTP_CONFLICT,
+        "{s:s, s:I}",
+        "hint", "contract hash does not match this order",
+        "code", (json_int_t) TALER_EC_PAID_CONTRACT_HASH_MISMATCH);
+    }
+  }
+  if (NULL != session_id)
+  {
+    if (GNUNET_OK !=
+        TMH_db->start (TMH_db->cls,
+                       "post /paid"))
+    {
+      json_decref (contract_terms);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         TALER_EC_PAID_DB_ERROR,
+                                         "failed to start database 
transaction");
+    }
+    qs = TMH_db->mark_contract_paid (TMH_db->cls,
+                                     hc->instance->settings.id,
+                                     &pr.h_contract_terms,
+                                     session_id);
+    /* Since the order was paid already, we get qs == 0. */
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+    {
+      qs = TMH_db->commit (TMH_db->cls);
+      if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+        qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+    }
+    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+    {
+      TMH_db->rollback (TMH_db->cls);
+      json_decref (contract_terms);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         TALER_EC_PAID_DB_ERROR,
+                                         "failed to update contract's session 
id");
+    }
   }
-#endif
+  json_decref (contract_terms);
+
   return TALER_MHD_reply_static (connection,
                                  MHD_HTTP_NO_CONTENT,
                                  NULL,
diff --git a/src/include/taler_merchant_testing_lib.h 
b/src/include/taler_merchant_testing_lib.h
index b91b1f7..a22d361 100644
--- a/src/include/taler_merchant_testing_lib.h
+++ b/src/include/taler_merchant_testing_lib.h
@@ -735,6 +735,25 @@ TALER_TESTING_cmd_merchant_pay_order (const char *label,
                                       const char *amount_without_fee,
                                       const char *session_id);
 
+
+/**
+ * Make an "order paid" test command.
+ *
+ * @param label command label
+ * @param merchant_url merchant base URL
+ * @param pay_reference reference to the payment to verify
+ * @param session_id the session to use for the verification.
+ * @param http_status expected HTTP response code
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_merchant_post_orders_paid (const char *label,
+                                             const char *merchant_url,
+                                             const char *pay_reference,
+                                             const char *session_id,
+                                             unsigned int http_status);
+
+
 /**
  * Make an "abort" test command.
  *
diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am
index bb76eac..769abd9 100644
--- a/src/testing/Makefile.am
+++ b/src/testing/Makefile.am
@@ -36,6 +36,7 @@ libtalermerchanttesting_la_SOURCES = \
   testing_api_cmd_merchant_get_tip.c \
   testing_api_cmd_pay_order.c \
   testing_api_cmd_post_instances.c \
+  testing_api_cmd_post_orders_paid.c \
   testing_api_cmd_post_orders.c \
   testing_api_cmd_post_products.c \
   testing_api_cmd_post_reserves.c \
diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c
index 268f414..9765b89 100644
--- a/src/testing/test_merchant_api.c
+++ b/src/testing/test_merchant_api.c
@@ -333,6 +333,11 @@ run (void *cls,
     TALER_TESTING_cmd_poll_order_conclude ("poll-order-merchant-1-conclude",
                                            MHD_HTTP_OK,
                                            "poll-order-merchant-1-start"),
+    TALER_TESTING_cmd_merchant_post_orders_paid ("verify-order-1-paid",
+                                                 merchant_url,
+                                                 "deposit-simple",
+                                                 "session-1",
+                                                 MHD_HTTP_NO_CONTENT),
     TALER_TESTING_cmd_wallet_get_order ("get-order-wallet-1-2",
                                         merchant_url,
                                         "create-proposal-1",
diff --git a/src/testing/testing_api_cmd_post_orders_paid.c 
b/src/testing/testing_api_cmd_post_orders_paid.c
new file mode 100644
index 0000000..cd06542
--- /dev/null
+++ b/src/testing/testing_api_cmd_post_orders_paid.c
@@ -0,0 +1,258 @@
+/*
+  This file is part of TALER
+  Copyright (C) 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 Foundation; either version 3, or
+  (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file lib/testing_api_cmd_post_orders_paid.c
+ * @brief command to test POST /orders/$ID/paid.
+ * @author Jonathan Buchanan
+ */
+#include "platform.h"
+#include <taler/taler_exchange_service.h>
+#include <taler/taler_testing_lib.h>
+#include "taler_merchant_service.h"
+#include "taler_merchant_testing_lib.h"
+
+
+/**
+ * State of a "POST /orders/$ID/paid" CMD.
+ */
+struct PostOrdersPaidState
+{
+
+  /**
+   * Handle for a "POST /paid" request.
+   */
+  struct TALER_MERCHANT_OrderPaidHandle *oph;
+
+  /**
+   * The interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *is;
+
+  /**
+   * Base URL of the merchant serving the request.
+   */
+  const char *merchant_url;
+
+  /**
+   * Reference to the "pay" command to verify.
+   */
+  const char *pay_reference;
+
+  /**
+   * The session to use for the requet.
+   */
+  const char *session_id;
+
+  /**
+   * Expected HTTP response code.
+   */
+  unsigned int http_status;
+
+};
+
+
+/**
+ * Response from the merchant after POST /paid.
+ *
+ * @param cls pointer to `struct PostOrdersPaidState`.
+ * @param hr the http response.
+ */
+static void
+paid_cb (void *cls,
+         const struct TALER_MERCHANT_HttpResponse *hr)
+{
+  struct PostOrdersPaidState *ops = cls;
+
+  ops->oph = NULL;
+  if (ops->http_status != hr->http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u (%d) to command %s\n",
+                hr->http_status,
+                (int) hr->ec,
+                TALER_TESTING_interpreter_get_current_label (ops->is));
+    TALER_TESTING_FAIL (ops->is);
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Successful order-paid (HTTP status: %u)\n",
+              ops->http_status);
+  TALER_TESTING_interpreter_next (ops->is);
+}
+
+
+/**
+ * Run a "paid" CMD.
+ *
+ * @param cls closure
+ * @param cmd command being run.
+ * @param is interpreter state
+ */
+static void
+paid_run (void *cls,
+          const struct TALER_TESTING_Command *cmd,
+          struct TALER_TESTING_Interpreter *is)
+{
+  struct PostOrdersPaidState *ops = cls;
+  const struct TALER_TESTING_Command *pay_cmd;
+  const char *proposal_reference;
+  const struct TALER_TESTING_Command *proposal_cmd;
+  const char *order_id;
+  const struct GNUNET_HashCode *h_contract_terms;
+  struct TALER_MerchantSignatureP *merchant_sig;
+
+  ops->is = is;
+
+  pay_cmd = TALER_TESTING_interpreter_lookup_command (is,
+                                                      ops->pay_reference);
+  if (NULL == pay_cmd)
+    TALER_TESTING_FAIL (is);
+  if (GNUNET_OK !=
+      TALER_TESTING_get_trait_merchant_sig (pay_cmd,
+                                            0,
+                                            &merchant_sig))
+    TALER_TESTING_FAIL (is);
+  if (GNUNET_OK !=
+      TALER_TESTING_get_trait_proposal_reference (pay_cmd,
+                                                  0,
+                                                  &proposal_reference))
+    TALER_TESTING_FAIL (is);
+  proposal_cmd = TALER_TESTING_interpreter_lookup_command (is,
+                                                           proposal_reference);
+
+  if (NULL == proposal_cmd)
+    TALER_TESTING_FAIL (is);
+
+  {
+    const json_t *contract_terms;
+    const char *error_name;
+    unsigned int error_line;
+
+    if (GNUNET_OK !=
+        TALER_TESTING_get_trait_contract_terms (proposal_cmd,
+                                                0,
+                                                &contract_terms))
+      TALER_TESTING_FAIL (is);
+    {
+      /* Get information that needs to be put verbatim in the
+       * deposit permission */
+      struct GNUNET_JSON_Specification spec[] = {
+        GNUNET_JSON_spec_string ("order_id",
+                                 &order_id),
+        GNUNET_JSON_spec_end ()
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (contract_terms,
+                             spec,
+                             &error_name,
+                             &error_line))
+      {
+        char *js;
+
+        js = json_dumps (contract_terms,
+                         JSON_INDENT (1));
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Parser failed on %s:%u for input `%s'\n",
+                    error_name,
+                    error_line,
+                    js);
+        free (js);
+        TALER_TESTING_FAIL (is);
+      }
+    }
+  }
+
+  if (GNUNET_OK !=
+      TALER_TESTING_get_trait_h_contract_terms (proposal_cmd,
+                                                0,
+                                                &h_contract_terms))
+    TALER_TESTING_FAIL (is);
+
+  ops->oph = TALER_MERCHANT_order_paid (is->ctx,
+                                        ops->merchant_url,
+                                        order_id,
+                                        ops->session_id,
+                                        h_contract_terms,
+                                        merchant_sig,
+                                        &paid_cb,
+                                        ops);
+  if (NULL == ops->oph)
+    TALER_TESTING_FAIL (is);
+}
+
+
+/**
+ * Free a "paid" CMD, and cancel it if need be.
+ *
+ * @param cls closure.
+ * @param cmd command currently being freed.
+ */
+static void
+paid_cleanup (void *cls,
+              const struct TALER_TESTING_Command *cmd)
+{
+  struct PostOrdersPaidState *ops = cls;
+
+  if (NULL != ops->oph)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Command `%s' did not complete.\n",
+                TALER_TESTING_interpreter_get_current_label (
+                  ops->is));
+    TALER_MERCHANT_order_paid_cancel (ops->oph);
+  }
+  GNUNET_free (ops);
+}
+
+
+/**
+ * Make an "order paid" test command.
+ *
+ * @param label command label
+ * @param merchant_url merchant base URL
+ * @param pay_reference reference to the payment to verify
+ * @param session_id the session to use for the verification.
+ * @param http_status expected HTTP response code
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_merchant_post_orders_paid (const char *label,
+                                             const char *merchant_url,
+                                             const char *pay_reference,
+                                             const char *session_id,
+                                             unsigned int http_status)
+{
+  struct PostOrdersPaidState *ops;
+
+  ops = GNUNET_new (struct PostOrdersPaidState);
+  ops->http_status = http_status;
+  ops->pay_reference = pay_reference;
+  ops->merchant_url = merchant_url;
+  ops->session_id = session_id;
+  {
+    struct TALER_TESTING_Command cmd = {
+      .cls = ops,
+      .label = label,
+      .run = &paid_run,
+      .cleanup = &paid_cleanup
+    };
+
+    return cmd;
+  }
+}

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