LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_post-orders-ID-refund.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 3 190 1.6 %
Date: 2022-06-30 06:15:34 Functions: 1 9 11.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (C) 2020-2022 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify
       6             :   it under the terms of the GNU Affero General Public License as
       7             :   published by the Free Software Foundation; either version 3,
       8             :   or (at your option) any later version.
       9             : 
      10             :   TALER is distributed in the hope that it will be useful, but
      11             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :   GNU General Public License for more details.
      14             : 
      15             :   You should have received a copy of the GNU General Public
      16             :   License along with TALER; see the file COPYING.  If not,
      17             :   see <http://www.gnu.org/licenses/>
      18             : */
      19             : 
      20             : /**
      21             :  * @file taler-merchant-httpd_post-orders-ID-refund.c
      22             :  * @brief handling of POST /orders/$ID/refund requests
      23             :  * @author Jonathan Buchanan
      24             :  */
      25             : #include "platform.h"
      26             : #include <taler/taler_dbevents.h>
      27             : #include <taler/taler_signatures.h>
      28             : #include <taler/taler_json_lib.h>
      29             : #include <taler/taler_exchange_service.h>
      30             : #include "taler-merchant-httpd.h"
      31             : #include "taler-merchant-httpd_auditors.h"
      32             : #include "taler-merchant-httpd_exchanges.h"
      33             : #include "taler-merchant-httpd_post-orders-ID-refund.h"
      34             : 
      35             : 
      36             : /**
      37             :  * Information we keep for each coin to be refunded.
      38             :  */
      39             : struct CoinRefund
      40             : {
      41             : 
      42             :   /**
      43             :    * Kept in a DLL.
      44             :    */
      45             :   struct CoinRefund *next;
      46             : 
      47             :   /**
      48             :    * Kept in a DLL.
      49             :    */
      50             :   struct CoinRefund *prev;
      51             : 
      52             :   /**
      53             :    * Request to connect to the target exchange.
      54             :    */
      55             :   struct TMH_EXCHANGES_FindOperation *fo;
      56             : 
      57             :   /**
      58             :    * Handle for the refund operation with the exchange.
      59             :    */
      60             :   struct TALER_EXCHANGE_RefundHandle *rh;
      61             : 
      62             :   /**
      63             :    * Request this operation is part of.
      64             :    */
      65             :   struct PostRefundData *prd;
      66             : 
      67             :   /**
      68             :    * URL of the exchange for this @e coin_pub.
      69             :    */
      70             :   char *exchange_url;
      71             : 
      72             :   /**
      73             :    * Fully reply from the exchange, only possibly set if
      74             :    * we got a JSON reply and a non-#MHD_HTTP_OK error code
      75             :    */
      76             :   json_t *exchange_reply;
      77             : 
      78             :   /**
      79             :    * When did the merchant grant the refund. To be used to group events
      80             :    * in the wallet.
      81             :    */
      82             :   struct GNUNET_TIME_Timestamp execution_time;
      83             : 
      84             :   /**
      85             :    * Coin to refund.
      86             :    */
      87             :   struct TALER_CoinSpendPublicKeyP coin_pub;
      88             : 
      89             :   /**
      90             :    * Refund transaction ID to use.
      91             :    */
      92             :   uint64_t rtransaction_id;
      93             : 
      94             :   /**
      95             :    * Unique serial number identifying the refund.
      96             :    */
      97             :   uint64_t refund_serial;
      98             : 
      99             :   /**
     100             :    * Amount to refund.
     101             :    */
     102             :   struct TALER_Amount refund_amount;
     103             : 
     104             :   /**
     105             :    * Public key of the exchange affirming the refund.
     106             :    */
     107             :   struct TALER_ExchangePublicKeyP exchange_pub;
     108             : 
     109             :   /**
     110             :    * Signature of the exchange affirming the refund.
     111             :    */
     112             :   struct TALER_ExchangeSignatureP exchange_sig;
     113             : 
     114             :   /**
     115             :    * HTTP status from the exchange, #MHD_HTTP_OK if
     116             :    * @a exchange_pub and @a exchange_sig are valid.
     117             :    */
     118             :   unsigned int exchange_status;
     119             : 
     120             :   /**
     121             :    * HTTP error code from the exchange.
     122             :    */
     123             :   enum TALER_ErrorCode exchange_code;
     124             : 
     125             : };
     126             : 
     127             : 
     128             : /**
     129             :  * Context for the operation.
     130             :  */
     131             : struct PostRefundData
     132             : {
     133             : 
     134             :   /**
     135             :    * Hashed version of contract terms. All zeros if not provided.
     136             :    */
     137             :   struct TALER_PrivateContractHashP h_contract_terms;
     138             : 
     139             :   /**
     140             :    * DLL of (suspended) requests.
     141             :    */
     142             :   struct PostRefundData *next;
     143             : 
     144             :   /**
     145             :    * DLL of (suspended) requests.
     146             :    */
     147             :   struct PostRefundData *prev;
     148             : 
     149             :   /**
     150             :    * Refunds for this order. Head of DLL.
     151             :    */
     152             :   struct CoinRefund *cr_head;
     153             : 
     154             :   /**
     155             :    * Refunds for this order. Tail of DLL.
     156             :    */
     157             :   struct CoinRefund *cr_tail;
     158             : 
     159             :   /**
     160             :    * Context of the request.
     161             :    */
     162             :   struct TMH_HandlerContext *hc;
     163             : 
     164             :   /**
     165             :    * Entry in the #resume_timeout_heap for this check payment, if we are
     166             :    * suspended.
     167             :    */
     168             :   struct TMH_SuspendedConnection sc;
     169             : 
     170             :   /**
     171             :    * order ID for the payment
     172             :    */
     173             :   const char *order_id;
     174             : 
     175             :   /**
     176             :    * Where to get the contract
     177             :    */
     178             :   const char *contract_url;
     179             : 
     180             :   /**
     181             :    * fulfillment URL of the contract (valid as long as
     182             :    * @e contract_terms is valid).
     183             :    */
     184             :   const char *fulfillment_url;
     185             : 
     186             :   /**
     187             :    * session of the client
     188             :    */
     189             :   const char *session_id;
     190             : 
     191             :   /**
     192             :    * Contract terms of the payment we are checking. NULL when they
     193             :    * are not (yet) known.
     194             :    */
     195             :   json_t *contract_terms;
     196             : 
     197             :   /**
     198             :    * Total refunds granted for this payment. Only initialized
     199             :    * if @e refunded is set to true.
     200             :    */
     201             :   struct TALER_Amount refund_amount;
     202             : 
     203             :   /**
     204             :    * Did we suspend @a connection and are thus in
     205             :    * the #prd_head DLL (#GNUNET_YES). Set to
     206             :    * #GNUNET_NO if we are not suspended, and to
     207             :    * #GNUNET_SYSERR if we should close the connection
     208             :    * without a response due to shutdown.
     209             :    */
     210             :   enum GNUNET_GenericReturnValue suspended;
     211             : 
     212             :   /**
     213             :    * Return code: #TALER_EC_NONE if successful.
     214             :    */
     215             :   enum TALER_ErrorCode ec;
     216             : 
     217             :   /**
     218             :    * HTTP status to use for the reply, 0 if not yet known.
     219             :    */
     220             :   unsigned int http_status;
     221             : 
     222             :   /**
     223             :    * Set to true if we are dealing with an unclaimed order
     224             :    * (and thus @e h_contract_terms is not set, and certain
     225             :    * DB queries will not work).
     226             :    */
     227             :   bool unclaimed;
     228             : 
     229             :   /**
     230             :    * Set to true if this payment has been refunded and
     231             :    * @e refund_amount is initialized.
     232             :    */
     233             :   bool refunded;
     234             : 
     235             :   /**
     236             :    * Set to true if a refund is still available for the
     237             :    * wallet for this payment.
     238             :    */
     239             :   bool refund_available;
     240             : 
     241             :   /**
     242             :    * Set to true if the client requested HTML, otherwise
     243             :    * we generate JSON.
     244             :    */
     245             :   bool generate_html;
     246             : 
     247             : };
     248             : 
     249             : 
     250             : /**
     251             :  * Head of DLL of (suspended) requests.
     252             :  */
     253             : static struct PostRefundData *prd_head;
     254             : 
     255             : /**
     256             :  * Tail of DLL of (suspended) requests.
     257             :  */
     258             : static struct PostRefundData *prd_tail;
     259             : 
     260             : 
     261             : /**
     262             :  * Function called when we are done processing a refund request.
     263             :  * Frees memory associated with @a ctx.
     264             :  *
     265             :  * @param ctx a `struct PostRefundData`
     266             :  */
     267             : static void
     268           0 : refund_cleanup (void *ctx)
     269             : {
     270           0 :   struct PostRefundData *prd = ctx;
     271             :   struct CoinRefund *cr;
     272             : 
     273           0 :   while (NULL != (cr = prd->cr_head))
     274             :   {
     275           0 :     GNUNET_CONTAINER_DLL_remove (prd->cr_head,
     276             :                                  prd->cr_tail,
     277             :                                  cr);
     278           0 :     json_decref (cr->exchange_reply);
     279           0 :     GNUNET_free (cr->exchange_url);
     280           0 :     if (NULL != cr->fo)
     281             :     {
     282           0 :       TMH_EXCHANGES_find_exchange_cancel (cr->fo);
     283           0 :       cr->fo = NULL;
     284             :     }
     285           0 :     if (NULL != cr->rh)
     286             :     {
     287           0 :       TALER_EXCHANGE_refund_cancel (cr->rh);
     288           0 :       cr->rh = NULL;
     289             :     }
     290           0 :     GNUNET_free (cr);
     291             :   }
     292           0 :   json_decref (prd->contract_terms);
     293           0 :   GNUNET_free (prd);
     294           0 : }
     295             : 
     296             : 
     297             : /**
     298             :  * Force resuming all suspended order lookups, needed during shutdown.
     299             :  */
     300             : void
     301           3 : TMH_force_wallet_refund_order_resume (void)
     302             : {
     303             :   struct PostRefundData *prd;
     304             : 
     305           3 :   while (NULL != (prd = prd_head))
     306             :   {
     307           0 :     GNUNET_CONTAINER_DLL_remove (prd_head,
     308             :                                  prd_tail,
     309             :                                  prd);
     310           0 :     GNUNET_assert (GNUNET_YES == prd->suspended);
     311           0 :     prd->suspended = GNUNET_SYSERR;
     312           0 :     MHD_resume_connection (prd->sc.con);
     313             :   }
     314           3 : }
     315             : 
     316             : 
     317             : /**
     318             :  * Check if @a prd has exchange requests still pending.
     319             :  *
     320             :  * @param prd state to check
     321             :  * @return true if activities are still pending
     322             :  */
     323             : static bool
     324           0 : exchange_operations_pending (struct PostRefundData *prd)
     325             : {
     326           0 :   for (struct CoinRefund *cr = prd->cr_head;
     327             :        NULL != cr;
     328           0 :        cr = cr->next)
     329             :   {
     330           0 :     if ( (NULL != cr->fo) ||
     331           0 :          (NULL != cr->rh) )
     332           0 :       return true;
     333             :   }
     334           0 :   return false;
     335             : }
     336             : 
     337             : 
     338             : /**
     339             :  * Check if @a prd is ready to be resumed, and if so, do it.
     340             :  *
     341             :  * @param prd refund request to be possibly ready
     342             :  */
     343             : static void
     344           0 : check_resume_prd (struct PostRefundData *prd)
     345             : {
     346           0 :   if ( (TALER_EC_NONE == prd->ec) &&
     347           0 :        exchange_operations_pending (prd) )
     348           0 :     return;
     349           0 :   GNUNET_CONTAINER_DLL_remove (prd_head,
     350             :                                prd_tail,
     351             :                                prd);
     352           0 :   GNUNET_assert (prd->suspended);
     353           0 :   prd->suspended = GNUNET_NO;
     354           0 :   MHD_resume_connection (prd->sc.con);
     355           0 :   TALER_MHD_daemon_trigger ();
     356             : }
     357             : 
     358             : 
     359             : /**
     360             :  * Notify applications waiting for a client to obtain
     361             :  * a refund.
     362             :  *
     363             :  * @param prd refund request with the change
     364             :  */
     365             : static void
     366           0 : notify_refund_obtained (struct PostRefundData *prd)
     367             : {
     368           0 :   struct TMH_OrderPayEventP refund_eh = {
     369           0 :     .header.size = htons (sizeof (refund_eh)),
     370           0 :     .header.type = htons (TALER_DBEVENT_MERCHANT_REFUND_OBTAINED),
     371           0 :     .merchant_pub = prd->hc->instance->merchant_pub
     372             :   };
     373             : 
     374           0 :   GNUNET_CRYPTO_hash (prd->order_id,
     375             :                       strlen (prd->order_id),
     376             :                       &refund_eh.h_order_id);
     377           0 :   TMH_db->event_notify (TMH_db->cls,
     378             :                         &refund_eh.header,
     379             :                         NULL,
     380             :                         0);
     381           0 : }
     382             : 
     383             : 
     384             : /**
     385             :  * Callbacks of this type are used to serve the result of submitting a
     386             :  * refund request to an exchange.
     387             :  *
     388             :  * @param cls a `struct CoinRefund`
     389             :  * @param hr HTTP response data
     390             :  * @param exchange_pub exchange key used to sign refund confirmation
     391             :  * @param exchange_sig exchange's signature over refund
     392             :  */
     393             : static void
     394           0 : refund_cb (void *cls,
     395             :            const struct TALER_EXCHANGE_HttpResponse *hr,
     396             :            const struct TALER_ExchangePublicKeyP *exchange_pub,
     397             :            const struct TALER_ExchangeSignatureP *exchange_sig)
     398             : {
     399           0 :   struct CoinRefund *cr = cls;
     400             : 
     401           0 :   cr->rh = NULL;
     402           0 :   cr->exchange_status = hr->http_status;
     403           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     404             :               "Exchange refund status for coin %s is %u\n",
     405             :               TALER_B2S (&cr->coin_pub),
     406             :               hr->http_status);
     407           0 :   if (MHD_HTTP_OK != hr->http_status)
     408             :   {
     409           0 :     cr->exchange_code = hr->ec;
     410           0 :     cr->exchange_reply = json_incref ((json_t*) hr->reply);
     411             :   }
     412             :   else
     413             :   {
     414             :     enum GNUNET_DB_QueryStatus qs;
     415             : 
     416           0 :     cr->exchange_pub = *exchange_pub;
     417           0 :     cr->exchange_sig = *exchange_sig;
     418           0 :     qs = TMH_db->insert_refund_proof (TMH_db->cls,
     419             :                                       cr->refund_serial,
     420             :                                       exchange_sig,
     421             :                                       exchange_pub);
     422           0 :     if (0 >= qs)
     423             :     {
     424             :       /* generally, this is relatively harmless for the merchant, but let's at
     425             :          least log this. */
     426           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     427             :                   "Failed to persist exchange response to /refund in database: %d\n",
     428             :                   qs);
     429             :     }
     430             :     else
     431             :     {
     432           0 :       notify_refund_obtained (cr->prd);
     433             :     }
     434             :   }
     435           0 :   check_resume_prd (cr->prd);
     436           0 : }
     437             : 
     438             : 
     439             : /**
     440             :  * Function called with the result of a #TMH_EXCHANGES_find_exchange()
     441             :  * operation.
     442             :  *
     443             :  * @param cls a `struct CoinRefund *`
     444             :  * @param hr HTTP response details
     445             :  * @param eh handle to the exchange context
     446             :  * @param payto_uri payto://-URI of the exchange
     447             :  * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
     448             :  * @param exchange_trusted true if this exchange is trusted by config
     449             :  */
     450             : static void
     451           0 : exchange_found_cb (void *cls,
     452             :                    const struct TALER_EXCHANGE_HttpResponse *hr,
     453             :                    struct TALER_EXCHANGE_Handle *eh,
     454             :                    const char *payto_uri,
     455             :                    const struct TALER_Amount *wire_fee,
     456             :                    bool exchange_trusted)
     457             : {
     458           0 :   struct CoinRefund *cr = cls;
     459           0 :   struct PostRefundData *prd = cr->prd;
     460             : 
     461             :   (void) payto_uri;
     462             :   (void) wire_fee;
     463             :   (void) exchange_trusted;
     464           0 :   cr->fo = NULL;
     465           0 :   if (NULL == hr)
     466             :   {
     467           0 :     prd->http_status = MHD_HTTP_GATEWAY_TIMEOUT;
     468           0 :     prd->ec = TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT;
     469           0 :     check_resume_prd (prd);
     470           0 :     return;
     471             :   }
     472           0 :   if (NULL == eh)
     473             :   {
     474           0 :     prd->http_status = MHD_HTTP_BAD_GATEWAY;
     475           0 :     prd->ec = TALER_EC_MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE;
     476           0 :     check_resume_prd (prd);
     477           0 :     return;
     478             :   }
     479           0 :   cr->rh = TALER_EXCHANGE_refund (eh,
     480           0 :                                   &cr->refund_amount,
     481           0 :                                   &prd->h_contract_terms,
     482           0 :                                   &cr->coin_pub,
     483             :                                   cr->rtransaction_id,
     484           0 :                                   &prd->hc->instance->merchant_priv,
     485             :                                   &refund_cb,
     486             :                                   cr);
     487             : }
     488             : 
     489             : 
     490             : /**
     491             :  * Function called with information about a refund.
     492             :  * It is responsible for summing up the refund amount.
     493             :  *
     494             :  * @param cls closure
     495             :  * @param refund_serial unique serial number of the refund
     496             :  * @param timestamp time of the refund (for grouping of refunds in the wallet UI)
     497             :  * @param coin_pub public coin from which the refund comes from
     498             :  * @param exchange_url URL of the exchange that issued @a coin_pub
     499             :  * @param rtransaction_id identificator of the refund
     500             :  * @param reason human-readable explanation of the refund
     501             :  * @param refund_amount refund amount which is being taken from @a coin_pub
     502             :  * @param pending true if the this refund was not yet processed by the wallet/exchange
     503             :  */
     504             : static void
     505           0 : process_refunds_cb (void *cls,
     506             :                     uint64_t refund_serial,
     507             :                     struct GNUNET_TIME_Timestamp timestamp,
     508             :                     const struct TALER_CoinSpendPublicKeyP *coin_pub,
     509             :                     const char *exchange_url,
     510             :                     uint64_t rtransaction_id,
     511             :                     const char *reason,
     512             :                     const struct TALER_Amount *refund_amount,
     513             :                     bool pending)
     514             : {
     515           0 :   struct PostRefundData *prd = cls;
     516             :   struct CoinRefund *cr;
     517             : 
     518           0 :   for (cr = prd->cr_head;
     519             :        NULL != cr;
     520           0 :        cr = cr->next)
     521           0 :     if (cr->refund_serial == refund_serial)
     522           0 :       return;
     523             :   /* already known */
     524             : 
     525           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     526             :               "Found refund of %s for coin %s with reason `%s' in database\n",
     527             :               TALER_amount2s (refund_amount),
     528             :               TALER_B2S (coin_pub),
     529             :               reason);
     530           0 :   cr = GNUNET_new (struct CoinRefund);
     531           0 :   cr->refund_serial = refund_serial;
     532           0 :   cr->exchange_url = GNUNET_strdup (exchange_url);
     533           0 :   cr->prd = prd;
     534           0 :   cr->coin_pub = *coin_pub;
     535           0 :   cr->rtransaction_id = rtransaction_id;
     536           0 :   cr->refund_amount = *refund_amount;
     537           0 :   cr->execution_time = timestamp;
     538           0 :   GNUNET_CONTAINER_DLL_insert (prd->cr_head,
     539             :                                prd->cr_tail,
     540             :                                cr);
     541           0 :   if (prd->refunded)
     542             :   {
     543           0 :     GNUNET_assert (0 <=
     544             :                    TALER_amount_add (&prd->refund_amount,
     545             :                                      &prd->refund_amount,
     546             :                                      refund_amount));
     547           0 :     return;
     548             :   }
     549           0 :   prd->refund_amount = *refund_amount;
     550           0 :   prd->refunded = true;
     551           0 :   prd->refund_available |= pending;
     552             : }
     553             : 
     554             : 
     555             : /**
     556             :  * Obtain refunds for an order.
     557             :  *
     558             :  * @param rh context of the handler
     559             :  * @param connection the MHD connection to handle
     560             :  * @param[in,out] hc context with further information about the request
     561             :  * @return MHD result code
     562             :  */
     563             : MHD_RESULT
     564           0 : TMH_post_orders_ID_refund (const struct TMH_RequestHandler *rh,
     565             :                            struct MHD_Connection *connection,
     566             :                            struct TMH_HandlerContext *hc)
     567             : {
     568           0 :   struct PostRefundData *prd = hc->ctx;
     569             :   enum GNUNET_DB_QueryStatus qs;
     570             : 
     571           0 :   if (NULL == prd)
     572             :   {
     573           0 :     prd = GNUNET_new (struct PostRefundData);
     574           0 :     prd->sc.con = connection;
     575           0 :     prd->hc = hc;
     576           0 :     prd->order_id = hc->infix;
     577           0 :     hc->ctx = prd;
     578           0 :     hc->cc = &refund_cleanup;
     579             :     {
     580             :       enum GNUNET_GenericReturnValue res;
     581             : 
     582             :       struct GNUNET_JSON_Specification spec[] = {
     583           0 :         GNUNET_JSON_spec_fixed_auto ("h_contract", &prd->h_contract_terms),
     584           0 :         GNUNET_JSON_spec_end ()
     585             :       };
     586           0 :       res = TALER_MHD_parse_json_data (connection,
     587           0 :                                        hc->request_body,
     588             :                                        spec);
     589           0 :       if (GNUNET_OK != res)
     590             :         return (GNUNET_NO == res)
     591             :                ? MHD_YES
     592           0 :                : MHD_NO;
     593             :     }
     594             : 
     595           0 :     TMH_db->preflight (TMH_db->cls);
     596             :     {
     597             :       json_t *contract_terms;
     598             :       uint64_t order_serial;
     599             :       bool paid;
     600             : 
     601           0 :       qs = TMH_db->lookup_contract_terms (TMH_db->cls,
     602           0 :                                           hc->instance->settings.id,
     603           0 :                                           hc->infix,
     604             :                                           &contract_terms,
     605             :                                           &order_serial,
     606             :                                           &paid,
     607             :                                           NULL);
     608           0 :       if (0 > qs)
     609             :       {
     610             :         /* single, read-only SQL statements should never cause
     611             :            serialization problems */
     612           0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
     613             :         /* Always report on hard error as well to enable diagnostics */
     614           0 :         GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
     615           0 :         return TALER_MHD_reply_with_error (connection,
     616             :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     617             :                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
     618             :                                            "contract terms");
     619             :       }
     620           0 :       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     621             :       {
     622           0 :         json_decref (contract_terms);
     623           0 :         return TALER_MHD_reply_with_error (connection,
     624             :                                            MHD_HTTP_NOT_FOUND,
     625             :                                            TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
     626           0 :                                            hc->infix);
     627             :       }
     628             :       {
     629             :         struct TALER_PrivateContractHashP h_contract_terms;
     630             : 
     631           0 :         if (GNUNET_OK !=
     632           0 :             TALER_JSON_contract_hash (contract_terms,
     633             :                                       &h_contract_terms))
     634             :         {
     635           0 :           GNUNET_break (0);
     636           0 :           json_decref (contract_terms);
     637           0 :           return TALER_MHD_reply_with_error (connection,
     638             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     639             :                                              TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
     640             :                                              NULL);
     641             :         }
     642           0 :         json_decref (contract_terms);
     643           0 :         if (0 != GNUNET_memcmp (&h_contract_terms,
     644             :                                 &prd->h_contract_terms))
     645             :         {
     646           0 :           return TALER_MHD_reply_with_error (
     647             :             connection,
     648             :             MHD_HTTP_FORBIDDEN,
     649             :             TALER_EC_MERCHANT_GENERIC_CONTRACT_HASH_DOES_NOT_MATCH_ORDER,
     650             :             NULL);
     651             :         }
     652             :       }
     653             :     }
     654             :   }
     655           0 :   if (GNUNET_SYSERR == prd->suspended)
     656           0 :     return MHD_NO; /* we are in shutdown */
     657             : 
     658           0 :   if (TALER_EC_NONE != prd->ec)
     659             :   {
     660           0 :     GNUNET_break (0 != prd->http_status);
     661             :     /* kill pending coin refund operations immediately, just to be
     662             :        extra sure they don't modify 'prd' after we already created
     663             :        a reply (this might not be needed, but feels safer). */
     664           0 :     for (struct CoinRefund *cr = prd->cr_head;
     665             :          NULL != cr;
     666           0 :          cr = cr->next)
     667             :     {
     668           0 :       if (NULL != cr->fo)
     669             :       {
     670           0 :         TMH_EXCHANGES_find_exchange_cancel (cr->fo);
     671           0 :         cr->fo = NULL;
     672             :       }
     673           0 :       if (NULL != cr->rh)
     674             :       {
     675           0 :         TALER_EXCHANGE_refund_cancel (cr->rh);
     676           0 :         cr->rh = NULL;
     677             :       }
     678             :     }
     679           0 :     return TALER_MHD_reply_with_error (connection,
     680             :                                        prd->http_status,
     681             :                                        prd->ec,
     682             :                                        NULL);
     683             :   }
     684             : 
     685             :   {
     686           0 :     GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TMH_currency,
     687             :                                                        &prd->refund_amount));
     688           0 :     qs = TMH_db->lookup_refunds_detailed (TMH_db->cls,
     689           0 :                                           hc->instance->settings.id,
     690           0 :                                           &prd->h_contract_terms,
     691             :                                           &process_refunds_cb,
     692             :                                           prd);
     693           0 :     if (0 > qs)
     694             :     {
     695           0 :       GNUNET_break (0);
     696           0 :       return TALER_MHD_reply_with_error (connection,
     697             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     698             :                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
     699             :                                          "detailed refunds");
     700             :     }
     701             :   }
     702             : 
     703             :   /* Now launch exchange interactions, unless we already have the
     704             :      response in the database! */
     705           0 :   for (struct CoinRefund *cr = prd->cr_head;
     706             :        NULL != cr;
     707           0 :        cr = cr->next)
     708             :   {
     709             :     enum GNUNET_DB_QueryStatus qs;
     710             : 
     711           0 :     qs = TMH_db->lookup_refund_proof (TMH_db->cls,
     712             :                                       cr->refund_serial,
     713             :                                       &cr->exchange_sig,
     714             :                                       &cr->exchange_pub);
     715           0 :     switch (qs)
     716             :     {
     717           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
     718             :     case GNUNET_DB_STATUS_SOFT_ERROR:
     719           0 :       return TALER_MHD_reply_with_error (connection,
     720             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     721             :                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
     722             :                                          "refund proof");
     723           0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     724           0 :       if (NULL == cr->exchange_reply)
     725             :       {
     726             :         /* We need to talk to the exchange */
     727           0 :         cr->fo = TMH_EXCHANGES_find_exchange (cr->exchange_url,
     728             :                                               NULL,
     729             :                                               GNUNET_NO,
     730             :                                               &exchange_found_cb,
     731             :                                               cr);
     732             :       }
     733           0 :       break;
     734           0 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     735             :       /* We got a reply earlier, set status accordingly */
     736           0 :       cr->exchange_status = MHD_HTTP_OK;
     737           0 :       break;
     738             :     }
     739           0 :   }
     740             : 
     741             :   /* Check if there are still exchange operations pending */
     742           0 :   if (exchange_operations_pending (prd))
     743             :   {
     744           0 :     if (GNUNET_NO == prd->suspended)
     745             :     {
     746           0 :       prd->suspended = GNUNET_YES;
     747           0 :       MHD_suspend_connection (connection);
     748           0 :       GNUNET_CONTAINER_DLL_insert (prd_head,
     749             :                                    prd_tail,
     750             :                                    prd);
     751             :     }
     752           0 :     return MHD_YES;   /* we're still talking to the exchange */
     753             :   }
     754             : 
     755             :   {
     756             :     json_t *ra;
     757             : 
     758           0 :     ra = json_array ();
     759           0 :     GNUNET_assert (NULL != ra);
     760           0 :     for (struct CoinRefund *cr = prd->cr_head;
     761             :          NULL != cr;
     762           0 :          cr = cr->next)
     763             :     {
     764             :       json_t *refund;
     765             : 
     766           0 :       if (MHD_HTTP_OK != cr->exchange_status)
     767             :       {
     768           0 :         if (NULL == cr->exchange_reply)
     769             :         {
     770           0 :           refund = GNUNET_JSON_PACK (
     771             :             GNUNET_JSON_pack_string ("type",
     772             :                                      "failure"),
     773             :             GNUNET_JSON_pack_uint64 ("exchange_status",
     774             :                                      cr->exchange_status),
     775             :             GNUNET_JSON_pack_uint64 ("rtransaction_id",
     776             :                                      cr->rtransaction_id),
     777             :             GNUNET_JSON_pack_data_auto ("coin_pub",
     778             :                                         &cr->coin_pub),
     779             :             TALER_JSON_pack_amount ("refund_amount",
     780             :                                     &cr->refund_amount),
     781             :             GNUNET_JSON_pack_timestamp ("execution_time",
     782             :                                         cr->execution_time));
     783             :         }
     784             :         else
     785             :         {
     786           0 :           refund = GNUNET_JSON_PACK (
     787             :             GNUNET_JSON_pack_string ("type",
     788             :                                      "failure"),
     789             :             GNUNET_JSON_pack_uint64 ("exchange_status",
     790             :                                      cr->exchange_status),
     791             :             GNUNET_JSON_pack_uint64 ("exchange_code",
     792             :                                      cr->exchange_code),
     793             :             GNUNET_JSON_pack_object_incref ("exchange_reply",
     794             :                                             cr->exchange_reply),
     795             :             GNUNET_JSON_pack_uint64 ("rtransaction_id",
     796             :                                      cr->rtransaction_id),
     797             :             GNUNET_JSON_pack_data_auto ("coin_pub",
     798             :                                         &cr->coin_pub),
     799             :             TALER_JSON_pack_amount ("refund_amount",
     800             :                                     &cr->refund_amount),
     801             :             GNUNET_JSON_pack_timestamp ("execution_time",
     802             :                                         cr->execution_time));
     803             :         }
     804             :       }
     805             :       else
     806             :       {
     807           0 :         refund = GNUNET_JSON_PACK (
     808             :           GNUNET_JSON_pack_string ("type",
     809             :                                    "success"),
     810             :           GNUNET_JSON_pack_uint64 ("exchange_status",
     811             :                                    cr->exchange_status),
     812             :           GNUNET_JSON_pack_data_auto ("exchange_sig",
     813             :                                       &cr->exchange_sig),
     814             :           GNUNET_JSON_pack_data_auto ("exchange_pub",
     815             :                                       &cr->exchange_pub),
     816             :           GNUNET_JSON_pack_uint64 ("rtransaction_id",
     817             :                                    cr->rtransaction_id),
     818             :           GNUNET_JSON_pack_data_auto ("coin_pub",
     819             :                                       &cr->coin_pub),
     820             :           TALER_JSON_pack_amount ("refund_amount",
     821             :                                   &cr->refund_amount),
     822             :           GNUNET_JSON_pack_timestamp ("execution_time",
     823             :                                       cr->execution_time));
     824             :       }
     825           0 :       GNUNET_assert (
     826             :         0 ==
     827             :         json_array_append_new (ra,
     828             :                                refund));
     829             :     }
     830             : 
     831           0 :     return TALER_MHD_REPLY_JSON_PACK (
     832             :       connection,
     833             :       MHD_HTTP_OK,
     834             :       TALER_JSON_pack_amount ("refund_amount",
     835             :                               &prd->refund_amount),
     836             :       GNUNET_JSON_pack_array_steal ("refunds",
     837             :                                     ra),
     838             :       GNUNET_JSON_pack_data_auto ("merchant_pub",
     839             :                                   &hc->instance->merchant_pub));
     840             :   }
     841             : 
     842             :   return MHD_YES;
     843             : }
     844             : 
     845             : 
     846             : /* end of taler-merchant-httpd_post-orders-ID-refund.c */

Generated by: LCOV version 1.14