gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] branch master updated: early stages of implementing POS


From: gnunet
Subject: [taler-merchant] branch master updated: early stages of implementing POST /orders/$ORDER_ID/refund
Date: Sun, 16 Aug 2020 08:43:31 +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 84d79e5  early stages of implementing POST /orders/$ORDER_ID/refund
     new b3e2c2f  Merge branch 'master' of ssh://git.taler.net/merchant
84d79e5 is described below

commit 84d79e5c8eda85d4dc2af6528de19a35350ad60e
Author: Jonathan Buchanan <jonathan.russ.buchanan@gmail.com>
AuthorDate: Sun Aug 16 02:42:03 2020 -0400

    early stages of implementing POST /orders/$ORDER_ID/refund
---
 src/backend/Makefile.am                            |   2 +
 src/backend/taler-merchant-httpd.c                 |  12 +
 .../taler-merchant-httpd_post-orders-ID-refund.c   | 704 +++++++++++++++++++++
 .../taler-merchant-httpd_post-orders-ID-refund.h   |  40 ++
 4 files changed, 758 insertions(+)

diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 8e99993..af86034 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -87,6 +87,8 @@ taler_merchant_httpd_SOURCES = \
     taler-merchant-httpd_post-orders-ID-pay.h \
   taler-merchant-httpd_post-orders-ID-paid.c \
     taler-merchant-httpd_post-orders-ID-paid.h \
+  taler-merchant-httpd_post-orders-ID-refund.c \
+    taler-merchant-httpd_post-orders-ID-refund.h \
   taler-merchant-httpd_post-tips-ID-pickup.c \
     taler-merchant-httpd_post-tips-ID-pickup.h \
   taler-merchant-httpd_qr.c \
diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index cf149d2..f65b429 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -60,6 +60,7 @@
 #include "taler-merchant-httpd_post-orders-ID-claim.h"
 #include "taler-merchant-httpd_post-orders-ID-paid.h"
 #include "taler-merchant-httpd_post-orders-ID-pay.h"
+#include "taler-merchant-httpd_post-orders-ID-refund.h"
 #include "taler-merchant-httpd_post-tips-ID-pickup.h"
 #include "taler-merchant-httpd_reserves.h"
 #include "taler-merchant-httpd_templating.h"
@@ -1067,6 +1068,17 @@ url_handler (void *cls,
          to set a conservative bound for sane wallets */
       .max_upload = 1024 * 1024
     },
