gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] 73/277: DB API for /abort


From: gnunet
Subject: [taler-merchant] 73/277: DB API for /abort
Date: Sun, 05 Jul 2020 20:49:46 +0200

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

grothoff pushed a commit to branch master
in repository merchant.

commit d5a57b88a7ccbfdfe49378410417d170eff2f14f
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Fri May 1 16:12:51 2020 +0200

    DB API for /abort
---
 .../taler-merchant-httpd_post-orders-ID-abort.c    | 259 ++++++++++-----------
 src/backenddb/plugin_merchantdb_postgres.c         | 133 ++++++++++-
 src/include/taler_merchantdb_plugin.h              |  40 ++++
 3 files changed, 286 insertions(+), 146 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c 
b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
index 27a8381..e6e8e40 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
@@ -704,36 +704,66 @@ begin_transaction (struct AbortContext *ac)
     return;
   }
 
-  /* check payment was indeed incomplete */
-  qs = TMH_db->lookup_paid_order (TMH_db->cls,
-                                  ac->hc->instance->settings.id,
-                                  &ac->h_contract_terms,
-                                  NULL);
-  if (0 < qs)
+  /* check payment was indeed incomplete
+     (now that we are in the transaction scope!) */
   {
-    /* Payment is complete, refuse to abort. */
-    TMH_db->rollback (TMH_db->cls);
-    resume_abort_with_error (ac,
-                             MHD_HTTP_FORBIDDEN,
-                             
TALER_EC_ABORT_ABORT_REFUND_REFUSED_ABORTMENT_COMPLETE,
-                             "Payment was complete, refusing to abort");
-    return;
-  }
-  if (0 > qs)
-  {
-    TMH_db->rollback (TMH_db->cls);
-    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+    struct GNUNET_HashCode h_contract_terms;
+    bool paid;
+
+    qs = TMH_db->lookup_order_status (TMH_db->cls,
+                                      ac->hc->instance->settings.id,
+                                      ac->hc->infix,
+                                      &h_contract_terms,
+                                      &paid);
+    switch (qs)
     {
-      begin_transaction (ac);
+    case GNUNET_DB_STATUS_SOFT_ERROR:
+    case GNUNET_DB_STATUS_HARD_ERROR:
+      /* Always report on hard error to enable diagnostics */
+      GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+      TMH_db->rollback (TMH_db->cls);
+      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      {
+        begin_transaction (ac);
+        return;
+      }
+      /* Always report on hard error as well to enable diagnostics */
+      GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+      resume_abort_with_error (ac,
+                               MHD_HTTP_INTERNAL_SERVER_ERROR,
+                               TALER_EC_ABORT_DB_FETCH_TRANSACTION_ERROR,
+                               "Merchant database error");
+      return;
+    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+      TMH_db->rollback (TMH_db->cls);
+      resume_abort_with_error (ac,
+                               MHD_HTTP_NOT_FOUND,
+                               TALER_EC_ABORT_CONTRACT_NOT_FOUND,
+                               "Could not find contract");
+      return;
+    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+      if (paid)
+      {
+        /* Payment is complete, refuse to abort. */
+        TMH_db->rollback (TMH_db->cls);
+        resume_abort_with_error (ac,
+                                 MHD_HTTP_FORBIDDEN,
+                                 
TALER_EC_ABORT_REFUND_REFUSED_PAYMENT_COMPLETE,
+                                 "Payment was complete, refusing to abort");
+        return;
+      }
+    }
+    if (0 !=
+        GNUNET_memcmp (&ac->h_contract_terms,
+                       &h_contract_terms))
+    {
+      GNUNET_break_op (0);
+      resume_abort_with_error (ac,
+                               MHD_HTTP_BAD_REQUEST,
+                               TALER_EC_ABORT_CONTRACT_HASH_MISSMATCH,
+                               "Provided hash does not match order on file");
       return;
     }
-    /* Always report on hard error as well to enable diagnostics */
-    GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-    resume_abort_with_error (ac,
-                             MHD_HTTP_INTERNAL_SERVER_ERROR,
-                             TALER_EC_ABORT_DB_STORE_ABORT_ERROR,
-                             "Merchant database error");
-    return;
   }
 
   /* Mark all deposits we have in our database for the order as refunded. */
