LCOV - code coverage report
Current view: top level - lib - merchant_api_post_order_pay.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 0 282 0.0 %
Date: 2022-08-25 06:17:04 Functions: 0 7 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2021 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify
       6             :   it under the terms of the GNU Lesser General Public License as
       7             :   published by the Free Software Foundation; either version 2.1,
       8             :   or (at your option) any later version.
       9             : 
      10             :   TALER is distributed in the hope that it will be useful,
      11             :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :   GNU Lesser General Public License for more details.
      14             : 
      15             :   You should have received a copy of the GNU Lesser General
      16             :   Public License along with TALER; see the file COPYING.LGPL.
      17             :   If not, see <http://www.gnu.org/licenses/>
      18             : */
      19             : /**
      20             :  * @file merchant_api_post_order_pay.c
      21             :  * @brief Implementation of the POST /order/$ID/pay request
      22             :  *        of the merchant's HTTP API
      23             :  * @author Christian Grothoff
      24             :  * @author Marcello Stanisci
      25             :  */
      26             : #include "platform.h"
      27             : #include <curl/curl.h>
      28             : #include <jansson.h>
      29             : #include <microhttpd.h> /* just for HTTP status codes */
      30             : #include <gnunet/gnunet_util_lib.h>
      31             : #include <gnunet/gnunet_curl_lib.h>
      32             : #include "taler_merchant_service.h"
      33             : #include "merchant_api_curl_defaults.h"
      34             : #include <taler/taler_json_lib.h>
      35             : #include <taler/taler_signatures.h>
      36             : #include <taler/taler_exchange_service.h>
      37             : #include <taler/taler_curl_lib.h>
      38             : 
      39             : 
      40             : /**
      41             :  * @brief A Pay Handle
      42             :  */
      43             : struct TALER_MERCHANT_OrderPayHandle
      44             : {
      45             : 
      46             :   /**
      47             :    * The url for this request.
      48             :    */
      49             :   char *url;
      50             : 
      51             :   /**
      52             :    * Handle for the request.
      53             :    */
      54             :   struct GNUNET_CURL_Job *job;
      55             : 
      56             :   /**
      57             :    * Function to call with the result in "pay" @e mode.
      58             :    */
      59             :   TALER_MERCHANT_OrderPayCallback pay_cb;
      60             : 
      61             :   /**
      62             :    * Closure for @a pay_cb.
      63             :    */
      64             :   void *pay_cb_cls;
      65             : 
      66             :   /**
      67             :    * Reference to the execution context.
      68             :    */
      69             :   struct GNUNET_CURL_Context *ctx;
      70             : 
      71             :   /**
      72             :    * Minor context that holds body and headers.
      73             :    */
      74             :   struct TALER_CURL_PostContext post_ctx;
      75             : 
      76             :   /**
      77             :    * The coins we are paying with.
      78             :    */
      79             :   struct TALER_MERCHANT_PaidCoin *coins;
      80             : 
      81             :   /**
      82             :    * Hash of the contract we are paying, set
      83             :    * if @e am_wallet is true.
      84             :    */
      85             :   struct TALER_PrivateContractHashP h_contract_terms;
      86             : 
      87             :   /**
      88             :    * Public key of the merchant (instance) being paid, set
      89             :    * if @e am_wallet is true.
      90             :    */
      91             :   struct TALER_MerchantPublicKeyP merchant_pub;
      92             : 
      93             :   /**
      94             :    * JSON with the full reply, used during async
      95             :    * processing.
      96             :    */
      97             :   json_t *full_reply;
      98             : 
      99             :   /**
     100             :    * Pointer into @e coins array for the coin that
     101             :    * created a conflict (that we are checking).
     102             :    */
     103             :   const struct TALER_MERCHANT_PaidCoin *error_pc;
     104             : 
     105             :   /**
     106             :    * Coin history that proves a conflict.
     107             :    */
     108             :   json_t *error_history;
     109             : 
     110             :   /**
     111             :    * Handle to the exchange that issued a problematic
     112             :    * coin (if any).
     113             :    */
     114             :   struct TALER_EXCHANGE_Handle *exchange;
     115             : 
     116             :   /**
     117             :    * Number of @e coins we are paying with.
     118             :    */
     119             :   unsigned int num_coins;
     120             : 
     121             :   /**
     122             :    * Set to true if this is the wallet API and we have
     123             :    * initialized @e h_contract_terms and @e merchant_pub.
     124             :    */
     125             :   bool am_wallet;
     126             : 
     127             : };
     128             : 
     129             : 
     130             : /**
     131             :  * We got a 409 response back from the exchange (or the merchant).
     132             :  * Now we need to check the provided cryptographic proof that the
     133             :  * coin was actually already spent!
     134             :  *
     135             :  * @param oph operation handle
     136             :  * @param keys key data from the exchange
     137             :  * @return #GNUNET_OK if conflict is valid
     138             :  */
     139             : static enum GNUNET_GenericReturnValue
     140           0 : check_conflict (struct TALER_MERCHANT_OrderPayHandle *oph,
     141             :                 const struct TALER_EXCHANGE_Keys *keys)
     142             : {
     143             :   struct TALER_Amount spent;
     144             :   struct TALER_Amount spent_plus_contrib;
     145             :   struct TALER_DenominationHashP h_denom_pub_pc;
     146             :   const struct TALER_EXCHANGE_DenomPublicKey *dpk;
     147             : 
     148           0 :   TALER_denom_pub_hash (&oph->error_pc->denom_pub,
     149             :                         &h_denom_pub_pc);
     150           0 :   dpk = TALER_EXCHANGE_get_denomination_key_by_hash (
     151             :     keys,
     152             :     &h_denom_pub_pc);
     153           0 :   if (GNUNET_OK !=
     154           0 :       TALER_EXCHANGE_verify_coin_history (dpk,
     155           0 :                                           &oph->error_pc->coin_pub,
     156             :                                           oph->error_history,
     157             :                                           &spent))
     158             :   {
     159             :     /* Exchange's history fails to verify */
     160           0 :     GNUNET_break_op (0);
     161           0 :     return GNUNET_SYSERR;
     162             :   }
     163           0 :   if (0 >
     164           0 :       TALER_amount_add (&spent_plus_contrib,
     165             :                         &spent,
     166           0 :                         &oph->error_pc->amount_with_fee))
     167             :   {
     168             :     /* We got an integer overflow? Bad application! */
     169           0 :     GNUNET_break (0);
     170           0 :     return GNUNET_SYSERR;
     171             :   }
     172           0 :   if (-1 != TALER_amount_cmp (&oph->error_pc->denom_value,
     173             :                               &spent_plus_contrib))
     174             :   {
     175             :     /* according to our calculations, the transaction should
     176             :        have still worked, AND we did not get any proof of
     177             :        coin public key re-use; hence: exchange error! */
     178           0 :     GNUNET_break_op (0);
     179           0 :     return GNUNET_SYSERR;
     180             :   }
     181           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     182             :               "Accepting proof of double-spending (or coin public key re-use)\n");
     183           0 :   return GNUNET_OK;
     184             : }
     185             : 
     186             : 
     187             : /**
     188             :  * We got the fee structure from the exchange. Now
     189             :  * validate the conflict error.
     190             :  *
     191             :  * @param cls a `struct TALER_MERCHANT_OrderPayHandle`
     192             :  * @param ehr reply from the exchange
     193             :  * @param keys the key structure
     194             :  * @param compat protocol compatibility indication
     195             :  */
     196             : static void
     197           0 : cert_cb (void *cls,
     198             :          const struct TALER_EXCHANGE_HttpResponse *ehr,
     199             :          const struct TALER_EXCHANGE_Keys *keys,
     200             :          enum TALER_EXCHANGE_VersionCompatibility compat)
     201             : {
     202           0 :   struct TALER_MERCHANT_OrderPayHandle *oph = cls;
     203             : 
     204           0 :   if (TALER_EXCHANGE_VC_INCOMPATIBLE & compat)
     205             :   {
     206           0 :     struct TALER_MERCHANT_PayResponse pr = {
     207             :       .hr.http_status = MHD_HTTP_CONFLICT,
     208             :       .hr.exchange_http_status = 0,
     209             :       .hr.ec = TALER_EC_WALLET_EXCHANGE_PROTOCOL_VERSION_INCOMPATIBLE,
     210           0 :       .hr.reply = oph->full_reply,
     211           0 :       .hr.exchange_reply = ehr->reply,
     212             :       .hr.hint = "could not check error: incompatible exchange version"
     213             :     };
     214             : 
     215           0 :     oph->pay_cb (oph->pay_cb_cls,
     216             :                  &pr);
     217           0 :     TALER_MERCHANT_order_pay_cancel (oph);
     218           0 :     return;
     219             :   }
     220           0 :   if ( (MHD_HTTP_OK != ehr->http_status) ||
     221             :        (NULL == keys) )
     222             :   {
     223           0 :     struct TALER_MERCHANT_PayResponse pr = {
     224             :       .hr.http_status = MHD_HTTP_CONFLICT,
     225           0 :       .hr.exchange_http_status = ehr->http_status,
     226             :       .hr.ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
     227           0 :       .hr.reply = oph->full_reply,
     228           0 :       .hr.exchange_reply = ehr->reply,
     229             :       .hr.hint = "failed to download /keys from the exchange"
     230             :     };
     231             : 
     232           0 :     oph->pay_cb (oph->pay_cb_cls,
     233             :                  &pr);
     234           0 :     TALER_MERCHANT_order_pay_cancel (oph);
     235           0 :     return;
     236             :   }
     237             : 
     238           0 :   if (GNUNET_OK !=
     239           0 :       check_conflict (oph,
     240             :                       keys))
     241             :   {
     242           0 :     struct TALER_MERCHANT_PayResponse pr = {
     243             :       .hr.http_status = 0,
     244             :       .hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE,
     245           0 :       .hr.reply = oph->full_reply
     246             :     };
     247             : 
     248           0 :     oph->pay_cb (oph->pay_cb_cls,
     249             :                  &pr);
     250           0 :     TALER_MERCHANT_order_pay_cancel (oph);
     251           0 :     return;
     252             :   }
     253             : 
     254             :   {
     255           0 :     struct TALER_MERCHANT_PayResponse pr = {
     256             :       .hr.http_status = MHD_HTTP_CONFLICT,
     257           0 :       .hr.ec = TALER_JSON_get_error_code (oph->full_reply),
     258           0 :       .hr.reply = oph->full_reply
     259             :     };
     260             : 
     261           0 :     oph->pay_cb (oph->pay_cb_cls,
     262             :                  &pr);
     263           0 :     TALER_MERCHANT_order_pay_cancel (oph);
     264             :   }
     265             : }
     266             : 
     267             : 
     268             : /**
     269             :  * We got a 409 response back from the exchange (or the merchant).
     270             :  * Now we need to check the provided cryptograophic proof that the
     271             :  * coin was actually already spent!
     272             :  *
     273             :  * @param[in,out] oph handle of the original pay operation
     274             :  * @param[in,out] pr response to modify if #GNUNET_OK is returned
     275             :  * @param json cryptograophic proof returned by the
     276             :  *        exchange/merchant
     277             :  * @return #GNUNET_OK if proof checks out,
     278             :  *         #GNUNET_SYSERR if it is wrong,
     279             :  *         #GNUNET_NO if checking continues asynchronously
     280             :  */
     281             : static enum GNUNET_GenericReturnValue
     282           0 : parse_conflict (struct TALER_MERCHANT_OrderPayHandle *oph,
     283             :                 struct TALER_MERCHANT_PayResponse *pr,
     284             :                 const json_t *json)
     285             : {
     286             :   json_t *ereply;
     287             :   const char *exchange_url;
     288             :   struct GNUNET_JSON_Specification spec[] = {
     289           0 :     GNUNET_JSON_spec_json ("exchange_reply",
     290             :                            &ereply),
     291           0 :     GNUNET_JSON_spec_string ("exchange_url",
     292             :                              &exchange_url),
     293           0 :     GNUNET_JSON_spec_end ()
     294             :   };
     295             :   struct TALER_CoinSpendPublicKeyP coin_pub;
     296             :   struct GNUNET_JSON_Specification hspec[] = {
     297           0 :     GNUNET_JSON_spec_json ("history",
     298             :                            &oph->error_history),
     299           0 :     GNUNET_JSON_spec_fixed_auto ("coin_pub",
     300             :                                  &coin_pub),
     301           0 :     GNUNET_JSON_spec_end ()
     302             :   };
     303           0 :   enum TALER_ErrorCode ec = TALER_JSON_get_error_code (json);
     304             : 
     305           0 :   switch (ec)
     306             :   {
     307           0 :   case TALER_EC_GENERIC_CURRENCY_MISMATCH:
     308             :     /* no proof to check, still very strange, as we
     309             :        should have checked that the currency matches */
     310           0 :     GNUNET_break_op (0);
     311           0 :     TALER_MERCHANT_parse_error_details_ (json,
     312             :                                          MHD_HTTP_CONFLICT,
     313             :                                          &pr->hr);
     314           0 :     return GNUNET_OK;
     315           0 :   case TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_ALREADY_PAID:
     316             :     /* We can only be happy and accept the result;
     317             :        FIXME: parse the refunds... */
     318           0 :     TALER_MERCHANT_parse_error_details_ (json,
     319             :                                          MHD_HTTP_CONFLICT,
     320             :                                          &pr->hr);
     321           0 :     return GNUNET_OK;
     322           0 :   case TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS:
     323             :     /* main case, handled below */
     324           0 :     break;
     325           0 :   default:
     326           0 :     GNUNET_break_op (0);
     327           0 :     return GNUNET_SYSERR;
     328             :   }
     329             : 
     330           0 :   if (GNUNET_OK !=
     331           0 :       GNUNET_JSON_parse (json,
     332             :                          spec,
     333             :                          NULL, NULL))
     334             :   {
     335           0 :     GNUNET_break_op (0);
     336           0 :     return GNUNET_SYSERR;
     337             :   }
     338           0 :   if (GNUNET_OK !=
     339           0 :       GNUNET_JSON_parse (ereply,
     340             :                          hspec,
     341             :                          NULL, NULL))
     342             :   {
     343           0 :     GNUNET_break_op (0);
     344           0 :     GNUNET_JSON_parse_free (spec);
     345           0 :     return GNUNET_SYSERR;
     346             :   }
     347           0 :   GNUNET_JSON_parse_free (spec);
     348             : 
     349           0 :   for (unsigned int i = 0; i<oph->num_coins; i++)
     350             :   {
     351           0 :     if (0 ==
     352           0 :         GNUNET_memcmp (&oph->coins[i].coin_pub,
     353             :                        &coin_pub))
     354             :     {
     355           0 :       oph->error_pc = &oph->coins[i];
     356           0 :       oph->full_reply = json_incref ((json_t *) json);
     357           0 :       oph->exchange = TALER_EXCHANGE_connect (oph->ctx,
     358           0 :                                               oph->error_pc->exchange_url,
     359             :                                               &cert_cb,
     360             :                                               oph,
     361             :                                               TALER_EXCHANGE_OPTION_END);
     362           0 :       return GNUNET_NO;
     363             :     }
     364             :   }
     365           0 :   GNUNET_break_op (0); /* complaint is not about any of the coins
     366             :                           that we actually paid with... */
     367           0 :   GNUNET_JSON_parse_free (hspec);
     368           0 :   return GNUNET_SYSERR;
     369             : }
     370             : 
     371             : 
     372             : /**
     373             :  * Function called when we're done processing the
     374             :  * HTTP /pay request.
     375             :  *
     376             :  * @param cls the `struct TALER_MERCHANT_Pay`
     377             :  * @param response_code HTTP response code, 0 on error
     378             :  * @param response response body, NULL if not in JSON
     379             :  */
     380             : static void
     381           0 : handle_pay_finished (void *cls,
     382             :                      long response_code,
     383             :                      const void *response)
     384             : {
     385           0 :   struct TALER_MERCHANT_OrderPayHandle *oph = cls;
     386           0 :   const json_t *json = response;
     387           0 :   struct TALER_MERCHANT_PayResponse pr = {
     388           0 :     .hr.http_status = (unsigned int) response_code,
     389             :     .hr.reply = json
     390             :   };
     391             : 
     392           0 :   oph->job = NULL;
     393           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     394             :               "/pay completed with response code %u\n",
     395             :               (unsigned int) response_code);
     396           0 :   switch (response_code)
     397             :   {
     398           0 :   case 0:
     399           0 :     pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     400           0 :     break;
     401           0 :   case MHD_HTTP_OK:
     402           0 :     if (oph->am_wallet)
     403             :     {
     404             :       /* Here we can (and should) verify the merchant's signature */
     405             :       struct GNUNET_JSON_Specification spec[] = {
     406           0 :         GNUNET_JSON_spec_fixed_auto (
     407             :           "sig",
     408             :           &pr.details.success.merchant_sig),
     409           0 :         GNUNET_JSON_spec_end ()
     410             :       };
     411             : 
     412           0 :       if (GNUNET_OK !=
     413           0 :           GNUNET_JSON_parse (json,
     414             :                              spec,
     415             :                              NULL, NULL))
     416             :       {
     417           0 :         GNUNET_break_op (0);
     418           0 :         pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     419           0 :         pr.hr.http_status = 0;
     420           0 :         pr.hr.hint = "sig field missing in response";
     421           0 :         break;
     422             :       }
     423             : 
     424           0 :       if (GNUNET_OK !=
     425           0 :           TALER_merchant_pay_verify (&oph->h_contract_terms,
     426           0 :                                      &oph->merchant_pub,
     427             :                                      &pr.details.success.merchant_sig))
     428             :       {
     429           0 :         GNUNET_break_op (0);
     430           0 :         pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     431           0 :         pr.hr.http_status = 0;
     432           0 :         pr.hr.hint = "signature invalid";
     433             :       }
     434             :     }
     435           0 :     break;
     436             :   /* Tolerating Not Acceptable because sometimes
     437             :    * - especially in tests - we might want to POST
     438             :    * coins one at a time.  */
     439           0 :   case MHD_HTTP_NOT_ACCEPTABLE:
     440           0 :     pr.hr.ec = TALER_JSON_get_error_code (json);
     441           0 :     pr.hr.hint = TALER_JSON_get_error_hint (json);
     442           0 :     break;
     443           0 :   case MHD_HTTP_BAD_REQUEST:
     444           0 :     pr.hr.ec = TALER_JSON_get_error_code (json);
     445           0 :     pr.hr.hint = TALER_JSON_get_error_hint (json);
     446             :     /* This should never happen, either us
     447             :      * or the merchant is buggy (or API version conflict);
     448             :      * just pass JSON reply to the application */
     449           0 :     break;
     450           0 :   case MHD_HTTP_PAYMENT_REQUIRED:
     451             :     /* was originally paid, but then refunded */
     452           0 :     pr.hr.ec = TALER_JSON_get_error_code (json);
     453           0 :     pr.hr.hint = TALER_JSON_get_error_hint (json);
     454           0 :     break;
     455           0 :   case MHD_HTTP_FORBIDDEN:
     456           0 :     pr.hr.ec = TALER_JSON_get_error_code (json);
     457           0 :     pr.hr.hint = TALER_JSON_get_error_hint (json);
     458             :     /* Nothing really to verify, merchant says we tried to abort the payment
     459             :      * after it was successful. We should pass the JSON reply to the
     460             :      * application */
     461           0 :     break;
     462           0 :   case MHD_HTTP_NOT_FOUND:
     463           0 :     pr.hr.ec = TALER_JSON_get_error_code (json);
     464           0 :     pr.hr.hint = TALER_JSON_get_error_hint (json);
     465             :     /* Nothing really to verify, this should never
     466             :        happen, we should pass the JSON reply to the
     467             :        application */
     468           0 :     break;
     469           0 :   case MHD_HTTP_REQUEST_TIMEOUT:
     470           0 :     pr.hr.ec = TALER_JSON_get_error_code (json);
     471           0 :     pr.hr.hint = TALER_JSON_get_error_hint (json);
     472             :     /* The merchant couldn't generate a timely response, likely because
     473             :        it itself waited too long on the exchange.
     474             :        Pass on to application. */
     475           0 :     break;
     476           0 :   case MHD_HTTP_CONFLICT:
     477             :     {
     478             :       enum GNUNET_GenericReturnValue ret;
     479             : 
     480           0 :       ret = parse_conflict (oph,
     481             :                             &pr,
     482             :                             json);
     483             :       switch (ret)
     484             :       {
     485           0 :       case GNUNET_OK:
     486             :         /* continued below, 'pr' was modified */
     487           0 :         break;
     488           0 :       case GNUNET_NO:
     489             :         /* handled asynchronously! */
     490           0 :         return; /* ! */
     491           0 :       case GNUNET_SYSERR:
     492           0 :         GNUNET_break_op (0);
     493           0 :         response_code = 0;
     494           0 :         break;
     495             :       }
     496           0 :       break;
     497             :     }
     498           0 :   case MHD_HTTP_GONE:
     499           0 :     TALER_MERCHANT_parse_error_details_ (json,
     500             :                                          response_code,
     501             :                                          &pr.hr);
     502             :     /* The merchant says we are too late, the offer has expired or some
     503             :        denomination key of a coin involved has expired.
     504             :        Might be a disagreement in timestamps? Still, pass on to application. */
     505           0 :     break;
     506           0 :   case MHD_HTTP_PRECONDITION_FAILED:
     507           0 :     TALER_MERCHANT_parse_error_details_ (json,
     508             :                                          response_code,
     509             :                                          &pr.hr);
     510             :     /* Nothing really to verify, the merchant is blaming us for failing to
     511             :        satisfy some constraint (likely it does not like our exchange because
     512             :        of some disagreement on the PKI).  We should pass the JSON reply to the
     513             :        application */
     514           0 :     break;
     515           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     516           0 :     TALER_MERCHANT_parse_error_details_ (json,
     517             :                                          response_code,
     518             :                                          &pr.hr);
     519             :     /* Server had an internal issue; we should retry,
     520             :        but this API leaves this to the application */
     521           0 :     break;
     522           0 :   case MHD_HTTP_BAD_GATEWAY:
     523             :     /* Nothing really to verify, the merchant is blaming the exchange.
     524             :        We should pass the JSON reply to the application */
     525           0 :     TALER_MERCHANT_parse_error_details_ (json,
     526             :                                          response_code,
     527             :                                          &pr.hr);
     528           0 :     break;
     529           0 :   case MHD_HTTP_SERVICE_UNAVAILABLE:
     530           0 :     TALER_MERCHANT_parse_error_details_ (json,
     531             :                                          response_code,
     532             :                                          &pr.hr);
     533             :     /* Exchange couldn't respond properly; the retry is
     534             :        left to the application */
     535           0 :     break;
     536           0 :   case MHD_HTTP_GATEWAY_TIMEOUT:
     537           0 :     TALER_MERCHANT_parse_error_details_ (json,
     538             :                                          response_code,
     539             :                                          &pr.hr);
     540             :     /* Exchange couldn't respond in a timely fashion;
     541             :        the retry is left to the application */
     542           0 :     break;
     543           0 :   default:
     544           0 :     TALER_MERCHANT_parse_error_details_ (json,
     545             :                                          response_code,
     546             :                                          &pr.hr);
     547             :     /* unexpected response code */
     548           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     549             :                 "Unexpected response code %u/%d\n",
     550             :                 (unsigned int) response_code,
     551             :                 (int) pr.hr.ec);
     552           0 :     GNUNET_break_op (0);
     553           0 :     break;
     554             :   }
     555           0 :   oph->pay_cb (oph->pay_cb_cls,
     556             :                &pr);
     557           0 :   TALER_MERCHANT_order_pay_cancel (oph);
     558             : }
     559             : 
     560             : 
     561             : struct TALER_MERCHANT_OrderPayHandle *
     562           0 : TALER_MERCHANT_order_pay_frontend (
     563             :   struct GNUNET_CURL_Context *ctx,
     564             :   const char *merchant_url,
     565             :   const char *order_id,
     566             :   const char *session_id,
     567             :   unsigned int num_coins,
     568             :   const struct TALER_MERCHANT_PaidCoin coins[],
     569             :   TALER_MERCHANT_OrderPayCallback pay_cb,
     570             :   void *pay_cb_cls)
     571             : {
     572             :   struct TALER_MERCHANT_OrderPayHandle *oph;
     573             :   json_t *pay_obj;
     574             :   json_t *j_coins;
     575             :   CURL *eh;
     576             :   struct TALER_Amount total_fee;
     577             :   struct TALER_Amount total_amount;
     578             : 
     579           0 :   if (0 == num_coins)
     580             :   {
     581           0 :     GNUNET_break (0);
     582           0 :     return NULL;
     583             :   }
     584           0 :   j_coins = json_array ();
     585           0 :   GNUNET_assert (NULL != j_coins);
     586           0 :   for (unsigned int i = 0; i<num_coins; i++)
     587             :   {
     588             :     json_t *j_coin;
     589           0 :     const struct TALER_MERCHANT_PaidCoin *pc = &coins[i];
     590             :     struct TALER_Amount fee;
     591             :     struct TALER_DenominationHashP denom_hash;
     592             : 
     593           0 :     if (0 >
     594           0 :         TALER_amount_subtract (&fee,
     595             :                                &pc->amount_with_fee,
     596             :                                &pc->amount_without_fee))
     597             :     {
     598             :       /* Integer underflow, fee larger than total amount?
     599             :          This should not happen (client violated API!) */
     600           0 :       GNUNET_break (0);
     601           0 :       json_decref (j_coins);
     602           0 :       return NULL;
     603             :     }
     604           0 :     if (0 == i)
     605             :     {
     606           0 :       total_fee = fee;
     607           0 :       total_amount = pc->amount_with_fee;
     608             :     }
     609             :     else
     610             :     {
     611           0 :       if ( (0 >
     612           0 :             TALER_amount_add (&total_fee,
     613             :                               &total_fee,
     614           0 :                               &fee)) ||
     615             :            (0 >
     616           0 :             TALER_amount_add (&total_amount,
     617             :                               &total_amount,
     618             :                               &pc->amount_with_fee)) )
     619             :       {
     620             :         /* integer overflow */
     621           0 :         GNUNET_break (0);
     622           0 :         json_decref (j_coins);
     623           0 :         return NULL;
     624             :       }
     625             :     }
     626             : 
     627           0 :     TALER_denom_pub_hash (&pc->denom_pub,
     628             :                           &denom_hash);
     629             :     /* create JSON for this coin */
     630           0 :     j_coin = GNUNET_JSON_PACK (
     631             :       TALER_JSON_pack_amount ("contribution",
     632             :                               &pc->amount_with_fee),
     633             :       GNUNET_JSON_pack_data_auto ("coin_pub",
     634             :                                   &pc->coin_pub),
     635             :       GNUNET_JSON_pack_string ("exchange_url",
     636             :                                pc->exchange_url),
     637             :       GNUNET_JSON_pack_data_auto ("h_denom",
     638             :                                   &denom_hash),
     639             :       TALER_JSON_pack_denom_sig ("ub_sig",
     640             :                                  &pc->denom_sig),
     641             :       GNUNET_JSON_pack_data_auto ("coin_sig",
     642             :                                   &pc->coin_sig));
     643           0 :     if (0 !=
     644           0 :         json_array_append_new (j_coins,
     645             :                                j_coin))
     646             :     {
     647           0 :       GNUNET_break (0);
     648           0 :       json_decref (j_coins);
     649           0 :       return NULL;
     650             :     }
     651             :   }
     652             : 
     653           0 :   pay_obj = GNUNET_JSON_PACK (
     654             :     GNUNET_JSON_pack_array_steal ("coins",
     655             :                                   j_coins),
     656             :     GNUNET_JSON_pack_allow_null (
     657             :       GNUNET_JSON_pack_string ("session_id",
     658             :                                session_id)));
     659             : 
     660           0 :   oph = GNUNET_new (struct TALER_MERCHANT_OrderPayHandle);
     661           0 :   oph->ctx = ctx;
     662           0 :   oph->pay_cb = pay_cb;
     663           0 :   oph->pay_cb_cls = pay_cb_cls;
     664             :   {
     665             :     char *path;
     666             : 
     667           0 :     GNUNET_asprintf (&path,
     668             :                      "orders/%s/pay",
     669             :                      order_id);
     670           0 :     oph->url = TALER_url_join (merchant_url,
     671             :                                path,
     672             :                                NULL);
     673           0 :     GNUNET_free (path);
     674             :   }
     675           0 :   if (NULL == oph->url)
     676             :   {
     677           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     678             :                 "Could not construct request URL.\n");
     679           0 :     json_decref (pay_obj);
     680           0 :     GNUNET_free (oph);
     681           0 :     return NULL;
     682             :   }
     683           0 :   oph->num_coins = num_coins;
     684           0 :   oph->coins = GNUNET_new_array (num_coins,
     685             :                                  struct TALER_MERCHANT_PaidCoin);
     686           0 :   memcpy (oph->coins,
     687             :           coins,
     688             :           num_coins * sizeof (struct TALER_MERCHANT_PaidCoin));
     689             : 
     690           0 :   eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
     691           0 :   if (GNUNET_OK !=
     692           0 :       TALER_curl_easy_post (&oph->post_ctx,
     693             :                             eh,
     694             :                             pay_obj))
     695             :   {
     696           0 :     GNUNET_break (0);
     697           0 :     curl_easy_cleanup (eh);
     698           0 :     json_decref (pay_obj);
     699           0 :     GNUNET_free (oph->url);
     700           0 :     GNUNET_free (oph);
     701           0 :     return NULL;
     702             :   }
     703           0 :   json_decref (pay_obj);
     704           0 :   oph->job = GNUNET_CURL_job_add2 (ctx,
     705             :                                    eh,
     706           0 :                                    oph->post_ctx.headers,
     707             :                                    &handle_pay_finished,
     708             :                                    oph);
     709           0 :   return oph;
     710             : }
     711             : 
     712             : 
     713             : struct TALER_MERCHANT_OrderPayHandle *
     714           0 : TALER_MERCHANT_order_pay (
     715             :   struct GNUNET_CURL_Context *ctx,
     716             :   const char *merchant_url,
     717             :   const char *session_id,
     718             :   const struct TALER_PrivateContractHashP *h_contract_terms,
     719             :   const struct TALER_Amount *amount,
     720             :   const struct TALER_Amount *max_fee,
     721             :   const struct TALER_MerchantPublicKeyP *merchant_pub,
     722             :   const struct TALER_MerchantSignatureP *merchant_sig,
     723             :   struct GNUNET_TIME_Timestamp timestamp,
     724             :   struct GNUNET_TIME_Timestamp refund_deadline,
     725             :   struct GNUNET_TIME_Timestamp pay_deadline,
     726             :   const struct TALER_MerchantWireHashP *h_wire,
     727             :   const char *order_id,
     728             :   unsigned int num_coins,
     729             :   const struct TALER_MERCHANT_PayCoin coins[],
     730             :   TALER_MERCHANT_OrderPayCallback pay_cb,
     731             :   void *pay_cb_cls)
     732             : {
     733           0 :   if (GNUNET_YES !=
     734           0 :       TALER_amount_cmp_currency (amount,
     735             :                                  max_fee))
     736             :   {
     737           0 :     GNUNET_break (0);
     738           0 :     return NULL;
     739             :   }
     740             : 
     741           0 :   {
     742           0 :     struct TALER_MERCHANT_PaidCoin pc[num_coins];
     743             : 
     744           0 :     for (unsigned int i = 0; i<num_coins; i++)
     745             :     {
     746           0 :       const struct TALER_MERCHANT_PayCoin *coin = &coins[i]; // coin priv.
     747           0 :       struct TALER_MERCHANT_PaidCoin *p = &pc[i]; // coin pub.
     748             :       struct TALER_Amount fee;
     749             :       struct TALER_DenominationHashP h_denom_pub;
     750             : 
     751           0 :       if (0 >
     752           0 :           TALER_amount_subtract (&fee,
     753             :                                  &coin->amount_with_fee,
     754             :                                  &coin->amount_without_fee))
     755             :       {
     756             :         /* Integer underflow, fee larger than total amount?
     757             :            This should not happen (client violated API!) */
     758           0 :         GNUNET_break (0);
     759           0 :         return NULL;
     760             :       }
     761           0 :       TALER_denom_pub_hash (&coin->denom_pub,
     762             :                             &h_denom_pub);
     763           0 :       TALER_wallet_deposit_sign (&coin->amount_with_fee,
     764             :                                  &fee,
     765             :                                  h_wire,
     766             :                                  h_contract_terms,
     767             :                                  coin->h_age_commitment,
     768             :                                  NULL /* h_extensions! */,
     769             :                                  &h_denom_pub,
     770             :                                  timestamp,
     771             :                                  merchant_pub,
     772             :                                  refund_deadline,
     773             :                                  &coin->coin_priv,
     774             :                                  &p->coin_sig);
     775           0 :       p->denom_pub = coin->denom_pub;
     776           0 :       p->denom_sig = coin->denom_sig;
     777           0 :       p->denom_value = coin->denom_value;
     778           0 :       GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv,
     779             :                                           &p->coin_pub.eddsa_pub);
     780           0 :       p->amount_with_fee = coin->amount_with_fee;
     781           0 :       p->amount_without_fee = coin->amount_without_fee;
     782           0 :       p->exchange_url = coin->exchange_url;
     783             :     }
     784             :     {
     785             :       struct TALER_MERCHANT_OrderPayHandle *oph;
     786             : 
     787           0 :       oph = TALER_MERCHANT_order_pay_frontend (ctx,
     788             :                                                merchant_url,
     789             :                                                order_id,
     790             :                                                session_id,
     791             :                                                num_coins,
     792             :                                                pc,
     793             :                                                pay_cb,
     794             :                                                pay_cb_cls);
     795           0 :       if (NULL == oph)
     796           0 :         return NULL;
     797           0 :       oph->h_contract_terms = *h_contract_terms;
     798           0 :       oph->merchant_pub = *merchant_pub;
     799           0 :       oph->am_wallet = true;
     800           0 :       return oph;
     801             :     }
     802             :   }
     803             : }
     804             : 
     805             : 
     806             : void
     807           0 : TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *oph)
     808             : {
     809           0 :   if (NULL != oph->job)
     810             :   {
     811           0 :     GNUNET_CURL_job_cancel (oph->job);
     812           0 :     oph->job = NULL;
     813             :   }
     814           0 :   if (NULL != oph->exchange)
     815             :   {
     816           0 :     TALER_EXCHANGE_disconnect (oph->exchange);
     817           0 :     oph->exchange = NULL;
     818             :   }
     819           0 :   TALER_curl_easy_post_finished (&oph->post_ctx);
     820           0 :   json_decref (oph->error_history);
     821           0 :   json_decref (oph->full_reply);
     822           0 :   GNUNET_free (oph->coins);
     823           0 :   GNUNET_free (oph->url);
     824           0 :   GNUNET_free (oph);
     825           0 : }
     826             : 
     827             : 
     828             : /* end of merchant_api_post_order_pay.c */

Generated by: LCOV version 1.14