+    /* POST /orders/$ID/refund: */
+    {
+      .url_prefix = "/orders/",
+      .have_id_segment = true,
+      .url_suffix = "refund",
+      .method = MHD_HTTP_METHOD_POST,
+      .handler = &TMH_post_orders_ID_refund,
+      /* 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-refund.c 
b/src/backend/taler-merchant-httpd_post-orders-ID-refund.c
new file mode 100644
index 0000000..bfdb6ca
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-refund.c
@@ -0,0 +1,704 @@
+/*
+  This file is part of TALER
+  (C) 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
+  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 backend/taler-merchant-httpd_post-orders-ID-refund.c
+ * @brief handling of POST /orders/$ID/refund requests
+ * @author Jonathan Buchanan
+ */
+#include "platform.h"
+#include <taler/taler_signatures.h>
+#include <taler/taler_json_lib.h>
+#include <taler/taler_exchange_service.h>
+#include "taler-merchant-httpd_auditors.h"
+#include "taler-merchant-httpd_exchanges.h"
+#include "taler-merchant-httpd_post-orders-ID-refund.h"
+
+
+/**
+ * Information we keep for each coin to be refunded.
+ */
+struct CoinRefund
+{
+
+  /**
+   * Kept in a DLL.
+   */
+  struct CoinRefund *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct CoinRefund *prev;
+
+  /**
+   * Request to connect to the target exchange.
+   */
+  struct TMH_EXCHANGES_FindOperation *fo;
+
+  /**
+   * Handle for the refund operation with the exchange.
+   */
+  struct TALER_EXCHANGE_RefundHandle *rh;
+
+  /**
+   * Request this operation is part of.
+   */
+  struct PostRefundData *prd;
+
+  /**
+   * URL of the exchange for this @e coin_pub.
+   */
+  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;
+
+  /**
+   * When did the merchant grant the refund. To be used to group events
+   * in the wallet.
+   */
+  struct GNUNET_TIME_Absolute execution_time;
+
+  /**
+   * Coin to refund.
+   */
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+
+  /**
+   * Refund transaction ID to use.
+   */
+  uint64_t rtransaction_id;
+
+  /**
+   * Unique serial number identifying the refund.
+   */
+  uint64_t refund_serial;
+
+  /**
+   * Amount to refund.
+   */
+  struct TALER_Amount refund_amount;
+
+  /**
+   * Public key of the exchange affirming the refund.
+   */
+  struct TALER_ExchangePublicKeyP exchange_pub;
+
+  /**
+   * Signature of the exchange affirming the refund.
+   */
+  struct TALER_ExchangeSignatureP exchange_sig;
+
+  /**
+   * HTTP status from the exchange, #MHD_HTTP_OK if
+   * @a exchange_pub and @a exchange_sig are valid.
+   */
+  unsigned int exchange_status;
+
+  /**
+   * HTTP error code from the exchange.
+   */
+  enum TALER_ErrorCode exchange_code;
+
+};
+
+
+/**
+ * Context for the operation.
+ */
+struct PostRefundData
+{
+
+  /**
+   * Hashed version of contract terms. All zeros if
+   * not provided.
+   */
+  struct GNUNET_HashCode h_contract_terms;
+
+  /**
+   * DLL of (suspended) requests.
+   */
+  struct PostRefundData *next;
+
+  /**
+   * DLL of (suspended) requests.
+   */
+  struct PostRefundData *prev;
+
+  /**
+   * Refunds for this order. Head of DLL.
+   */
+  struct CoinRefund *cr_head;
+
+  /**
+   * Refunds for this order. Tail of DLL.
+   */
+  struct CoinRefund *cr_tail;
+
+  /**
+   * Context of the request.
+   */
+  struct TMH_HandlerContext *hc;
+
+  /**
+   * Entry in the #resume_timeout_heap for this check payment, if we are
+   * suspended.
+   */
+  struct TMH_SuspendedConnection sc;
+
+  /**
+   * Which merchant instance is this for?
+   */
+  struct MerchantInstance *mi;
+
+  /**
+   * order ID for the payment
+   */
+  const char *order_id;
+
+  /**
+   * Where to get the contract
+   */
+  const char *contract_url;
+
+  /**
+   * fulfillment URL of the contract (valid as long as
+   * @e contract_terms is valid).
+   */
+  const char *fulfillment_url;
+
+  /**
+   * session of the client
+   */
+  const char *session_id;
+
+  /**
+   * Contract terms of the payment we are checking. NULL when they
+   * are not (yet) known.
+   */
+  json_t *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?
+   */
+  bool suspended;
+
+  /**
+   * Return code: #TALER_EC_NONE if successful.
+   */
+  enum TALER_ErrorCode ec;
+
+  /**
+   * Set to true if we are dealing with an unclaimed order
+   * (and thus @e h_contract_terms is not set, and certain
+   * DB queries will not work).
+   */
+  bool unclaimed;
+
+  /**
+   * Set to true if this payment has been refunded and
+   * @e refund_amount is initialized.
+   */
+  bool refunded;
+
+  /**
+   * Set to true if a refund is still available for the
+   * wallet for this payment.
+   */
+  bool refund_available;
+
+  /**
+   * Set to true if the client requested HTML, otherwise
+   * we generate JSON.
+   */
+  bool generate_html;
+
+};
+
+
+/**
+ * Head of DLL of (suspended) requests.
+ */
+static struct PostRefundData *prd_head;
+
+/**
+ * Tail of DLL of (suspended) requests.
+ */
+static struct PostRefundData *prd_tail;
+
+
+/* FIXME: Handle shutdown and other events that require ending all requests */
+
+
+/**
+ * Check if @a prd has exchange requests still pending.
+ *
+ * @param prd state to check
+ * @return true if activities are still pending
+ */
+static bool
+exchange_operations_pending (struct PostRefundData *prd)
+{
+  for (struct CoinRefund *cr = prd->cr_head;
+       NULL != cr;
+       cr = cr->next)
+  {
+    if ( (NULL != cr->fo) ||
+         (NULL != cr->rh) )
+      return true;
+  }
+  return false;
+}
+
+
+/**
+ * Check if @a prd is ready to be resumed, and if so, do it.
+ *
+ * @param prd refund request to be possibly ready
+ */
+static void
+check_resume_prd (struct PostRefundData *prd)
+{
+  if (exchange_operations_pending (prd))
+    return;
+  GNUNET_CONTAINER_DLL_remove (prd_head,
+                               prd_tail,
+                               prd);
+  GNUNET_assert (prd->suspended);
+  prd->suspended = false;
+  MHD_resume_connection (prd->sc.con);
+  TMH_trigger_daemon ();
+}
+
+
+/**
+ * Callbacks of this type are used to serve the result of submitting a
+ * refund request to an exchange.
+ *
+ * @param cls a `struct CoinRefund`
+ * @param hr HTTP response data
+ * @param exchange_pub exchange key used to sign refund confirmation
+ * @param exchange_sig exchange's signature over refund
+ */
+static void
+refund_cb (void *cls,
+           const struct TALER_EXCHANGE_HttpResponse *hr,
+           const struct TALER_ExchangePublicKeyP *exchange_pub,
+           const struct TALER_ExchangeSignatureP *exchange_sig)
+{
+  struct CoinRefund *cr = cls;
+
+  cr->rh = NULL;
+  cr->exchange_status = hr->http_status;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Exchange refund status for coin %s is %u\n",
+              TALER_B2S (&cr->coin_pub),
+              hr->http_status);
+  if (MHD_HTTP_OK != hr->http_status)
+  {
+    cr->exchange_code = hr->ec;
+    cr->exchange_reply = json_incref ((json_t*) hr->reply);
+  }
+  else
+  {
+    enum GNUNET_DB_QueryStatus qs;
+
+    cr->exchange_pub = *exchange_pub;
+    cr->exchange_sig = *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
+         least log this. */
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Failed to persist exchange response to /refund in database: 
%d\n",
+                  qs);
+    }
+  }
+  check_resume_prd (cr->prd);
+}
+
+
+/**
+ * Function called with the result of a #TMH_EXCHANGES_find_exchange()
+ * operation.
+ *
+ * @param cls a `struct CoinRefund *`
+ * @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 CoinRefund *cr = cls;
+
+  (void) payto_uri;
+  cr->fo = NULL;
+  if (TALER_EC_NONE == hr->ec)
+  {
+    cr->rh = TALER_EXCHANGE_refund (eh,
+                                    &cr->refund_amount,
+                                    &cr->prd->h_contract_terms,
+                                    &cr->coin_pub,
+                                    cr->rtransaction_id,
+                                    &cr->prd->hc->instance->merchant_priv,
+                                    &refund_cb,
+                                    cr);
+    return;
+  }
+  cr->exchange_status = hr->http_status;
+  cr->exchange_code = hr->ec;
+  cr->exchange_reply = json_incref ((json_t*) hr->reply);
+  check_resume_prd (cr->prd);
+}
+
+
+/**
+ * Function called with information about a refund.
+ * It is responsible for summing up the refund amount.
+ *
+ * @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 timestamp when was the refund made
+ * @param refund_amount refund amount which is being taken from @a coin_pub
+ * @param pending true if the this refund was not yet processed by the 
wallet/exchange
+ */
+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,
+                    bool pending)
+{
+  struct PostRefundData *prd = cls;
+  struct CoinRefund *cr;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Found refund of %s for coin %s with reason `%s' in database\n",
+              TALER_amount2s (refund_amount),
+              TALER_B2S (coin_pub),
+              reason);
+  cr = GNUNET_new (struct CoinRefund);
+  cr->refund_serial = refund_serial;
+  cr->exchange_url = GNUNET_strdup (exchange_url);
+  cr->prd = prd;
+  cr->coin_pub = *coin_pub;
+  cr->rtransaction_id = rtransaction_id;
+  cr->refund_amount = *refund_amount;
+  cr->execution_time = timestamp;
+  GNUNET_CONTAINER_DLL_insert (prd->cr_head,
+                               prd->cr_tail,
+                               cr);
+  if (prd->refunded)
+  {
+    GNUNET_assert (0 <=
+                   TALER_amount_add (&prd->refund_amount,
+                                     &prd->refund_amount,
+                                     refund_amount));
+    return;
+  }
+  prd->refund_amount = *refund_amount;
+  prd->refunded = true;
+  prd->refund_available |= pending;
+}
+
+
+/**
+ * Obtain refunds for an order.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] hc context with further information about the request
+ * @return MHD result code
+ */
+MHD_RESULT
+TMH_post_orders_ID_refund (const struct TMH_RequestHandler *rh,
+                           struct MHD_Connection *connection,
+                           struct TMH_HandlerContext *hc)
+{
+  struct PostRefundData *prd = hc->ctx;
+  enum GNUNET_DB_QueryStatus qs;
+
+  if (NULL == prd)
+  {
+    prd = GNUNET_new (struct PostRefundData);
+    prd->sc.con = connection;
+    prd->hc = hc;
+    prd->order_id = hc->infix;
+    {
+      enum GNUNET_GenericReturnValue res;
+
+      struct GNUNET_JSON_Specification spec[] = {
+        GNUNET_JSON_spec_fixed_auto ("h_contract", &prd->h_contract_terms),
+        GNUNET_JSON_spec_end ()
+      };
+      res = TALER_MHD_parse_json_data (connection,
+                                       hc->request_body,
+                                       spec);
+      if (GNUNET_OK != res)
+        return (GNUNET_NO == res)
+               ? MHD_YES
+               : MHD_NO;
+    }
+
+    TMH_db->preflight (TMH_db->cls);
+    {
+      json_t *contract_terms;
+      uint64_t order_serial;
+      qs = TMH_db->lookup_contract_terms (TMH_db->cls,
+                                          hc->instance->settings.id,
+                                          hc->infix,
+                                          &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_GET_ORDERS_DB_FETCH_CONTRACT_TERMS_ERROR,
+                                           "db error fetching contract terms");
+      }
+      if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+      {
+        json_decref (contract_terms);
+        return TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_NOT_FOUND,
+                                           TALER_EC_GET_ORDERS_ORDER_NOT_FOUND,
+                                           "Did not find contract terms for 
order in DB");
+      }
+      {
+        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_GET_ORDERS_FAILED_COMPUTE_PROPOSAL_HASH,
+                                             "Failed to hash contract terms");
+        }
+        json_decref (contract_terms);
+        if (0 != GNUNET_memcmp (&h_contract_terms,
+                                &prd->h_contract_terms))
+        {
+          return TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_FORBIDDEN,
+                                             
TALER_EC_GET_ORDERS_FAILED_COMPUTE_PROPOSAL_HASH,
+                                             "");
+        }
+      }
+    }
+  }
+  {
+    GNUNET_assert (GNUNET_OK == TALER_amount_get_zero (TMH_currency,
+                                                       &prd->refund_amount));
+    qs = TMH_db->lookup_refunds_detailed (TMH_db->cls,
+                                          hc->instance->settings.id,
+                                          &prd->h_contract_terms,
+                                          &process_refunds_cb,
+                                          prd);
+    if (0 > qs)
+    {
+      GNUNET_break (0);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         TALER_EC_GET_ORDERS_DB_LOOKUP_ERROR,
+                                         "Failed to lookup refunds for 
contract");
+    }
+  }
+
+  /* Now launch exchange interactions, unless we already have the
+     response in the database! */
+  for (struct CoinRefund *cr = prd->cr_head;
+       NULL != cr;
+       cr = cr->next)
+  {
+    enum GNUNET_DB_QueryStatus 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 */
+      /* FIXME: notify clients polling for this to happen */
+      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;
+    }
+  }
+
+  /* Check if there are still exchange operations pending */
+  if (exchange_operations_pending (prd))
+  {
+    if (! prd->suspended)
+    {
+      prd->suspended = true;
+      MHD_suspend_connection (connection);
+      GNUNET_CONTAINER_DLL_insert (prd_head,
+                                   prd_tail,
+                                   prd);
+    }
+    return MHD_YES;   /* we're still talking to the exchange */
+  }
+
+  {
+    json_t *ra;
+
+    ra = json_array ();
+    GNUNET_assert (NULL != ra);
+    for (struct CoinRefund *cr = prd->cr_head;
+         NULL != cr;
+         cr = cr->next)
+    {
+      json_t *refund;
+
+      if (MHD_HTTP_OK != cr->exchange_status)
+      {
+        if (NULL == cr->exchange_reply)
+        {
+          refund = json_pack ("{s:s, s:I,s:I,s:o,s:o,s:o}"
+                              "type",
+                              "failure",
+                              "exchange_status",
+                              (json_int_t) cr->exchange_status,
+                              "rtransaction_id",
+                              (json_int_t) cr->rtransaction_id,
+                              "coin_pub",
+                              GNUNET_JSON_from_data_auto (&cr->coin_pub),
+                              "refund_amount",
+                              TALER_JSON_from_amount (&cr->refund_amount),
+                              "execution_time",
+                              GNUNET_JSON_from_time_abs (cr->execution_time));
+        }
+        else
+        {
+          refund = json_pack ("{s:s,s:I,s:I,s:O,s:I,s:o,s:o,s:o}"
+                              "type",
+                              "failure",
+                              "exchange_status",
+                              (json_int_t) cr->exchange_status,
+                              "exchange_code",
+                              (json_int_t) cr->exchange_code,
+                              "exchange_reply",
+                              cr->exchange_reply,
+                              "rtransaction_id",
+                              (json_int_t) cr->rtransaction_id,
+                              "coin_pub",
+                              GNUNET_JSON_from_data_auto (&cr->coin_pub),
+                              "refund_amount",
+                              TALER_JSON_from_amount (&cr->refund_amount),
+                              "execution_time",
+                              GNUNET_JSON_from_time_abs (cr->execution_time));
+        }
+      }
+      else
+      {
+        refund = json_pack ("{s:s,s:I,s:o,s:o,s:I,s:o,s:o,s:o}",
+                            "type",
+                            "success",
+                            "exchange_status",
+                            (json_int_t) cr->exchange_status,
+                            "exchange_sig",
+                            GNUNET_JSON_from_data_auto (&cr->exchange_sig),
+                            "exchange_pub",
+                            GNUNET_JSON_from_data_auto (&cr->exchange_pub),
+                            "rtransaction_id",
+                            (json_int_t) cr->rtransaction_id,
+                            "coin_pub",
+                            GNUNET_JSON_from_data_auto (&cr->coin_pub),
+                            "refund_amount",
+                            TALER_JSON_from_amount (&cr->refund_amount),
+                            "execution_time",
+                            GNUNET_JSON_from_time_abs (cr->execution_time));
+      }
+      GNUNET_assert (
+        0 ==
+        json_array_append_new (ra,
+                               refund));
+    }
+
+    return TALER_MHD_reply_json_pack (
+      connection,
+      MHD_HTTP_OK,
+      "{s:o, s:o, s:o}",
+      "refund_amount",
+      TALER_JSON_from_amount (&prd->refund_amount),
+      "refunds",
+      ra,
+      "merchant_pub",
+      GNUNET_JSON_from_data_auto (&hc->instance->merchant_pub));
+  }
+
+  return MHD_YES;
+}
+
+
+/* end of taler-merchant-httpd_post-orders-ID-refund.c */
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-refund.h 
b/src/backend/taler-merchant-httpd_post-orders-ID-refund.h
new file mode 100644
index 0000000..8bf0a6e
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-refund.h
@@ -0,0 +1,40 @@
+/*
+  This file is part of TALER
+  (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 backend/taler-merchant-httpd_post-orders-ID-refund.h
+ * @brief headers for POST /orders/$ID/refund handler
+ * @author Jonathan Buchanan
+ */
+#ifndef TALER_EXCHANGE_HTTPD_POST_ORDERS_ID_REFUND_H
+#define TALER_EXCHANGE_HTTPD_POST_ORDERS_ID_REFUND_H
+#include <microhttpd.h>
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Obtain refunds for an order.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] hc context with further information about the request
+ * @return MHD result code
+ */
+MHD_RESULT
+TMH_post_orders_ID_refund (const struct TMH_RequestHandler *rh,
+                           struct MHD_Connection *connection,
+                           struct TMH_HandlerContext *hc);
+
+#endif

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