@@ -802,132 +832,77 @@ parse_abort (struct MHD_Connection *connection,
              struct TMH_HandlerContext *hc,
              struct AbortContext *ac)
 {
+  json_t *coins;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_json ("coins",
+                           &coins),
+    GNUNET_JSON_spec_fixed_auto ("h_contract",
+                                 &ac->h_contract_terms),
+
+    GNUNET_JSON_spec_end ()
+  };
+  enum GNUNET_GenericReturnValue res;
+
+  res = TALER_MHD_parse_json_data (connection,
+                                   hc->request_body,
+                                   spec);
+  if (GNUNET_YES != res)
+  {
+    GNUNET_break_op (0);
+    return res;
+  }
+  ac->coins_cnt = json_array_size (coins);
+  if (0 == ac->coins_cnt)
   {
-    json_t *coins;
-    struct GNUNET_JSON_Specification spec[] = {
-      GNUNET_JSON_spec_json ("coins",
-                             &coins),
-      GNUNET_JSON_spec_fixed_auto ("h_contract",
-                                   &ac->h_contract_terms),
-
-      GNUNET_JSON_spec_end ()
-    };
-    enum GNUNET_GenericReturnValue res;
-
-    res = TALER_MHD_parse_json_data (connection,
-                                     hc->request_body,
-                                     spec);
-    if (GNUNET_YES != res)
-    {
-      GNUNET_break_op (0);
-      return res;
-    }
-    ac->coins_cnt = json_array_size (coins);
-    if (0 == ac->coins_cnt)
-    {
-      GNUNET_JSON_parse_free (spec);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_BAD_REQUEST,
-                                         TALER_EC_ABORT_COINS_ARRAY_EMPTY,
-                                         "coins");
-    }
-    /* note: 1 coin = 1 deposit confirmation expected */
-    ac->pending = ac->coins_cnt;
-    ac->rd = GNUNET_new_array (ac->coins_cnt,
-                               struct RefundDetails);
-    /* This loop populates the array 'rd' in 'ac' */
-    {
-      unsigned int coins_index;
-      json_t *coin;
-      json_array_foreach (coins, coins_index, coin)
-      {
-        struct RefundDetails *rd = &ac->rd[coins_index];
-        const char *exchange_url;
-        struct GNUNET_JSON_Specification ispec[] = {
-          TALER_JSON_spec_amount ("contribution",
-                                  &rd->amount_with_fee),
-          TALER_JSON_spec_amount ("refund_fee",
-                                  &rd->refund_fee),
-          GNUNET_JSON_spec_string ("exchange_url",
-                                   &exchange_url),
-          GNUNET_JSON_spec_fixed_auto ("coin_pub",
-                                       &rd->coin_pub),
-          GNUNET_JSON_spec_end ()
-        };
-
-        res = TALER_MHD_parse_json_data (connection,
-                                         coin,
-                                         ispec);
-        if (GNUNET_YES != res)
-        {
-          GNUNET_JSON_parse_free (spec);
-          GNUNET_break_op (0);
-          return res;
-        }
-        rd->exchange_url = GNUNET_strdup (exchange_url);
-        rd->index = coins_index;
-        rd->ac = ac;
-      }
-    }
     GNUNET_JSON_parse_free (spec);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       TALER_EC_ABORT_COINS_ARRAY_EMPTY,
+                                       "coins");
   }
