LCOV - code coverage report
Current view: top level - lib - merchant_api_post_order_pay.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 151 278 54.3 %
Date: 2025-06-23 16:22:09 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2023 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 <gnunet/gnunet_common.h>
      29             : #include <gnunet/gnunet_json_lib.h>
      30             : #include <jansson.h>
      31             : #include <microhttpd.h> /* just for HTTP status codes */
      32             : #include <gnunet/gnunet_util_lib.h>
      33             : #include <gnunet/gnunet_curl_lib.h>
      34             : #include "taler_merchant_service.h"
      35             : #include "merchant_api_common.h"
      36             : #include "merchant_api_curl_defaults.h"
      37             : #include <stdio.h>
      38             : #include <taler/taler_json_lib.h>
      39             : #include <taler/taler_signatures.h>
      40             : #include <taler/taler_exchange_service.h>
      41             : #include <taler/taler_curl_lib.h>
      42             : 
      43             : 
      44             : /**
      45             :  * @brief A Pay Handle
      46             :  */
      47             : struct TALER_MERCHANT_OrderPayHandle
      48             : {
      49             : 
      50             :   /**
      51             :    * The url for this request.
      52             :    */
      53             :   char *url;
      54             : 
      55             :   /**
      56             :    * Handle for the request.
      57             :    */
      58             :   struct GNUNET_CURL_Job *job;
      59             : 
      60             :   /**
      61             :    * Function to call with the result in "pay" @e mode.
      62             :    */
      63             :   TALER_MERCHANT_OrderPayCallback pay_cb;
      64             : 
      65             :   /**
      66             :    * Closure for @a pay_cb.
      67             :    */
      68             :   void *pay_cb_cls;
      69             : 
      70             :   /**
      71             :    * Reference to the execution context.
      72             :    */
      73             :   struct GNUNET_CURL_Context *ctx;
      74             : 
      75             :   /**
      76             :    * Minor context that holds body and headers.
      77             :    */
      78             :   struct TALER_CURL_PostContext post_ctx;
      79             : 
      80             :   /**
      81             :    * The coins we are paying with.
      82             :    */
      83             :   struct TALER_MERCHANT_PaidCoin *coins;
      84             : 
      85             :   /**
      86             :    * Hash of the contract we are paying, set
      87             :    * if @e am_wallet is true.
      88             :    */
      89             :   struct TALER_PrivateContractHashP h_contract_terms;
      90             : 
      91             :   /**
      92             :    * Public key of the merchant (instance) being paid, set
      93             :    * if @e am_wallet is true.
      94             :    */
      95             :   struct TALER_MerchantPublicKeyP merchant_pub;
      96             : 
      97             :   /**
      98             :    * JSON with the full reply, used during async
      99             :    * processing.
     100             :    */
     101             :   json_t *full_reply;
     102             : 
     103             :   /**
     104             :    * Pointer into @e coins array for the coin that
     105             :    * created a conflict (that we are checking).
     106             :    */
     107             :   const struct TALER_MERCHANT_PaidCoin *error_pc;
     108             : 
     109             :   /**
     110             :    * Coin history that proves a conflict.
     111             :    */
     112             :   json_t *error_history;
     113             : 
     114             :   /**
     115             :    * Number of @e coins we are paying with.
     116             :    */
     117             :   unsigned int num_coins;
     118             : 
     119             :   /**
     120             :    * Set to true if this is the wallet API and we have
     121             :    * initialized @e h_contract_terms and @e merchant_pub.
     122             :    */
     123             :   bool am_wallet;
     124             : 
     125             : };
     126             : 
     127             : 
     128             : /**
     129             :  * Parse blindly signed output tokens from response.
     130             :  *
     131             :  * @param token_sigs the JSON array with the token signatures. Can be NULL.
     132             :  * @param tokens where to store the parsed tokens.
     133             :  * @param num_tokens where to store the length of the @a tokens array.
     134             :  */
     135             : static enum GNUNET_GenericReturnValue
     136          22 : parse_tokens (const json_t *token_sigs,
     137             :               struct TALER_MERCHANT_OutputToken **tokens,
     138             :               unsigned int *num_tokens)
     139             : {
     140          22 :   GNUNET_array_grow (*tokens,
     141             :                      *num_tokens,
     142             :                      json_array_size (token_sigs));
     143             : 
     144          26 :   for (unsigned int i = 0; i<(*num_tokens); i++)
     145             :   {
     146           4 :     struct TALER_MERCHANT_OutputToken *token = &(*tokens)[i];
     147             :     struct GNUNET_JSON_Specification spec[] = {
     148           4 :       TALER_JSON_spec_blinded_token_issue_sig ("blind_sig",
     149             :                                                &token->blinded_sig),
     150           4 :       GNUNET_JSON_spec_end ()
     151             :     };
     152             :     const json_t *jtoken
     153           4 :       = json_array_get (token_sigs,
     154             :                         i);
     155             : 
     156           4 :     if (NULL == jtoken)
     157             :     {
     158           0 :       GNUNET_break (0);
     159           0 :       return GNUNET_SYSERR;
     160             :     }
     161           4 :     if (GNUNET_OK !=
     162           4 :         GNUNET_JSON_parse (jtoken,
     163             :                            spec,
     164             :                            NULL, NULL))
     165             :     {
     166           0 :       GNUNET_break (0);
     167           0 :       return GNUNET_SYSERR;
     168             :     }
     169             :   }
     170             : 
     171          22 :   return GNUNET_YES;
     172             : }
     173             : 
     174             : 
     175             : /**
     176             :  * Function called when we're done processing the
     177             :  * HTTP /pay request.
     178             :  *
     179             :  * @param cls the `struct TALER_MERCHANT_Pay`
     180             :  * @param response_code HTTP response code, 0 on error
     181             :  * @param response response body, NULL if not in JSON
     182             :  */
     183             : static void
     184          34 : handle_pay_finished (void *cls,
     185             :                      long response_code,
     186             :                      const void *response)
     187             : {
     188          34 :   struct TALER_MERCHANT_OrderPayHandle *oph = cls;
     189          34 :   const json_t *json = response;
     190          34 :   struct TALER_MERCHANT_PayResponse pr = {
     191          34 :     .hr.http_status = (unsigned int) response_code,
     192             :     .hr.reply = json
     193             :   };
     194             : 
     195          34 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     196             :               "Received /pay response with status code %u\n",
     197             :               (unsigned int) response_code);
     198             : 
     199          34 :   json_dumpf (json,
     200             :               stderr,
     201             :               JSON_INDENT (2));
     202             : 
     203          34 :   oph->job = NULL;
     204          34 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     205             :               "/pay completed with response code %u\n",
     206             :               (unsigned int) response_code);
     207          34 :   switch (response_code)
     208             :   {
     209           0 :   case 0:
     210           0 :     pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     211           0 :     break;
     212          22 :   case MHD_HTTP_OK:
     213          22 :     if (oph->am_wallet)
     214             :     {
     215          22 :       const json_t *token_sigs = NULL;
     216             :       struct GNUNET_JSON_Specification spec[] = {
     217          22 :         GNUNET_JSON_spec_fixed_auto ("sig",
     218             :                                      &pr.details.ok.merchant_sig),
     219          22 :         GNUNET_JSON_spec_mark_optional (
     220             :           GNUNET_JSON_spec_string ("pos_confirmation",
     221             :                                    &pr.details.ok.pos_confirmation),
     222             :           NULL),
     223          22 :         GNUNET_JSON_spec_mark_optional (
     224             :           GNUNET_JSON_spec_array_const ("token_sigs",
     225             :                                         &token_sigs),
     226             :           NULL),
     227          22 :         GNUNET_JSON_spec_end ()
     228             :       };
     229             : 
     230          22 :       if (GNUNET_OK !=
     231          22 :           GNUNET_JSON_parse (json,
     232             :                              spec,
     233             :                              NULL, NULL))
     234             :       {
     235           0 :         GNUNET_break_op (0);
     236           0 :         pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     237           0 :         pr.hr.http_status = 0;
     238           0 :         pr.hr.hint = "sig field missing in response";
     239           0 :         break;
     240             :       }
     241             : 
     242          22 :       if (GNUNET_OK !=
     243          22 :           parse_tokens (token_sigs,
     244             :                         &pr.details.ok.tokens,
     245             :                         &pr.details.ok.num_tokens))
     246             :       {
     247           0 :         GNUNET_break_op (0);
     248           0 :         pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     249           0 :         pr.hr.http_status = 0;
     250           0 :         pr.hr.hint = "failed to parse token_sigs field in response";
     251           0 :         break;
     252             :       }
     253             : 
     254          22 :       if (GNUNET_OK !=
     255          22 :           TALER_merchant_pay_verify (&oph->h_contract_terms,
     256          22 :                                      &oph->merchant_pub,
     257             :                                      &pr.details.ok.merchant_sig))
     258             :       {
     259           0 :         GNUNET_break_op (0);
     260           0 :         pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     261           0 :         pr.hr.http_status = 0;
     262           0 :         pr.hr.hint = "signature invalid";
     263             :       }
     264             :     }
     265          22 :     break;
     266             :   /* Tolerating Not Acceptable because sometimes
     267             :    * - especially in tests - we might want to POST
     268             :    * coins one at a time.  */
     269           0 :   case MHD_HTTP_NOT_ACCEPTABLE:
     270           0 :     pr.hr.ec = TALER_JSON_get_error_code (json);
     271           0 :     pr.hr.hint = TALER_JSON_get_error_hint (json);
     272           0 :     break;
     273           2 :   case MHD_HTTP_BAD_REQUEST:
     274           2 :     pr.hr.ec = TALER_JSON_get_error_code (json);
     275           2 :     pr.hr.hint = TALER_JSON_get_error_hint (json);
     276             :     /* This should never happen, either us
     277             :      * or the merchant is buggy (or API version conflict);
     278             :      * just pass JSON reply to the application */
     279           2 :     break;
     280           0 :   case MHD_HTTP_PAYMENT_REQUIRED:
     281             :     /* was originally paid, but then refunded */
     282           0 :     pr.hr.ec = TALER_JSON_get_error_code (json);
     283           0 :     pr.hr.hint = TALER_JSON_get_error_hint (json);
     284           0 :     break;
     285           0 :   case MHD_HTTP_FORBIDDEN:
     286           0 :     pr.hr.ec = TALER_JSON_get_error_code (json);
     287           0 :     pr.hr.hint = TALER_JSON_get_error_hint (json);
     288           0 :     break;
     289           0 :   case MHD_HTTP_NOT_FOUND:
     290           0 :     pr.hr.ec = TALER_JSON_get_error_code (json);
     291           0 :     pr.hr.hint = TALER_JSON_get_error_hint (json);
     292             :     /* Nothing really to verify, this should never
     293             :        happen, we should pass the JSON reply to the
     294             :        application */
     295           0 :     break;
     296           0 :   case MHD_HTTP_REQUEST_TIMEOUT:
     297           0 :     pr.hr.ec = TALER_JSON_get_error_code (json);
     298           0 :     pr.hr.hint = TALER_JSON_get_error_hint (json);
     299             :     /* The merchant couldn't generate a timely response, likely because
     300             :        it itself waited too long on the exchange.
     301             :        Pass on to application. */
     302           0 :     break;
     303          10 :   case MHD_HTTP_CONFLICT:
     304          10 :     TALER_MERCHANT_parse_error_details_ (json,
     305             :                                          MHD_HTTP_CONFLICT,
     306             :                                          &pr.hr);
     307          10 :     break;
     308           0 :   case MHD_HTTP_GONE:
     309           0 :     TALER_MERCHANT_parse_error_details_ (json,
     310             :                                          response_code,
     311             :                                          &pr.hr);
     312             :     /* The merchant says we are too late, the offer has expired or some
     313             :        denomination key of a coin involved has expired.
     314             :        Might be a disagreement in timestamps? Still, pass on to application. */
     315           0 :     break;
     316           0 :   case MHD_HTTP_PRECONDITION_FAILED:
     317           0 :     TALER_MERCHANT_parse_error_details_ (json,
     318             :                                          response_code,
     319             :                                          &pr.hr);
     320             :     /* Nothing really to verify, the merchant is blaming us for failing to
     321             :        satisfy some constraint (likely it does not like our exchange because
     322             :        of some disagreement on the PKI).  We should pass the JSON reply to the
     323             :        application */
     324           0 :     break;
     325           0 :   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
     326             :     {
     327           0 :       json_t *ebus = json_object_get (json,
     328             :                                       "exchange_base_urls");
     329           0 :       if (NULL == ebus)
     330             :       {
     331           0 :         GNUNET_break_op (0);
     332           0 :         pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     333           0 :         pr.hr.http_status = 0;
     334           0 :         pr.hr.hint = "failed to parse exchange_base_urls field in response";
     335           0 :         break;
     336             :       }
     337           0 :       {
     338           0 :         size_t alen = json_array_size (ebus);
     339           0 :         const char *ebua[GNUNET_NZL (alen)];
     340             :         size_t idx;
     341             :         json_t *jebu;
     342           0 :         bool ok = true;
     343             : 
     344           0 :         GNUNET_assert (alen <= UINT_MAX);
     345           0 :         json_array_foreach (ebus, idx, jebu)
     346             :         {
     347           0 :           ebua[idx] = json_string_value (jebu);
     348           0 :           if (NULL == ebua[idx])
     349             :           {
     350           0 :             GNUNET_break_op (0);
     351           0 :             pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     352           0 :             pr.hr.http_status = 0;
     353           0 :             pr.hr.hint = "non-string value in exchange_base_urls in response";
     354           0 :             ok = false;
     355           0 :             break;
     356             :           }
     357             :         }
     358           0 :         if (! ok)
     359           0 :           break;
     360             :         pr.details.unavailable_for_legal_reasons.num_exchanges
     361           0 :           = (unsigned int) alen;
     362             :         pr.details.unavailable_for_legal_reasons.exchanges
     363           0 :           = ebua;
     364           0 :         oph->pay_cb (oph->pay_cb_cls,
     365             :                      &pr);
     366           0 :         TALER_MERCHANT_order_pay_cancel (oph);
     367           0 :         return;
     368             :       }
     369             :     }
     370             :     break;
     371           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     372           0 :     TALER_MERCHANT_parse_error_details_ (json,
     373             :                                          response_code,
     374             :                                          &pr.hr);
     375             :     /* Server had an internal issue; we should retry,
     376             :        but this API leaves this to the application */
     377           0 :     break;
     378           0 :   case MHD_HTTP_BAD_GATEWAY:
     379             :     /* Nothing really to verify, the merchant is blaming the exchange.
     380             :        We should pass the JSON reply to the application */
     381           0 :     TALER_MERCHANT_parse_error_details_ (json,
     382             :                                          response_code,
     383             :                                          &pr.hr);
     384           0 :     break;
     385           0 :   case MHD_HTTP_SERVICE_UNAVAILABLE:
     386           0 :     TALER_MERCHANT_parse_error_details_ (json,
     387             :                                          response_code,
     388             :                                          &pr.hr);
     389             :     /* Exchange couldn't respond properly; the retry is
     390             :        left to the application */
     391           0 :     break;
     392           0 :   case MHD_HTTP_GATEWAY_TIMEOUT:
     393           0 :     TALER_MERCHANT_parse_error_details_ (json,
     394             :                                          response_code,
     395             :                                          &pr.hr);
     396             :     /* Exchange couldn't respond in a timely fashion;
     397             :        the retry is left to the application */
     398           0 :     break;
     399           0 :   default:
     400           0 :     TALER_MERCHANT_parse_error_details_ (json,
     401             :                                          response_code,
     402             :                                          &pr.hr);
     403             :     /* unexpected response code */
     404           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     405             :                 "Unexpected response code %u/%d\n",
     406             :                 (unsigned int) response_code,
     407             :                 (int) pr.hr.ec);
     408           0 :     GNUNET_break_op (0);
     409           0 :     break;
     410             :   }
     411          34 :   oph->pay_cb (oph->pay_cb_cls,
     412             :                &pr);
     413          34 :   TALER_MERCHANT_order_pay_cancel (oph);
     414             : }
     415             : 
     416             : 
     417             : struct TALER_MERCHANT_OrderPayHandle *
     418          34 : TALER_MERCHANT_order_pay_frontend (
     419             :   struct GNUNET_CURL_Context *ctx,
     420             :   const char *merchant_url,
     421             :   const char *order_id,
     422             :   const char *session_id,
     423             :   const json_t *wallet_data,
     424             :   unsigned int num_coins,
     425             :   const struct TALER_MERCHANT_PaidCoin coins[static num_coins],
     426             :   unsigned int num_tokens,
     427             :   const struct TALER_MERCHANT_UsedToken tokens[static num_tokens],
     428             :   json_t *j_output_tokens, // FIXME: not used, remove?
     429             :   TALER_MERCHANT_OrderPayCallback pay_cb,
     430             :   void *pay_cb_cls)
     431          34 : {
     432             :   struct TALER_MERCHANT_OrderPayHandle *oph;
     433             :   json_t *pay_obj;
     434             :   json_t *j_coins;
     435          34 :   json_t *j_tokens = NULL;
     436             :   CURL *eh;
     437             :   struct TALER_Amount total_fee;
     438             :   struct TALER_Amount total_amount;
     439             : 
     440          34 :   j_coins = json_array ();
     441          34 :   GNUNET_assert (NULL != j_coins);
     442          74 :   for (unsigned int i = 0; i<num_coins; i++)
     443             :   {
     444             :     json_t *j_coin;
     445          40 :     const struct TALER_MERCHANT_PaidCoin *pc = &coins[i];
     446             :     struct TALER_Amount fee;
     447             :     struct TALER_DenominationHashP denom_hash;
     448             : 
     449          40 :     if (0 >
     450          40 :         TALER_amount_subtract (&fee,
     451             :                                &pc->amount_with_fee,
     452             :                                &pc->amount_without_fee))
     453             :     {
     454             :       /* Integer underflow, fee larger than total amount?
     455             :          This should not happen (client violated API!) */
     456           0 :       GNUNET_break (0);
     457           0 :       json_decref (j_coins);
     458           0 :       return NULL;
     459             :     }
     460          40 :     if (0 == i)
     461             :     {
     462          30 :       total_fee = fee;
     463          30 :       total_amount = pc->amount_with_fee;
     464             :     }
     465             :     else
     466             :     {
     467          10 :       if ( (0 >
     468          10 :             TALER_amount_add (&total_fee,
     469             :                               &total_fee,
     470          10 :                               &fee)) ||
     471             :            (0 >
     472          10 :             TALER_amount_add (&total_amount,
     473             :                               &total_amount,
     474             :                               &pc->amount_with_fee)) )
     475             :       {
     476             :         /* integer overflow */
     477           0 :         GNUNET_break (0);
     478           0 :         json_decref (j_coins);
     479           0 :         return NULL;
     480             :       }
     481             :     }
     482             : 
     483          40 :     TALER_denom_pub_hash (&pc->denom_pub,
     484             :                           &denom_hash);
     485             :     /* create JSON for this coin */
     486          40 :     j_coin = GNUNET_JSON_PACK (
     487             :       TALER_JSON_pack_amount ("contribution",
     488             :                               &pc->amount_with_fee),
     489             :       GNUNET_JSON_pack_data_auto ("coin_pub",
     490             :                                   &pc->coin_pub),
     491             :       GNUNET_JSON_pack_string ("exchange_url",
     492             :                                pc->exchange_url),
     493             :       GNUNET_JSON_pack_data_auto ("h_denom",
     494             :                                   &denom_hash),
     495             :       TALER_JSON_pack_denom_sig ("ub_sig",
     496             :                                  &pc->denom_sig),
     497             :       GNUNET_JSON_pack_data_auto ("coin_sig",
     498             :                                   &pc->coin_sig));
     499          40 :     if (0 !=
     500          40 :         json_array_append_new (j_coins,
     501             :                                j_coin))
     502             :     {
     503           0 :       GNUNET_break (0);
     504           0 :       json_decref (j_coins);
     505           0 :       return NULL;
     506             :     }
     507             :   }
     508             : 
     509          34 :   if (0 < num_tokens)
     510             :   {
     511           4 :     j_tokens = json_array ();
     512           4 :     GNUNET_assert (NULL != j_tokens);
     513           8 :     for (unsigned int i = 0; i<num_tokens; i++)
     514             :     {
     515             :       json_t *j_token;
     516           4 :       const struct TALER_MERCHANT_UsedToken *ut = &tokens[i];
     517             : 
     518           4 :       j_token = GNUNET_JSON_PACK (
     519             :         GNUNET_JSON_pack_data_auto ("token_sig",
     520             :                                     &ut->token_sig),
     521             :         GNUNET_JSON_pack_data_auto ("token_pub",
     522             :                                     &ut->token_pub),
     523             :         GNUNET_JSON_pack_data_auto ("h_issue",
     524             :                                     &ut->issue_pub.public_key->pub_key_hash),
     525             :         TALER_JSON_pack_token_issue_sig ("ub_sig",
     526             :                                          &ut->ub_sig));
     527           4 :       if (0 !=
     528           4 :           json_array_append_new (j_tokens,
     529             :                                  j_token))
     530             :       {
     531           0 :         GNUNET_break (0);
     532           0 :         json_decref (j_tokens);
     533           0 :         return NULL;
     534             :       }
     535             :     }
     536             :   }
     537             : 
     538          34 :   pay_obj = GNUNET_JSON_PACK (
     539             :     GNUNET_JSON_pack_array_steal ("coins",
     540             :                                   j_coins),
     541             :     GNUNET_JSON_pack_allow_null (
     542             :       GNUNET_JSON_pack_array_steal ("tokens",
     543             :                                     j_tokens)),
     544             :     GNUNET_JSON_pack_allow_null (
     545             :       GNUNET_JSON_pack_object_incref ("wallet_data",
     546             :                                       (json_t *) wallet_data)),
     547             :     GNUNET_JSON_pack_allow_null (
     548             :       GNUNET_JSON_pack_string ("session_id",
     549             :                                session_id)));
     550             : 
     551          34 :   json_dumpf (pay_obj,
     552             :               stderr,
     553             :               JSON_INDENT (2));
     554             : 
     555          34 :   oph = GNUNET_new (struct TALER_MERCHANT_OrderPayHandle);
     556          34 :   oph->ctx = ctx;
     557          34 :   oph->pay_cb = pay_cb;
     558          34 :   oph->pay_cb_cls = pay_cb_cls;
     559             :   {
     560             :     char *path;
     561             : 
     562          34 :     GNUNET_asprintf (&path,
     563             :                      "orders/%s/pay",
     564             :                      order_id);
     565          34 :     oph->url = TALER_url_join (merchant_url,
     566             :                                path,
     567             :                                NULL);
     568          34 :     GNUNET_free (path);
     569             :   }
     570          34 :   if (NULL == oph->url)
     571             :   {
     572           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     573             :                 "Could not construct request URL.\n");
     574           0 :     json_decref (pay_obj);
     575           0 :     GNUNET_free (oph);
     576           0 :     return NULL;
     577             :   }
     578          34 :   oph->num_coins = num_coins;
     579          34 :   oph->coins = GNUNET_new_array (num_coins,
     580             :                                  struct TALER_MERCHANT_PaidCoin);
     581          34 :   GNUNET_memcpy (oph->coins,
     582             :                  coins,
     583             :                  num_coins * sizeof (struct TALER_MERCHANT_PaidCoin));
     584             : 
     585          34 :   eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
     586          34 :   if (GNUNET_OK !=
     587          34 :       TALER_curl_easy_post (&oph->post_ctx,
     588             :                             eh,
     589             :                             pay_obj))
     590             :   {
     591           0 :     GNUNET_break (0);
     592           0 :     curl_easy_cleanup (eh);
     593           0 :     json_decref (pay_obj);
     594           0 :     GNUNET_free (oph->url);
     595           0 :     GNUNET_free (oph);
     596           0 :     return NULL;
     597             :   }
     598          34 :   json_decref (pay_obj);
     599          68 :   oph->job = GNUNET_CURL_job_add2 (ctx,
     600             :                                    eh,
     601          34 :                                    oph->post_ctx.headers,
     602             :                                    &handle_pay_finished,
     603             :                                    oph);
     604          34 :   return oph;
     605             : }
     606             : 
     607             : 
     608             : struct TALER_MERCHANT_OrderPayHandle *
     609          34 : TALER_MERCHANT_order_pay (
     610             :   struct GNUNET_CURL_Context *ctx,
     611             :   const char *merchant_url,
     612             :   const char *session_id,
     613             :   const struct TALER_PrivateContractHashP *h_contract_terms,
     614             :   int choice_index,
     615             :   const struct TALER_Amount *amount,
     616             :   const struct TALER_Amount *max_fee,
     617             :   const struct TALER_MerchantPublicKeyP *merchant_pub,
     618             :   const struct TALER_MerchantSignatureP *merchant_sig,
     619             :   struct GNUNET_TIME_Timestamp timestamp,
     620             :   struct GNUNET_TIME_Timestamp refund_deadline,
     621             :   struct GNUNET_TIME_Timestamp pay_deadline,
     622             :   const struct TALER_MerchantWireHashP *h_wire,
     623             :   const char *order_id,
     624             :   unsigned int num_coins,
     625             :   const struct TALER_MERCHANT_PayCoin coins[static num_coins],
     626             :   unsigned int num_tokens,
     627             :   const struct TALER_MERCHANT_UseToken tokens[static num_tokens],
     628             :   unsigned int num_output_tokens,
     629             :   const struct TALER_MERCHANT_OutputToken output_tokens[static num_output_tokens
     630             :   ],
     631             :   TALER_MERCHANT_OrderPayCallback pay_cb,
     632             :   void *pay_cb_cls)
     633          34 : {
     634          34 :   json_t *j_output_tokens = NULL;
     635          34 :   const json_t *wallet_data = NULL;
     636             :   struct GNUNET_HashCode wallet_data_hash;
     637             : 
     638          34 :   if (GNUNET_YES !=
     639          34 :       TALER_amount_cmp_currency (amount,
     640             :                                  max_fee))
     641             :   {
     642           0 :     GNUNET_break (0);
     643           0 :     return NULL;
     644             :   }
     645          34 :   if ((0 < num_tokens || 0 < num_output_tokens) && 0 > choice_index)
     646             :   {
     647             :     /* Tokens (input or output) require a valid choice_index to be set.
     648             :        Only contracts with coices can use or issue tokens. */
     649           0 :     GNUNET_break (0);
     650           0 :     return NULL;
     651             :   }
     652          34 :   if (0 < num_output_tokens)
     653             :   {
     654             :     /* Build token envelopes json array. */
     655           6 :     j_output_tokens = json_array ();
     656           6 :     GNUNET_assert (NULL != j_output_tokens);
     657          12 :     for (unsigned int i = 0; i<num_output_tokens; i++)
     658             :     {
     659             :       json_t *j_token_ev;
     660           6 :       const struct TALER_MERCHANT_OutputToken *ev = &output_tokens[i];
     661             : 
     662           6 :       j_token_ev = GNUNET_JSON_PACK (
     663             :         TALER_JSON_pack_token_envelope (NULL,
     664             :                                         &ev->envelope));
     665             : 
     666           6 :       if (0 !=
     667           6 :           json_array_append_new (j_output_tokens,
     668             :                                  j_token_ev))
     669             :       {
     670           0 :         GNUNET_break (0);
     671           0 :         json_decref (j_output_tokens);
     672           0 :         return NULL;
     673             :       }
     674             :     }
     675             :   }
     676          34 :   if (0 <= choice_index)
     677             :   {
     678           6 :     wallet_data = GNUNET_JSON_PACK (
     679             :       GNUNET_JSON_pack_int64 ("choice_index",
     680             :                               choice_index),
     681             :       GNUNET_JSON_pack_allow_null (
     682             :         GNUNET_JSON_pack_array_incref ("tokens_evs",
     683             :                                        j_output_tokens)));
     684           6 :     TALER_json_hash (wallet_data,
     685             :                      &wallet_data_hash);
     686             :   }
     687          34 :   {
     688          34 :     struct TALER_MERCHANT_PaidCoin pc[num_coins];
     689          34 :     struct TALER_MERCHANT_UsedToken ut[num_tokens];
     690             : 
     691          74 :     for (unsigned int i = 0; i<num_coins; i++)
     692             :     {
     693          40 :       const struct TALER_MERCHANT_PayCoin *coin = &coins[i]; // coin priv.
     694          40 :       struct TALER_MERCHANT_PaidCoin *p = &pc[i]; // coin pub.
     695             :       struct TALER_Amount fee;
     696             :       struct TALER_DenominationHashP h_denom_pub;
     697             : 
     698          40 :       if (0 >
     699          40 :           TALER_amount_subtract (&fee,
     700             :                                  &coin->amount_with_fee,
     701             :                                  &coin->amount_without_fee))
     702             :       {
     703             :         /* Integer underflow, fee larger than total amount?
     704             :            This should not happen (client violated API!) */
     705           0 :         GNUNET_break (0);
     706           0 :         return NULL;
     707             :       }
     708          40 :       TALER_denom_pub_hash (&coin->denom_pub,
     709             :                             &h_denom_pub);
     710          40 :       TALER_wallet_deposit_sign (&coin->amount_with_fee,
     711             :                                  &fee,
     712             :                                  h_wire,
     713             :                                  h_contract_terms,
     714             :                                  (NULL != wallet_data)
     715             :                                  ? &wallet_data_hash
     716             :                                  : NULL,
     717          40 :                                  coin->h_age_commitment,
     718             :                                  NULL /* h_extensions! */,
     719             :                                  &h_denom_pub,
     720             :                                  timestamp,
     721             :                                  merchant_pub,
     722             :                                  refund_deadline,
     723             :                                  &coin->coin_priv,
     724             :                                  &p->coin_sig);
     725          40 :       p->denom_pub = coin->denom_pub;
     726          40 :       p->denom_sig = coin->denom_sig;
     727          40 :       p->denom_value = coin->denom_value;
     728          40 :       GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv,
     729             :                                           &p->coin_pub.eddsa_pub);
     730          40 :       p->amount_with_fee = coin->amount_with_fee;
     731          40 :       p->amount_without_fee = coin->amount_without_fee;
     732          40 :       p->exchange_url = coin->exchange_url;
     733             :     }
     734          38 :     for (unsigned int i = 0; i<num_tokens; i++)
     735             :     {
     736           4 :       const struct TALER_MERCHANT_UseToken *token = &tokens[i];
     737           4 :       struct TALER_MERCHANT_UsedToken *t = &ut[i];
     738             : 
     739           4 :       TALER_wallet_token_use_sign (h_contract_terms,
     740             :                                    &wallet_data_hash, // checked for != NULL above
     741             :                                    &token->token_priv,
     742             :                                    &t->token_sig);
     743           4 :       t->ub_sig = token->ub_sig;
     744           4 :       t->issue_pub = token->issue_pub;
     745           4 :       GNUNET_CRYPTO_eddsa_key_get_public (&token->token_priv.private_key,
     746             :                                           &t->token_pub.public_key);
     747             :     }
     748             :     {
     749             :       struct TALER_MERCHANT_OrderPayHandle *oph;
     750             : 
     751          34 :       oph = TALER_MERCHANT_order_pay_frontend (ctx,
     752             :                                                merchant_url,
     753             :                                                order_id,
     754             :                                                session_id,
     755             :                                                wallet_data,
     756             :                                                num_coins,
     757             :                                                pc,
     758             :                                                num_tokens,
     759             :                                                ut,
     760             :                                                j_output_tokens,
     761             :                                                pay_cb,
     762             :                                                pay_cb_cls);
     763          34 :       if (NULL == oph)
     764           0 :         return NULL;
     765          34 :       oph->h_contract_terms = *h_contract_terms;
     766          34 :       oph->merchant_pub = *merchant_pub;
     767          34 :       oph->am_wallet = true;
     768          34 :       return oph;
     769             :     }
     770             :   }
     771             : }
     772             : 
     773             : 
     774             : void
     775          34 : TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *oph)
     776             : {
     777          34 :   if (NULL != oph->job)
     778             :   {
     779           0 :     GNUNET_CURL_job_cancel (oph->job);
     780           0 :     oph->job = NULL;
     781             :   }
     782          34 :   TALER_curl_easy_post_finished (&oph->post_ctx);
     783          34 :   json_decref (oph->error_history);
     784          34 :   json_decref (oph->full_reply);
     785          34 :   GNUNET_free (oph->coins);
     786          34 :   GNUNET_free (oph->url);
     787          34 :   GNUNET_free (oph);
     788          34 : }
     789             : 
     790             : 
     791             : /* end of merchant_api_post_order_pay.c */

Generated by: LCOV version 1.16