LCOV - code coverage report
Current view: top level - lib - merchant_api_post_order_pay.c (source / functions) Hit Total Coverage
Test: GNU Taler coverage report Lines: 0 250 0.0 %
Date: 2021-04-12 06:04:58 Functions: 0 6 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014, 2015, 2016, 2017, 2020 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 <taler/taler_json_lib.h>
      34             : #include <taler/taler_signatures.h>
      35             : #include <taler/taler_exchange_service.h>
      36             : #include <taler/taler_curl_lib.h>
      37             : 
      38             : 
      39             : /**
      40             :  * @brief A Pay Handle
      41             :  */
      42             : struct TALER_MERCHANT_OrderPayHandle
      43             : {
      44             : 
      45             :   /**
      46             :    * The url for this request.
      47             :    */
      48             :   char *url;
      49             : 
      50             :   /**
      51             :    * Handle for the request.
      52             :    */
      53             :   struct GNUNET_CURL_Job *job;
      54             : 
      55             :   /**
      56             :    * Function to call with the result in "pay" @e mode.
      57             :    */
      58             :   TALER_MERCHANT_OrderPayCallback pay_cb;
      59             : 
      60             :   /**
      61             :    * Closure for @a pay_cb.
      62             :    */
      63             :   void *pay_cb_cls;
      64             : 
      65             :   /**
      66             :    * Reference to the execution context.
      67             :    */
      68             :   struct GNUNET_CURL_Context *ctx;
      69             : 
      70             :   /**
      71             :    * Minor context that holds body and headers.
      72             :    */
      73             :   struct TALER_CURL_PostContext post_ctx;
      74             : 
      75             :   /**
      76             :    * The coins we are paying with.
      77             :    */
      78             :   struct TALER_MERCHANT_PaidCoin *coins;
      79             : 
      80             :   /**
      81             :    * Hash of the contract we are paying, set
      82             :    * if @e am_wallet is true.
      83             :    */
      84             :   struct GNUNET_HashCode h_contract_terms;
      85             : 
      86             :   /**
      87             :    * Public key of the merchant (instance) being paid, set
      88             :    * if @e am_wallet is true.
      89             :    */
      90             :   struct TALER_MerchantPublicKeyP merchant_pub;
      91             : 
      92             :   /**
      93             :    * Number of @e coins we are paying with.
      94             :    */
      95             :   unsigned int num_coins;
      96             : 
      97             :   /**
      98             :    * Set to true if this is the wallet API and we have
      99             :    * initialized @e h_contract_terms and @e merchant_pub.
     100             :    */
     101             :   bool am_wallet;
     102             : 
     103             : };
     104             : 
     105             : 
     106             : /**
     107             :  * We got a 409 response back from the exchange (or the merchant).
     108             :  * Now we need to check the provided cryptograophic proof that the
     109             :  * coin was actually already spent!
     110             :  *
     111             :  * @param pc handle of the original coin we paid with
     112             :  * @param json cryptograophic proof of coin's transaction
     113             :  *        history as was returned by the exchange/merchant
     114             :  * @return #GNUNET_OK if proof checks out
     115             :  */
     116             : static int
     117           0 : check_coin_history (const struct TALER_MERCHANT_PaidCoin *pc,
     118             :                     json_t *json)
     119             : {
     120             :   struct TALER_Amount spent;
     121             :   struct TALER_Amount spent_plus_contrib;
     122             :   struct GNUNET_HashCode h_denom_pub;
     123             :   struct GNUNET_HashCode h_denom_pub_pc;
     124             : 
     125           0 :   if (GNUNET_OK !=
     126           0 :       TALER_EXCHANGE_verify_coin_history (NULL, /* do not verify fees */
     127           0 :                                           pc->amount_with_fee.currency,
     128             :                                           &pc->coin_pub,
     129             :                                           json,
     130             :                                           &h_denom_pub,
     131             :                                           &spent))
     132             :   {
     133             :     /* Exchange's history fails to verify */
     134           0 :     GNUNET_break_op (0);
     135           0 :     return GNUNET_SYSERR;
     136             :   }
     137           0 :   if (0 >
     138           0 :       TALER_amount_add (&spent_plus_contrib,
     139             :                         &spent,
     140             :                         &pc->amount_with_fee))
     141             :   {
     142             :     /* We got an integer overflow? Bad application! */
     143           0 :     GNUNET_break (0);
     144           0 :     return GNUNET_SYSERR;
     145             :   }
     146           0 :   GNUNET_CRYPTO_rsa_public_key_hash (pc->denom_pub.rsa_public_key,
     147             :                                      &h_denom_pub_pc);
     148           0 :   if ( (-1 != TALER_amount_cmp (&pc->denom_value,
     149           0 :                                 &spent_plus_contrib)) &&
     150           0 :        (0 != GNUNET_memcmp (&h_denom_pub,
     151             :                             &h_denom_pub_pc)) )
     152             :   {
     153             :     /* according to our calculations, the transaction should
     154             :        have still worked, AND we did not get any proof of
     155             :        coin public key re-use; hence: exchange error! */
     156           0 :     GNUNET_break_op (0);
     157           0 :     return GNUNET_SYSERR;
     158             :   }
     159           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     160             :               "Accepting proof of double-spending (or coin public key re-use)\n");
     161           0 :   return GNUNET_OK;
     162             : }
     163             : 
     164             : 
     165             : /**
     166             :  * We got a 409 response back from the exchange (or the merchant).
     167             :  * Now we need to check the provided cryptograophic proof that the
     168             :  * coin was actually already spent!
     169             :  *
     170             :  * @param oph handle of the original pay operation
     171             :  * @param json cryptograophic proof returned by the
     172             :  *        exchange/merchant
     173             :  * @return #GNUNET_OK if proof checks out
     174             :  */
     175             : static int
     176           0 : check_conflict (struct TALER_MERCHANT_OrderPayHandle *oph,
     177             :                 const json_t *json)
     178             : {
     179             :   json_t *history;
     180             :   json_t *ereply;
     181             :   struct TALER_CoinSpendPublicKeyP coin_pub;
     182             :   struct GNUNET_JSON_Specification spec[] = {
     183           0 :     GNUNET_JSON_spec_json ("exchange_reply", &ereply),
     184           0 :     GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub),
     185           0 :     GNUNET_JSON_spec_end ()
     186             :   };
     187             :   struct GNUNET_JSON_Specification hspec[] = {
     188           0 :     GNUNET_JSON_spec_json ("history", &history),
     189           0 :     GNUNET_JSON_spec_end ()
     190             :   };
     191             : 
     192           0 :   if (GNUNET_OK !=
     193           0 :       GNUNET_JSON_parse (json,
     194             :                          spec,
     195             :                          NULL, NULL))
     196             :   {
     197           0 :     GNUNET_break_op (0);
     198           0 :     return GNUNET_SYSERR;
     199             :   }
     200           0 :   if (GNUNET_OK !=
     201           0 :       GNUNET_JSON_parse (ereply,
     202             :                          hspec,
     203             :                          NULL, NULL))
     204             :   {
     205           0 :     GNUNET_break_op (0);
     206           0 :     GNUNET_JSON_parse_free (spec);
     207           0 :     return GNUNET_SYSERR;
     208             :   }
     209           0 :   GNUNET_JSON_parse_free (spec);
     210             : 
     211           0 :   for (unsigned int i = 0; i<oph->num_coins; i++)
     212             :   {
     213           0 :     if (0 == memcmp (&oph->coins[i].coin_pub,
     214             :                      &coin_pub,
     215             :                      sizeof (struct TALER_CoinSpendPublicKeyP)))
     216             :     {
     217             :       int ret;
     218             : 
     219           0 :       ret = check_coin_history (&oph->coins[i],
     220             :                                 history);
     221           0 :       GNUNET_JSON_parse_free (hspec);
     222           0 :       return ret;
     223             :     }
     224             :   }
     225           0 :   GNUNET_break_op (0); /* complaint is not about any of the coins
     226             :                           that we actually paid with... */
     227           0 :   GNUNET_JSON_parse_free (hspec);
     228           0 :   return GNUNET_SYSERR;
     229             : }
     230             : 
     231             : 
     232             : /**
     233             :  * Function called when we're done processing the
     234             :  * HTTP /pay request.
     235             :  *
     236             :  * @param cls the `struct TALER_MERCHANT_Pay`
     237             :  * @param response_code HTTP response code, 0 on error
     238             :  * @param response response body, NULL if not in JSON
     239             :  */
     240             : static void
     241           0 : handle_pay_finished (void *cls,
     242             :                      long response_code,
     243             :                      const void *response)
     244             : {
     245           0 :   struct TALER_MERCHANT_OrderPayHandle *oph = cls;
     246           0 :   const json_t *json = response;
     247           0 :   struct TALER_MERCHANT_HttpResponse hr = {
     248           0 :     .http_status = (unsigned int) response_code,
     249             :     .reply = json
     250             :   };
     251           0 :   struct TALER_MerchantSignatureP *merchant_sigp = NULL;
     252             :   struct TALER_MerchantSignatureP merchant_sig;
     253             : 
     254           0 :   oph->job = NULL;
     255           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     256             :               "/pay completed with response code %u\n",
     257             :               (unsigned int) response_code);
     258           0 :   switch (response_code)
     259             :   {
     260           0 :   case 0:
     261           0 :     hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     262           0 :     break;
     263           0 :   case MHD_HTTP_OK:
     264           0 :     if (oph->am_wallet)
     265             :     {
     266             :       /* Here we can (and should) verify the merchant's signature */
     267           0 :       struct PaymentResponsePS pr = {
     268           0 :         .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK),
     269           0 :         .purpose.size = htonl (sizeof (pr)),
     270             :         .h_contract_terms = oph->h_contract_terms
     271             :       };
     272             :       struct GNUNET_JSON_Specification spec[] = {
     273           0 :         GNUNET_JSON_spec_fixed_auto ("sig",
     274             :                                      &merchant_sig),
     275           0 :         GNUNET_JSON_spec_end ()
     276             :       };
     277             : 
     278           0 :       if (GNUNET_OK !=
     279           0 :           GNUNET_JSON_parse (json,
     280             :                              spec,
     281             :                              NULL, NULL))
     282             :       {
     283           0 :         GNUNET_break_op (0);
     284           0 :         hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     285           0 :         hr.http_status = 0;
     286           0 :         hr.hint = "sig field missing in response";
     287           0 :         break;
     288             :       }
     289             : 
     290           0 :       if (GNUNET_OK !=
     291           0 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_PAYMENT_OK,
     292             :                                       &pr,
     293             :                                       &merchant_sig.eddsa_sig,
     294             :                                       &oph->merchant_pub.eddsa_pub))
     295             :       {
     296           0 :         GNUNET_break_op (0);
     297           0 :         hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     298           0 :         hr.http_status = 0;
     299           0 :         hr.hint = "signature invalid";
     300             :       }
     301           0 :       merchant_sigp = &merchant_sig;
     302             :     }
     303           0 :     break;
     304             :   /* Tolerating Not Acceptable because sometimes
     305             :    * - especially in tests - we might want to POST
     306             :    * coins one at a time.  */
     307           0 :   case MHD_HTTP_NOT_ACCEPTABLE:
     308           0 :     hr.ec = TALER_JSON_get_error_code (json);
     309           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     310           0 :     break;
     311           0 :   case MHD_HTTP_BAD_REQUEST:
     312           0 :     hr.ec = TALER_JSON_get_error_code (json);
     313           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     314             :     /* This should never happen, either us
     315             :      * or the merchant is buggy (or API version conflict);
     316             :      * just pass JSON reply to the application */
     317           0 :     break;
     318           0 :   case MHD_HTTP_PAYMENT_REQUIRED:
     319             :     /* was originally paid, but then refunded */
     320           0 :     hr.ec = TALER_JSON_get_error_code (json);
     321           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     322           0 :     break;
     323           0 :   case MHD_HTTP_FORBIDDEN:
     324           0 :     hr.ec = TALER_JSON_get_error_code (json);
     325           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     326             :     /* Nothing really to verify, merchant says we tried to abort the payment
     327             :      * after it was successful. We should pass the JSON reply to the
     328             :      * application */
     329           0 :     break;
     330           0 :   case MHD_HTTP_NOT_FOUND:
     331           0 :     hr.ec = TALER_JSON_get_error_code (json);
     332           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     333             :     /* Nothing really to verify, this should never
     334             :        happen, we should pass the JSON reply to the
     335             :        application */
     336           0 :     break;
     337           0 :   case MHD_HTTP_PRECONDITION_FAILED:
     338           0 :     TALER_MERCHANT_parse_error_details_ (json,
     339             :                                          response_code,
     340             :                                          &hr);
     341             :     /* Nothing really to verify, the merchant is blaming us for failing to
     342             :        satisfy some constraint (likely it does not like our exchange because
     343             :        of some disagreement on the PKI).  We should pass the JSON reply to the
     344             :        application */
     345           0 :     break;
     346           0 :   case MHD_HTTP_REQUEST_TIMEOUT:
     347           0 :     hr.ec = TALER_JSON_get_error_code (json);
     348           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     349             :     /* The merchant couldn't generate a timely response, likely because
     350             :        it itself waited too long on the exchange.
     351             :        Pass on to application. */
     352           0 :     break;
     353           0 :   case MHD_HTTP_CONFLICT:
     354           0 :     hr.ec = TALER_JSON_get_error_code (json);
     355           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     356           0 :     if (GNUNET_OK != check_conflict (oph,
     357             :                                      json))
     358             :     {
     359           0 :       GNUNET_break_op (0);
     360           0 :       response_code = 0;
     361             :     }
     362           0 :     break;
     363           0 :   case MHD_HTTP_GONE:
     364           0 :     hr.ec = TALER_JSON_get_error_code (json);
     365           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     366             :     /* The merchant says we are too late, the offer has expired or some
     367             :        denomination key of a coin involved has expired.
     368             :        Might be a disagreement in timestamps? Still, pass on to application. */
     369           0 :     break;
     370           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     371           0 :     hr.ec = TALER_JSON_get_error_code (json);
     372           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     373             :     /* Server had an internal issue; we should retry,
     374             :        but this API leaves this to the application */
     375           0 :     break;
     376           0 :   case MHD_HTTP_BAD_GATEWAY:
     377             :     /* Nothing really to verify, the merchant is blaming the exchange.
     378             :        We should pass the JSON reply to the application */
     379           0 :     TALER_MERCHANT_parse_error_details_ (json,
     380             :                                          response_code,
     381             :                                          &hr);
     382           0 :     break;
     383           0 :   case MHD_HTTP_SERVICE_UNAVAILABLE:
     384           0 :     TALER_MERCHANT_parse_error_details_ (json,
     385             :                                          response_code,
     386             :                                          &hr);
     387             :     /* Exchange couldn't respond properly; the retry is
     388             :        left to the application */
     389           0 :     break;
     390           0 :   default:
     391           0 :     TALER_MERCHANT_parse_error_details_ (json,
     392             :                                          response_code,
     393             :                                          &hr);
     394             :     /* unexpected response code */
     395           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     396             :                 "Unexpected response code %u/%d\n",
     397             :                 (unsigned int) response_code,
     398             :                 (int) hr.ec);
     399           0 :     GNUNET_break_op (0);
     400           0 :     break;
     401             :   }
     402           0 :   oph->pay_cb (oph->pay_cb_cls,
     403             :                &hr,
     404             :                merchant_sigp);
     405           0 :   TALER_MERCHANT_order_pay_cancel (oph);
     406           0 : }
     407             : 
     408             : 
     409             : struct TALER_MERCHANT_OrderPayHandle *
     410           0 : TALER_MERCHANT_order_pay_frontend (
     411             :   struct GNUNET_CURL_Context *ctx,
     412             :   const char *merchant_url,
     413             :   const char *order_id,
     414             :   const char *session_id,
     415             :   unsigned int num_coins,
     416             :   const struct TALER_MERCHANT_PaidCoin coins[],
     417             :   TALER_MERCHANT_OrderPayCallback pay_cb,
     418             :   void *pay_cb_cls)
     419             : {
     420             :   struct TALER_MERCHANT_OrderPayHandle *oph;
     421             :   json_t *pay_obj;
     422             :   json_t *j_coins;
     423             :   CURL *eh;
     424             :   struct TALER_Amount total_fee;
     425             :   struct TALER_Amount total_amount;
     426             : 
     427           0 :   if (0 == num_coins)
     428             :   {
     429           0 :     GNUNET_break (0);
     430           0 :     return NULL;
     431             :   }
     432           0 :   j_coins = json_array ();
     433           0 :   for (unsigned int i = 0; i<num_coins; i++)
     434             :   {
     435             :     json_t *j_coin;
     436           0 :     const struct TALER_MERCHANT_PaidCoin *pc = &coins[i];
     437             :     struct TALER_Amount fee;
     438             :     struct GNUNET_HashCode denom_hash;
     439             : 
     440           0 :     if (0 >
     441           0 :         TALER_amount_subtract (&fee,
     442             :                                &pc->amount_with_fee,
     443             :                                &pc->amount_without_fee))
     444             :     {
     445             :       /* Integer underflow, fee larger than total amount?
     446             :          This should not happen (client violated API!) */
     447           0 :       GNUNET_break (0);
     448           0 :       json_decref (j_coins);
     449           0 :       return NULL;
     450             :     }
     451           0 :     if (0 == i)
     452             :     {
     453           0 :       total_fee = fee;
     454           0 :       total_amount = pc->amount_with_fee;
     455             :     }
     456             :     else
     457             :     {
     458           0 :       if ( (0 >
     459           0 :             TALER_amount_add (&total_fee,
     460             :                               &total_fee,
     461           0 :                               &fee)) ||
     462             :            (0 >
     463           0 :             TALER_amount_add (&total_amount,
     464             :                               &total_amount,
     465             :                               &pc->amount_with_fee)) )
     466             :       {
     467             :         /* integer overflow */
     468           0 :         GNUNET_break (0);
     469           0 :         json_decref (j_coins);
     470           0 :         return NULL;
     471             :       }
     472             :     }
     473             : 
     474           0 :     GNUNET_CRYPTO_rsa_public_key_hash (pc->denom_pub.rsa_public_key,
     475             :                                        &denom_hash);
     476             :     /* create JSON for this coin */
     477           0 :     j_coin = json_pack (
     478             :       "{s:o, s:o,"                   /* contribution/coin_pub */
     479             :       " s:s, s:o,"          /* exchange_url / denom_pub */
     480             :       " s:o, s:o}",          /* ub_sig / coin_sig */
     481             :       "contribution",
     482             :       TALER_JSON_from_amount (&pc->amount_with_fee),
     483             :       "coin_pub",
     484           0 :       GNUNET_JSON_from_data_auto (&pc->coin_pub),
     485             :       "exchange_url",
     486             :       pc->exchange_url,
     487             :       "h_denom",
     488             :       GNUNET_JSON_from_data_auto (&denom_hash),
     489             :       "ub_sig",
     490           0 :       GNUNET_JSON_from_rsa_signature (pc->denom_sig.rsa_signature),
     491             :       "coin_sig",
     492           0 :       GNUNET_JSON_from_data_auto (&pc->coin_sig));
     493           0 :     if (0 !=
     494           0 :         json_array_append_new (j_coins,
     495             :                                j_coin))
     496             :     {
     497           0 :       GNUNET_break (0);
     498           0 :       json_decref (j_coins);
     499           0 :       return NULL;
     500             :     }
     501             :   }
     502             : 
     503           0 :   pay_obj = json_pack ("{ s:o }",
     504             :                        "coins",
     505             :                        j_coins);
     506           0 :   if (NULL == pay_obj)
     507             :   {
     508           0 :     GNUNET_break (0);
     509           0 :     return NULL;
     510             :   }
     511           0 :   if (NULL != session_id)
     512             :   {
     513           0 :     if (0 != json_object_set (pay_obj,
     514             :                               "session_id",
     515             :                               json_string (session_id)))
     516             :     {
     517           0 :       GNUNET_break (0);
     518           0 :       json_decref (pay_obj);
     519           0 :       return NULL;
     520             :     }
     521             :   }
     522             : 
     523           0 :   oph = GNUNET_new (struct TALER_MERCHANT_OrderPayHandle);
     524           0 :   oph->ctx = ctx;
     525           0 :   oph->pay_cb = pay_cb;
     526           0 :   oph->pay_cb_cls = pay_cb_cls;
     527             :   {
     528             :     char *path;
     529             : 
     530           0 :     GNUNET_asprintf (&path,
     531             :                      "orders/%s/pay",
     532             :                      order_id);
     533           0 :     oph->url = TALER_url_join (merchant_url,
     534             :                                path,
     535             :                                NULL);
     536           0 :     GNUNET_free (path);
     537             :   }
     538           0 :   if (NULL == oph->url)
     539             :   {
     540           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     541             :                 "Could not construct request URL.\n");
     542           0 :     json_decref (pay_obj);
     543           0 :     GNUNET_free (oph);
     544           0 :     return NULL;
     545             :   }
     546           0 :   oph->num_coins = num_coins;
     547           0 :   oph->coins = GNUNET_new_array (num_coins,
     548             :                                  struct TALER_MERCHANT_PaidCoin);
     549           0 :   memcpy (oph->coins,
     550             :           coins,
     551             :           num_coins * sizeof (struct TALER_MERCHANT_PaidCoin));
     552             : 
     553           0 :   eh = curl_easy_init ();
     554           0 :   if (GNUNET_OK !=
     555           0 :       TALER_curl_easy_post (&oph->post_ctx,
     556             :                             eh,
     557             :                             pay_obj))
     558             :   {
     559           0 :     GNUNET_break (0);
     560           0 :     json_decref (pay_obj);
     561           0 :     GNUNET_free (oph);
     562           0 :     return NULL;
     563             :   }
     564             : 
     565           0 :   json_decref (pay_obj);
     566           0 :   GNUNET_assert (CURLE_OK ==
     567             :                  curl_easy_setopt (eh,
     568             :                                    CURLOPT_URL,
     569             :                                    oph->url));
     570           0 :   oph->job = GNUNET_CURL_job_add2 (ctx,
     571             :                                    eh,
     572           0 :                                    oph->post_ctx.headers,
     573             :                                    &handle_pay_finished,
     574             :                                    oph);
     575           0 :   return oph;
     576             : }
     577             : 
     578             : 
     579             : struct TALER_MERCHANT_OrderPayHandle *
     580           0 : TALER_MERCHANT_order_pay (struct GNUNET_CURL_Context *ctx,
     581             :                           const char *merchant_url,
     582             :                           const char *session_id,
     583             :                           const struct GNUNET_HashCode *h_contract_terms,
     584             :                           const struct TALER_Amount *amount,
     585             :                           const struct TALER_Amount *max_fee,
     586             :                           const struct TALER_MerchantPublicKeyP *merchant_pub,
     587             :                           const struct TALER_MerchantSignatureP *merchant_sig,
     588             :                           struct GNUNET_TIME_Absolute timestamp,
     589             :                           struct GNUNET_TIME_Absolute refund_deadline,
     590             :                           struct GNUNET_TIME_Absolute pay_deadline,
     591             :                           const struct GNUNET_HashCode *h_wire,
     592             :                           const char *order_id,
     593             :                           unsigned int num_coins,
     594             :                           const struct TALER_MERCHANT_PayCoin coins[],
     595             :                           TALER_MERCHANT_OrderPayCallback pay_cb,
     596             :                           void *pay_cb_cls)
     597             : {
     598           0 :   (void) GNUNET_TIME_round_abs (&timestamp);
     599           0 :   (void) GNUNET_TIME_round_abs (&pay_deadline);
     600           0 :   (void) GNUNET_TIME_round_abs (&refund_deadline);
     601           0 :   if (GNUNET_YES !=
     602           0 :       TALER_amount_cmp_currency (amount,
     603             :                                  max_fee))
     604             :   {
     605           0 :     GNUNET_break (0);
     606           0 :     return NULL;
     607             :   }
     608             : 
     609           0 :   {
     610           0 :     struct TALER_MERCHANT_PaidCoin pc[num_coins];
     611           0 :     struct TALER_DepositRequestPS dr = {
     612           0 :       .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT),
     613           0 :       .purpose.size = htonl (sizeof (dr)),
     614             :       .h_contract_terms = *h_contract_terms,
     615             :       .h_wire = *h_wire,
     616           0 :       .wallet_timestamp = GNUNET_TIME_absolute_hton (timestamp),
     617           0 :       .refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline),
     618             :       .merchant = *merchant_pub
     619             :     };
     620             : 
     621           0 :     for (unsigned int i = 0; i<num_coins; i++)
     622             :     {
     623           0 :       const struct TALER_MERCHANT_PayCoin *coin = &coins[i]; // coin priv.
     624           0 :       struct TALER_MERCHANT_PaidCoin *p = &pc[i]; // coin pub.
     625             :       struct TALER_Amount fee;
     626             : 
     627             :       /* prepare 'dr' for this coin to generate coin signature */
     628           0 :       GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv,
     629             :                                           &dr.coin_pub.eddsa_pub);
     630           0 :       TALER_amount_hton (&dr.amount_with_fee,
     631             :                          &coin->amount_with_fee);
     632           0 :       if (0 >
     633           0 :           TALER_amount_subtract (&fee,
     634             :                                  &coin->amount_with_fee,
     635             :                                  &coin->amount_without_fee))
     636             :       {
     637             :         /* Integer underflow, fee larger than total amount?
     638             :            This should not happen (client violated API!) */
     639           0 :         GNUNET_break (0);
     640           0 :         return NULL;
     641             :       }
     642           0 :       TALER_amount_hton (&dr.deposit_fee,
     643             :                          &fee);
     644           0 :       GNUNET_CRYPTO_rsa_public_key_hash (coin->denom_pub.rsa_public_key,
     645             :                                          &dr.h_denom_pub);
     646           0 :       GNUNET_CRYPTO_eddsa_sign (&coin->coin_priv.eddsa_priv,
     647             :                                 &dr,
     648             :                                 &p->coin_sig.eddsa_signature);
     649           0 :       p->denom_pub = coin->denom_pub;
     650           0 :       p->denom_sig = coin->denom_sig;
     651           0 :       p->denom_value = coin->denom_value;
     652           0 :       p->coin_pub = dr.coin_pub;
     653           0 :       p->amount_with_fee = coin->amount_with_fee;
     654           0 :       p->amount_without_fee = coin->amount_without_fee;
     655           0 :       p->exchange_url = coin->exchange_url;
     656             :     }
     657             :     {
     658             :       struct TALER_MERCHANT_OrderPayHandle *oph;
     659             : 
     660           0 :       oph = TALER_MERCHANT_order_pay_frontend (ctx,
     661             :                                                merchant_url,
     662             :                                                order_id,
     663             :                                                session_id,
     664             :                                                num_coins,
     665             :                                                pc,
     666             :                                                pay_cb,
     667             :                                                pay_cb_cls);
     668           0 :       if (NULL == oph)
     669           0 :         return NULL;
     670           0 :       oph->h_contract_terms = *h_contract_terms;
     671           0 :       oph->merchant_pub = *merchant_pub;
     672           0 :       oph->am_wallet = true;
     673           0 :       return oph;
     674             :     }
     675             :   }
     676             : }
     677             : 
     678             : 
     679             : void
     680           0 : TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *oph)
     681             : {
     682           0 :   if (NULL != oph->job)
     683             :   {
     684           0 :     GNUNET_CURL_job_cancel (oph->job);
     685           0 :     oph->job = NULL;
     686             :   }
     687           0 :   TALER_curl_easy_post_finished (&oph->post_ctx);
     688           0 :   GNUNET_free (oph->coins);
     689           0 :   GNUNET_free (oph->url);
     690           0 :   GNUNET_free (oph);
     691           0 : }
     692             : 
     693             : 
     694             : /* end of merchant_api_post_order_pay.c */

Generated by: LCOV version 1.14