-
-  /* Check request against contract on file */
+  /* note: 1 coin = 1 deposit confirmation expected */
+  ac->pending = ac->coins_cnt;
+  ac->rd = GNUNET_new_array (ac->coins_cnt,
+                             struct RefundDetails);
+  /* This loop populates the array 'rd' in 'ac' */
   {
-    enum GNUNET_DB_QueryStatus qs;
-    struct GNUNET_HashCode h_contract_terms;
-
-    qs = TMH_db->lookup_contract_terms_hash (TMH_db->cls,
-                                             hc->instance->settings.id,
-                                             hc->infix,
-                                             &h_contract_terms);
-    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 to enable diagnostics */
-      GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-      return (MHD_YES ==
-              TALER_MHD_reply_with_error (connection,
-                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                          TALER_EC_ABORT_DB_FETCH_ABORT_ERROR,
-                                          "Failed to obtain contract terms 
from DB"))
-             ? GNUNET_NO
-             : GNUNET_SYSERR;
-    }
-    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-    {
-      return (MHD_YES ==
-              TALER_MHD_reply_with_error (connection,
-                                          MHD_HTTP_NOT_FOUND,
-                                          TALER_EC_ABORT_PROPOSAL_NOT_FOUND,
-                                          "Order not found"))
-             ? GNUNET_NO
-             : GNUNET_SYSERR;
-    }
-
-    /* check client provided the right hash and is thus authorized to request 
aborting */
+    unsigned int coins_index;
+    json_t *coin;
+    json_array_foreach (coins, coins_index, coin)
     {
-      if (0 !=
-          GNUNET_memcmp (&ac->h_contract_terms,
-                         &h_contract_terms))
+      struct RefundDetails *rd = &ac->rd[coins_index];
+      const char *exchange_url;
+      struct GNUNET_JSON_Specification ispec[] = {
+        TALER_JSON_spec_amount ("contribution",
+                                &rd->amount_with_fee),
+        TALER_JSON_spec_amount ("refund_fee",
+                                &rd->refund_fee),
+        GNUNET_JSON_spec_string ("exchange_url",
+                                 &exchange_url),
+        GNUNET_JSON_spec_fixed_auto ("coin_pub",
+                                     &rd->coin_pub),
+        GNUNET_JSON_spec_end ()
+      };
+
+      res = TALER_MHD_parse_json_data (connection,
+                                       coin,
+                                       ispec);
+      if (GNUNET_YES != res)
       {
+        GNUNET_JSON_parse_free (spec);
         GNUNET_break_op (0);
-        return (MHD_YES ==
-                TALER_MHD_reply_with_error (connection,
-                                            MHD_HTTP_BAD_REQUEST,
-                                            
TALER_EC_ABORT_CONTRACT_HASH_MISSMATCH,
-                                            "Provided hash does not match 
order on file"))
-               ? GNUNET_NO
-               : GNUNET_SYSERR;
+        return res;
       }
+      rd->exchange_url = GNUNET_strdup (exchange_url);
+      rd->index = coins_index;
+      rd->ac = ac;
     }
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Handling /abort for order `%s' with contract hash `%s'\n",
-                ac->hc->infix,
-                GNUNET_h2s (&ac->h_contract_terms));
   }
+  GNUNET_JSON_parse_free (spec);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Handling /abort for order `%s' with contract hash `%s'\n",
+              ac->hc->infix,
+              GNUNET_h2s (&ac->h_contract_terms));
   return GNUNET_OK;
 }
 
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index 46929c0..14fb9b2 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -1095,7 +1095,7 @@ postgres_lookup_order (void *cls,
               instance_id);
   check_connection (pg);
   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                 "find_order",
+                                                 "lookup_order",
                                                  params,
                                                  rs);
   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
@@ -1551,7 +1551,7 @@ lookup_deposits_cb (void *cls,
     struct TALER_Amount deposit_fee;
     struct TALER_Amount refund_fee;
     struct TALER_Amount wire_fee;
-    const char *exchange_url;
+    char *exchange_url;
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_string ("exchange_url",
                                     &exchange_url),
@@ -1585,7 +1585,7 @@ lookup_deposits_cb (void *cls,
              &deposit_fee,
              &refund_fee,
              &wire_fee);
-    GNUNET_PQ_cleanup_result (rs); /* technically useless here */
+    GNUNET_PQ_cleanup_result (rs);
   }
 }
 
@@ -1897,6 +1897,88 @@ postgres_mark_contract_paid (void *cls,
 }
 
 
