LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_refund.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 102 172 59.3 %
Date: 2017-11-25 11:31:41 Functions: 5 6 83.3 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2017 Inria and GNUnet e.V.
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Affero General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU Affero General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file taler-exchange-httpd_refund.c
      18             :  * @brief Handle /refund requests; parses the POST and JSON and
      19             :  *        verifies the coin signature before handing things off
      20             :  *        to the database.
      21             :  * @author Florian Dold
      22             :  * @author Benedikt Mueller
      23             :  * @author Christian Grothoff
      24             :  */
      25             : #include "platform.h"
      26             : #include <gnunet/gnunet_util_lib.h>
      27             : #include <gnunet/gnunet_json_lib.h>
      28             : #include <jansson.h>
      29             : #include <microhttpd.h>
      30             : #include <pthread.h>
      31             : #include "taler_json_lib.h"
      32             : #include "taler-exchange-httpd_parsing.h"
      33             : #include "taler-exchange-httpd_refund.h"
      34             : #include "taler-exchange-httpd_responses.h"
      35             : #include "taler-exchange-httpd_keystate.h"
      36             : #include "taler-exchange-httpd_validation.h"
      37             : 
      38             : 
      39             : /**
      40             :  * Generate successful refund confirmation message.
      41             :  *
      42             :  * @param connection connection to the client
      43             :  * @param refund details about the successful refund
      44             :  * @return MHD result code
      45             :  */
      46             : static int
      47           1 : reply_refund_success (struct MHD_Connection *connection,
      48             :                       const struct TALER_EXCHANGEDB_Refund *refund)
      49             : {
      50             :   struct TALER_RefundConfirmationPS rc;
      51             :   struct TALER_ExchangePublicKeyP pub;
      52             :   struct TALER_ExchangeSignatureP sig;
      53             : 
      54           1 :   rc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND);
      55           1 :   rc.purpose.size = htonl (sizeof (struct TALER_RefundConfirmationPS));
      56           1 :   rc.h_contract_terms = refund->h_contract_terms;
      57           1 :   rc.coin_pub = refund->coin.coin_pub;
      58           1 :   rc.merchant = refund->merchant_pub;
      59           1 :   rc.rtransaction_id = GNUNET_htonll (refund->rtransaction_id);
      60           1 :   TALER_amount_hton (&rc.refund_amount,
      61             :                      &refund->refund_amount);
      62           1 :   TALER_amount_hton (&rc.refund_fee,
      63             :                      &refund->refund_fee);
      64           1 :   if (GNUNET_OK !=
      65           1 :       TEH_KS_sign (&rc.purpose,
      66             :                    &pub,
      67             :                    &sig))
      68             :   {
      69           0 :     return TEH_RESPONSE_reply_internal_error (connection,
      70             :                                               TALER_EC_EXCHANGE_BAD_CONFIGURATION,
      71             :                                               "no keys");
      72             :   }
      73           1 :   return TEH_RESPONSE_reply_json_pack (connection,
      74             :                                        MHD_HTTP_OK,
      75             :                                        "{s:s, s:o, s:o}",
      76             :                                        "status", "REFUND_OK",
      77             :                                        "sig", GNUNET_JSON_from_data_auto (&sig),
      78             :                                        "pub", GNUNET_JSON_from_data_auto (&pub));
      79             : }
      80             : 
      81             : 
      82             : /**
      83             :  * Generate generic refund failure message. All the details
      84             :  * are in the @a response_code.  The body can be empty.
      85             :  *
      86             :  * @param connection connection to the client
      87             :  * @param response_code response code to generate
      88             :  * @param ec taler error code to include
      89             :  * @return MHD result code
      90             :  */
      91             : static int
      92           1 : reply_refund_failure (struct MHD_Connection *connection,
      93             :                       unsigned int response_code,
      94             :                       enum TALER_ErrorCode ec)
      95             : {
      96           1 :   return TEH_RESPONSE_reply_json_pack (connection,
      97             :                                        response_code,
      98             :                                        "{s:s, s:I}",
      99             :                                        "status", "refund failure",
     100             :                                        "code", (json_int_t) ec);
     101             : }
     102             : 
     103             : 
     104             : /**
     105             :  * Generate refund conflict failure message. Returns the
     106             :  * transaction list @a tl with the details about the conflict.
     107             :  *
     108             :  * @param connection connection to the client
     109             :  * @param tl transaction list showing the conflict
     110             :  * @return MHD result code
     111             :  */
     112             : static int
     113           0 : reply_refund_conflict (struct MHD_Connection *connection,
     114             :                        const struct TALER_EXCHANGEDB_TransactionList *tl)
     115             : {
     116           0 :   return TEH_RESPONSE_reply_json_pack (connection,
     117             :                                        MHD_HTTP_CONFLICT,
     118             :                                        "{s:s, s:I, s:o}",
     119             :                                        "status", "conflicting refund",
     120             :                                        "code", (json_int_t) TALER_EC_REFUND_CONFLICT,
     121             :                                        "history", TEH_RESPONSE_compile_transaction_history (tl));
     122             : }
     123             : 
     124             : 
     125             : /**
     126             :  * Execute a "/refund" transaction.  Returns a confirmation that the
     127             :  * refund was successful, or a failure if we are not aware of a
     128             :  * matching /deposit or if it is too late to do the refund.
     129             :  *
     130             :  * IF it returns a non-error code, the transaction logic MUST
     131             :  * NOT queue a MHD response.  IF it returns an hard error, the
     132             :  * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF
     133             :  * it returns the soft error code, the function MAY be called again to
     134             :  * retry and MUST not queue a MHD response.
     135             :  *
     136             :  * @param cls closure with a `const struct TALER_EXCHANGEDB_Refund *`
     137             :  * @param connection MHD request which triggered the transaction
     138             :  * @param session database session to use
     139             :  * @param[out] mhd_ret set to MHD response status for @a connection,
     140             :  *             if transaction failed (!)
     141             :  * @return transaction status
     142             :  */
     143             : static enum GNUNET_DB_QueryStatus
     144           2 : refund_transaction (void *cls,
     145             :                     struct MHD_Connection *connection,
     146             :                     struct TALER_EXCHANGEDB_Session *session,
     147             :                     int *mhd_ret)
     148             : {
     149           2 :   const struct TALER_EXCHANGEDB_Refund *refund = cls;
     150             :   struct TALER_EXCHANGEDB_TransactionList *tl;
     151             :   const struct TALER_EXCHANGEDB_Deposit *dep;
     152             :   const struct TALER_EXCHANGEDB_Refund *ref;
     153             :   struct TEH_KS_StateHandle *mks;
     154             :   struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
     155             :   struct TALER_Amount expect_fee;
     156             :   enum GNUNET_DB_QueryStatus qs;
     157             :   int deposit_found;
     158             :   int refund_found;
     159             :   int fee_cmp;
     160             : 
     161           2 :   dep = NULL;
     162           2 :   ref = NULL;
     163           2 :   tl = NULL;
     164           2 :   qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
     165             :                                           session,
     166             :                                           &refund->coin.coin_pub,
     167             :                                           &tl);
     168           2 :   if (0 > qs)
     169             :   {
     170           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     171           0 :       *mhd_ret = reply_refund_failure (connection,
     172             :                                        MHD_HTTP_NOT_FOUND,
     173             :                                        TALER_EC_REFUND_COIN_NOT_FOUND);
     174           0 :     return qs;
     175             :   }
     176           2 :   deposit_found = GNUNET_NO;
     177           2 :   refund_found = GNUNET_NO;
     178           8 :   for (struct TALER_EXCHANGEDB_TransactionList *tlp = tl;
     179             :        NULL != tlp;
     180           4 :        tlp = tlp->next)
     181             :   {
     182           4 :     switch (tlp->type)
     183             :     {
     184             :     case TALER_EXCHANGEDB_TT_DEPOSIT:
     185           3 :       if (GNUNET_NO == deposit_found)
     186             :       {
     187           3 :         if ( (0 == memcmp (&tlp->details.deposit->merchant_pub,
     188           3 :                            &refund->merchant_pub,
     189           2 :                            sizeof (struct TALER_MerchantPublicKeyP))) &&
     190           2 :              (0 == memcmp (&tlp->details.deposit->h_contract_terms,
     191           2 :                            &refund->h_contract_terms,
     192             :                            sizeof (struct GNUNET_HashCode))) )
     193             :         {
     194           2 :           dep = tlp->details.deposit;
     195           2 :           deposit_found = GNUNET_YES;
     196           2 :           break;
     197             :         }
     198             :       }
     199           1 :       break;
     200             :     case TALER_EXCHANGEDB_TT_REFRESH_MELT:
     201             :       /* Melts cannot be refunded, ignore here */
     202           0 :       break;
     203             :     case TALER_EXCHANGEDB_TT_REFUND:
     204           1 :       if (GNUNET_NO == refund_found)
     205             :       {
     206             :         /* First, check if existing refund request is identical */
     207           1 :         if ( (0 == memcmp (&tlp->details.refund->merchant_pub,
     208           1 :                            &refund->merchant_pub,
     209           0 :                            sizeof (struct TALER_MerchantPublicKeyP))) &&
     210           0 :              (0 == memcmp (&tlp->details.refund->h_contract_terms,
     211           0 :                            &refund->h_contract_terms,
     212           0 :                            sizeof (struct GNUNET_HashCode))) &&
     213           0 :              (tlp->details.refund->rtransaction_id == refund->rtransaction_id) )
     214             :         {
     215           0 :           ref = tlp->details.refund;
     216           0 :           refund_found = GNUNET_YES;
     217           0 :           break;
     218             :         }
     219             :         /* Second, check if existing refund request conflicts */
     220           1 :         if ( (0 == memcmp (&tlp->details.refund->merchant_pub,
     221           1 :                            &refund->merchant_pub,
     222           0 :                            sizeof (struct TALER_MerchantPublicKeyP))) &&
     223           0 :              (0 == memcmp (&tlp->details.refund->h_contract_terms,
     224           0 :                            &refund->h_contract_terms,
     225           0 :                            sizeof (struct GNUNET_HashCode))) &&
     226           0 :              (tlp->details.refund->rtransaction_id != refund->rtransaction_id) )
     227             :         {
     228           0 :           GNUNET_break_op (0); /* conflicting refund found */
     229           0 :           refund_found = GNUNET_SYSERR;
     230             :           /* NOTE: Alternatively we could total up all existing
     231             :              refunds and check if the sum still permits the
     232             :              refund requested (thus allowing multiple, partial
     233             :              refunds). Fow now, we keep it simple. */
     234           0 :           break;
     235             :         }
     236             :       }
     237           1 :       break;
     238             :     case TALER_EXCHANGEDB_TT_PAYBACK:
     239             :       /* Paybacks cannot be refunded, ignore here */
     240           0 :       break;
     241             :     }
     242             :   }
     243             :   /* handle if deposit was NOT found */
     244           2 :   if (GNUNET_NO == deposit_found)
     245             :   {
     246           0 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     247             :                                             tl);
     248           0 :     *mhd_ret = TEH_RESPONSE_reply_transaction_unknown (connection,
     249             :                                                        TALER_EC_REFUND_DEPOSIT_NOT_FOUND);
     250           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     251             :   }
     252             :   /* handle if conflicting refund found */
     253           2 :   if (GNUNET_SYSERR == refund_found)
     254             :   {
     255           0 :     *mhd_ret = reply_refund_conflict (connection,
     256             :                                       tl);
     257           0 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     258             :                                             tl);
     259           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     260             :   }
     261             :   /* handle if identical refund found */
     262           2 :   if (GNUNET_YES == refund_found)
     263             :   {
     264             :     /* /refund already done, simply re-transmit confirmation */
     265           0 :     *mhd_ret = reply_refund_success (connection,
     266             :                                      ref);
     267           0 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     268             :                                             tl);
     269           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     270             :   }
     271             : 
     272             :   /* check currency is compatible */
     273           2 :   if ( (GNUNET_YES !=
     274           2 :         TALER_amount_cmp_currency (&refund->refund_amount,
     275           2 :                                    &dep->amount_with_fee)) ||
     276             :        (GNUNET_YES !=
     277           2 :         TALER_amount_cmp_currency (&refund->refund_fee,
     278             :                                    &dep->deposit_fee)) )
     279             :   {
     280           0 :     GNUNET_break_op (0); /* currency missmatch */
     281           0 :     *mhd_ret = reply_refund_failure (connection,
     282             :                                      MHD_HTTP_PRECONDITION_FAILED,
     283             :                                      TALER_EC_REFUND_CURRENCY_MISSMATCH);
     284           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     285             :   }
     286             : 
     287             :   /* check if we already send the money for the /deposit */
     288             :   // FIXME: DB API...
     289           2 :   qs = TEH_plugin->test_deposit_done (TEH_plugin->cls,
     290             :                                       session,
     291             :                                       dep);
     292           2 :   if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     293             :   {
     294             :     /* Internal error, we first had the deposit in the history,
     295             :        but now it is gone? */
     296           0 :     GNUNET_break (0);
     297           0 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     298             :                                             tl);
     299           0 :     *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
     300             :                                                   TALER_EC_REFUND_DB_INCONSISTENT,
     301             :                                                   "database inconsistent");
     302           0 :     return qs;
     303             :   }
     304           2 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     305           0 :     return qs; /* go and retry */
     306             : 
     307           2 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     308             :   {
     309             :     /* money was already transferred to merchant, can no longer refund */
     310           1 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     311             :                                             tl);
     312           1 :     *mhd_ret = reply_refund_failure (connection,
     313             :                                      MHD_HTTP_GONE,
     314             :                                      TALER_EC_REFUND_MERCHANT_ALREADY_PAID);
     315           1 :     return GNUNET_DB_STATUS_HARD_ERROR;
     316             :   }
     317             : 
     318             :   /* check refund amount is sufficiently low */
     319           1 :   if (1 == TALER_amount_cmp (&refund->refund_amount,
     320             :                              &dep->amount_with_fee) )
     321             :   {
     322           0 :     GNUNET_break_op (0); /* cannot refund more than original value */
     323           0 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     324             :                                             tl);
     325           0 :     *mhd_ret = reply_refund_failure (connection,
     326             :                                      MHD_HTTP_PRECONDITION_FAILED,
     327             :                                      TALER_EC_REFUND_INSUFFICIENT_FUNDS);
     328           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     329             :   }
     330             : 
     331             :   // FIXME: do this outside of transaction function?
     332             :   /* Check refund fee matches fee of denomination key! */
     333           1 :   mks = TEH_KS_acquire ();
     334           1 :   if (NULL == mks)
     335             :   {
     336           0 :     TALER_LOG_ERROR ("Lacking keys to operate\n");
     337           0 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     338             :                                             tl);
     339           0 :     *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
     340             :                                                   TALER_EC_EXCHANGE_BAD_CONFIGURATION,
     341             :                                                   "no keys");
     342           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     343             :   }
     344           1 :   dki = TEH_KS_denomination_key_lookup (mks,
     345             :                                         &dep->coin.denom_pub,
     346             :                                         TEH_KS_DKU_DEPOSIT);
     347           1 :   if (NULL == dki)
     348             :   {
     349             :     /* DKI not found, but we do have a coin with this DK in our database;
     350             :        not good... */
     351           0 :     GNUNET_break (0);
     352           0 :     TEH_KS_release (mks);
     353           0 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     354             :                                             tl);
     355           0 :     *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
     356             :                                                   TALER_EC_REFUND_DENOMINATION_KEY_NOT_FOUND,
     357             :                                                   "denomination key not found");
     358           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     359             :   }
     360           1 :   TALER_amount_ntoh (&expect_fee,
     361           1 :                      &dki->issue.properties.fee_refund);
     362           1 :   fee_cmp = TALER_amount_cmp (&refund->refund_fee,
     363             :                               &expect_fee);
     364           1 :   TEH_KS_release (mks);
     365             : 
     366           1 :   if (-1 == fee_cmp)
     367             :   {
     368           0 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     369             :                                             tl);
     370           0 :     *mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection,
     371             :                                                TALER_EC_REFUND_FEE_TOO_LOW,
     372             :                                                "refund_fee");
     373           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     374             :   }
     375           1 :   if (1 == fee_cmp)
     376             :   {
     377           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     378             :                 "Refund fee proposed by merchant is higher than necessary.\n");
     379             :   }
     380           1 :   TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     381             :                                           tl);
     382             : 
     383             :   /* Finally, store new refund data */
     384           1 :   qs = TEH_plugin->insert_refund (TEH_plugin->cls,
     385             :                                   session,
     386             :                                   refund);
     387           1 :   if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     388             :   {
     389           0 :     TALER_LOG_WARNING ("Failed to store /refund information in database\n");
     390           0 :     *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
     391             :                                                      TALER_EC_REFUND_STORE_DB_ERROR);
     392           0 :     return qs;
     393             :   }
     394             :   /* Success or soft failure */
     395           1 :   return qs;
     396             : }
     397             : 
     398             : 
     399             : /**
     400             :  * We have parsed the JSON information about the refund, do some basic
     401             :  * sanity checks (especially that the signature on the coin is valid)
     402             :  * and then execute the refund.  Note that we need the DB to check
     403             :  * the fee structure, so this is not done here.
     404             :  *
     405             :  * @param connection the MHD connection to handle
     406             :  * @param refund information about the refund
     407             :  * @return MHD result code
     408             :  */
     409             : static int
     410           2 : verify_and_execute_refund (struct MHD_Connection *connection,
     411             :                            const struct TALER_EXCHANGEDB_Refund *refund)
     412             : {
     413             :   struct TALER_RefundRequestPS rr;
     414             :   int mhd_ret;
     415             : 
     416           2 :   rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
     417           2 :   rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
     418           2 :   rr.h_contract_terms = refund->h_contract_terms;
     419           2 :   rr.coin_pub = refund->coin.coin_pub;
     420           2 :   rr.merchant = refund->merchant_pub;
     421           2 :   rr.rtransaction_id = GNUNET_htonll (refund->rtransaction_id);
     422           2 :   TALER_amount_hton (&rr.refund_amount,
     423             :                      &refund->refund_amount);
     424           2 :   TALER_amount_hton (&rr.refund_fee,
     425             :                      &refund->refund_fee);
     426           2 :   if (GNUNET_YES !=
     427           2 :       TALER_amount_cmp_currency (&refund->refund_amount,
     428             :                                  &refund->refund_fee) )
     429             :   {
     430           0 :     GNUNET_break_op (0);
     431           0 :     return TEH_RESPONSE_reply_arg_invalid (connection,
     432             :                                            TALER_EC_REFUND_FEE_CURRENCY_MISSMATCH,
     433             :                                            "refund_fee");
     434             :   }
     435           2 :   if (-1 == TALER_amount_cmp (&refund->refund_amount,
     436             :                               &refund->refund_fee) )
     437             :   {
     438           0 :     GNUNET_break_op (0);
     439           0 :     return TEH_RESPONSE_reply_arg_invalid (connection,
     440             :                                            TALER_EC_REFUND_FEE_ABOVE_AMOUNT,
     441             :                                            "refund_amount");
     442             :   }
     443           2 :   if (GNUNET_OK !=
     444           2 :       GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
     445             :                                   &rr.purpose,
     446             :                                   &refund->merchant_sig.eddsa_sig,
     447             :                                   &refund->merchant_pub.eddsa_pub))
     448             :   {
     449           0 :     TALER_LOG_WARNING ("Invalid signature on /refund request\n");
     450           0 :     return TEH_RESPONSE_reply_signature_invalid (connection,
     451             :                                                  TALER_EC_REFUND_MERCHANT_SIGNATURE_INVALID,
     452             :                                                  "merchant_sig");
     453             :   }
     454           2 :   if (GNUNET_OK !=
     455           2 :       TEH_DB_run_transaction (connection,
     456             :                               &mhd_ret,
     457             :                               &refund_transaction,
     458             :                               (void *) refund))
     459           1 :     return mhd_ret;
     460           1 :   return reply_refund_success (connection,
     461             :                                refund);
     462             : }
     463             : 
     464             : 
     465             : /**
     466             :  * Handle a "/refund" request.  Parses the JSON, and, if successful,
     467             :  * passes the JSON data to #verify_and_execute_refund() to
     468             :  * further check the details of the operation specified.  If
     469             :  * everything checks out, this will ultimately lead to the "/refund"
     470             :  * being executed, or rejected.
     471             :  *
     472             :  * @param rh context of the handler
     473             :  * @param connection the MHD connection to handle
     474             :  * @param[in,out] connection_cls the connection's closure (can be updated)
     475             :  * @param upload_data upload data
     476             :  * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
     477             :  * @return MHD result code
     478             :   */
     479             : int
     480           6 : TEH_REFUND_handler_refund (struct TEH_RequestHandler *rh,
     481             :                            struct MHD_Connection *connection,
     482             :                            void **connection_cls,
     483             :                            const char *upload_data,
     484             :                            size_t *upload_data_size)
     485             : {
     486             :   json_t *json;
     487             :   int res;
     488             :   struct TALER_EXCHANGEDB_Refund refund;
     489           6 :   struct GNUNET_JSON_Specification spec[] = {
     490             :     TALER_JSON_spec_amount ("refund_amount", &refund.refund_amount),
     491             :     TALER_JSON_spec_amount ("refund_fee", &refund.refund_fee),
     492             :     GNUNET_JSON_spec_fixed_auto ("h_contract_terms", &refund.h_contract_terms),
     493             :     GNUNET_JSON_spec_fixed_auto ("coin_pub", &refund.coin.coin_pub),
     494             :     GNUNET_JSON_spec_fixed_auto ("merchant_pub", &refund.merchant_pub),
     495             :     GNUNET_JSON_spec_uint64 ("rtransaction_id", &refund.rtransaction_id),
     496             :     GNUNET_JSON_spec_fixed_auto ("merchant_sig", &refund.merchant_sig),
     497             :     GNUNET_JSON_spec_end ()
     498             :   };
     499             : 
     500           6 :   res = TEH_PARSE_post_json (connection,
     501             :                              connection_cls,
     502             :                              upload_data,
     503             :                              upload_data_size,
     504             :                              &json);
     505           6 :   if (GNUNET_SYSERR == res)
     506           0 :     return MHD_NO;
     507           6 :   if ( (GNUNET_NO == res) || (NULL == json) )
     508           4 :     return MHD_YES;
     509           2 :   res = TEH_PARSE_json_data (connection,
     510             :                              json,
     511             :                              spec);
     512           2 :   json_decref (json);
     513           2 :   if (GNUNET_SYSERR == res)
     514           0 :     return MHD_NO; /* hard failure */
     515           2 :   if (GNUNET_NO == res)
     516           0 :     return MHD_YES; /* failure */
     517           2 :   res = verify_and_execute_refund (connection,
     518             :                                    &refund);
     519           2 :   GNUNET_JSON_parse_free (spec);
     520           2 :   return res;
     521             : }
     522             : 
     523             : 
     524             : /* end of taler-exchange-httpd_refund.c */

Generated by: LCOV version 1.13