+/**
+ * Function called during aborts to refund a coin. Marks the
+ * respective coin as refunded.
+ *
+ * @param cls closure
+ * @param instance_id instance to refund payment for
+ * @param h_contract_terms hash of the contract to refund coin for
+ * @param coin_pub public key of the coin to refund (fully)
+ * @param reason text justifying the refund
+ * @return transaction status
+ *        #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a coin_pub is unknown to us;
+ *        #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the request is valid,
+ *        regardless of whether it actually increased the refund
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_refund_coin (void *cls,
+                      const char *instance_id,
+                      const struct GNUNET_HashCode *h_contract_terms,
+                      const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                      const char *reason)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
+    GNUNET_PQ_query_param_auto_from_type (coin_pub),
+    GNUNET_PQ_query_param_string (reason),
+    GNUNET_PQ_query_param_end
+  };
+
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "refund_coin",
+                                             params);
+}
+
+
+/**
+ * Retrieve contract terms given its @a order_id
+ *
+ * @param cls closure
+ * @param instance_id instance's identifier
+ * @param order_id order to lookup contract for
+ * @param[out] h_contract_terms set to the hash of the contract.
+ * @param[out] paid set to the payment status of the contract
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_lookup_order_status (void *cls,
+                              const char *instance_id,
+                              const char *order_id,
+                              struct GNUNET_HashCode *h_contract_terms,
+                              bool *paid)
+{
+  struct PostgresClosure *pg = cls;
+  uint8_t paid8;
+  enum GNUNET_DB_QueryStatus qs;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (order_id),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+                                          h_contract_terms),
+    GNUNET_PQ_result_spec_auto_from_type ("paid",
+                                          &paid8),
+    GNUNET_PQ_result_spec_end
+  };
+
+  check_connection (pg);
+  qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                 "lookup_order_status",
+                                                 params,
+                                                 rs);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+    *paid = (0 != paid8);
+  else
+    *paid = false; /* just to be safe(r) */
+  return qs;
+}
+
+
 /* ********************* OLD API ************************** */
 
 /**
@@ -5404,6 +5486,48 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
                             "        FROM merchant_instances"
                             "       WHERE merchant_id=$1)",
                             3),
+    /* for postgres_refund_coin() */
+    GNUNET_PQ_make_prepare ("refund_coin",
+                            "INSERT INTO merchant_refunds"
+                            "(order_serial"
+                            ",rtransaction_id"
+                            ",coin_pub"
+                            ",reason"
+                            ",refund_amount_val"
+                            ",refund_amount_frac"
+                            ") "
+                            "SELECT "
+                            " order_serial"
+                            ",0" /* rtransaction_id always 0 for /abort */
+                            ",coin_pub"
+                            ",$4"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            " FROM merchant_deposits"
+                            " WHERE coin_pub=$3"
+                            "   AND order_serial="
+                            "  (SELECT order_serial"
+                            "     FROM merchant_contract_terms"
+                            "    WHERE h_contract_terms=$2"
+                            "      AND merchant_serial="
+                            "        (SELECT merchant_serial"
+                            "           FROM merchant_instances"
+                            "          WHERE merchant_id=$1))",
+                            4),
+
+    /* for postgres_lookup_order_status() */
+    GNUNET_PQ_make_prepare ("lookup_order_status",
+                            "SELECT"
+                            " h_contract_terms"
+                            ",paid"
+                            " FROM merchant_contract_terms"
+                            " WHERE merchant_contract_terms.merchant_serial="
+                            "     (SELECT merchant_serial "
+                            "        FROM merchant_instances"
+                            "        WHERE merchant_id=$1)"
+                            "   AND order_id=$2",
+                            2),
+
     /* OLD API: */
 
 #if 0
@@ -5800,7 +5924,8 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
   plugin->insert_deposit = &postgres_insert_deposit;
   plugin->lookup_refunds = &postgres_lookup_refunds;
   plugin->mark_contract_paid = &postgres_mark_contract_paid;
-
+  plugin->refund_coin = &postgres_refund_coin;
+  plugin->lookup_order_status = &postgres_lookup_order_status;
   /* OLD API: */
   plugin->find_contract_terms_from_hash =
     &postgres_find_contract_terms_from_hash;
diff --git a/src/include/taler_merchantdb_plugin.h 
b/src/include/taler_merchantdb_plugin.h
index f995d98..bca1071 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -997,6 +997,46 @@ struct TALER_MERCHANTDB_Plugin
                         const char *session_id);
 
 
+  /**
+   * Function called during aborts to refund a coin. Marks the
+   * respective coin as refunded.
+   *
+   * @param cls closure
+   * @param instance_id instance to refund payment for
+   * @param h_contract_terms hash of the contract to refund coin for
+   * @param coin_pub public key of the coin to refund (fully)
+   * @param reason text justifying the refund
+   * @return transaction status
+   *        #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a coin_pub is unknown to 
us;
+   *        #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the request is valid,
+   *        regardless of whether it actually increased the refund
+   */
+  enum GNUNET_DB_QueryStatus
+  (*refund_coin)(void *cls,
+                 const char *instance_id,
+                 const struct GNUNET_HashCode *h_contract_terms,
+                 const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                 const char *reason);
+
+
+  /**
+   * Retrieve contract terms given its @a order_id
+   *
+   * @param cls closure
+   * @param instance_id instance's identifier
+   * @param order_id order to lookup contract for
+   * @param[out] h_contract_terms set to the hash of the contract.
+   * @param[out] paid set to the payment status of the contract
+   * @return transaction status
+   */
+  enum GNUNET_DB_QueryStatus
+  (*lookup_order_status)(void *cls,
+                         const char *instance_id,
+                         const char *order_id,
+                         struct GNUNET_HashCode *h_contract_terms,
+                         bool *paid);
+
+
   /* ****************** OLD API ******************** */
 
 

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