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: 2020-08-15 06:12:35 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 lib/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 json 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_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_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_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_FAILED_DEPENDENCY:
     371           0 :     TALER_MERCHANT_parse_error_details_ (json,
     372             :                                          response_code,
     373             :                                          &hr);
     374             :     /* Nothing really to verify, the merchant is blaming the exchange.
     375             :        We should pass the JSON reply to the application */
     376           0 :     break;
     377           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     378           0 :     hr.ec = TALER_JSON_get_error_code (json);
     379           0 :     hr.hint = TALER_JSON_get_error_hint (json);
     380             :     /* Server had an internal issue; we should retry,
     381             :        but this API leaves this to the application */
     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             : /**
     410             :  * PAY a merchant.  API for frontends talking to backends. Here,
     411             :  * the frontend does not have the coin's private keys, but just
     412             :  * the public keys and signatures.  Note the subtle difference
     413             :  * in the type of @a coins compared to #TALER_MERCHANT_pay().
     414             :  *
     415             :  * @param ctx the execution loop context
     416             :  * @param merchant_url base URL of the merchant's backend
     417             :  * @param order_id order to pay
     418             :  * @param session_id session to pay for, or NULL for none
     419             :  * @param num_coins number of coins used to pay
     420             :  * @param coins array of coins we use to pay
     421             :  * @param pay_cb the callback to call when a reply for this request is available
     422             :  * @param pay_cb_cls closure for @a pay_cb
     423             :  * @return a handle for this request
     424             :  */
     425             : struct TALER_MERCHANT_OrderPayHandle *
     426           0 : TALER_MERCHANT_order_pay_frontend (
     427             :   struct GNUNET_CURL_Context *ctx,
     428             :   const char *merchant_url,
     429             :   const char *order_id,
     430             :   const char *session_id,
     431             :   unsigned int num_coins,
     432             :   const struct TALER_MERCHANT_PaidCoin coins[],
     433             :   TALER_MERCHANT_OrderPayCallback pay_cb,
     434             :   void *pay_cb_cls)
     435             : {
     436             :   struct TALER_MERCHANT_OrderPayHandle *oph;
     437             :   json_t *pay_obj;
     438             :   json_t *j_coins;
     439             :   CURL *eh;
     440             :   struct TALER_Amount total_fee;
     441             :   struct TALER_Amount total_amount;
     442             : 
     443           0 :   if (0 == num_coins)
     444             :   {
     445           0 :     GNUNET_break (0);
     446           0 :     return NULL;
     447             :   }
     448           0 :   j_coins = json_array ();
     449           0 :   for (unsigned int i = 0; i<num_coins; i++)
     450             :   {
     451             :     json_t *j_coin;
     452           0 :     const struct TALER_MERCHANT_PaidCoin *pc = &coins[i];
     453             :     struct TALER_Amount fee;
     454             :     struct GNUNET_HashCode denom_hash;
     455             : 
     456           0 :     if (0 >
     457           0 :         TALER_amount_subtract (&fee,
     458             :                                &pc->amount_with_fee,
     459             :                                &pc->amount_without_fee))
     460             :     {
     461             :       /* Integer underflow, fee larger than total amount?
     462             :          This should not happen (client violated API!) */
     463           0 :       GNUNET_break (0);
     464           0 :       json_decref (j_coins);
     465           0 :       return NULL;
     466             :     }
     467           0 :     if (0 == i)
     468             :     {
     469           0 :       total_fee = fee;
     470           0 :       total_amount = pc->amount_with_fee;
     471             :     }
     472             :     else
     473             :     {
     474           0 :       if ( (0 >
     475           0 :             TALER_amount_add (&total_fee,
     476             :                               &total_fee,
     477           0 :                               &fee)) ||
     478             :            (0 >
     479           0 :             TALER_amount_add (&total_amount,
     480             :                               &total_amount,
     481             :                               &pc->amount_with_fee)) )
     482             :       {
     483             :         /* integer overflow */
     484           0 :         GNUNET_break (0);
     485           0 :         json_decref (j_coins);
     486           0 :         return NULL;
     487             :       }
     488             :     }
     489             : 
     490           0 :     GNUNET_CRYPTO_rsa_public_key_hash (pc->denom_pub.rsa_public_key,
     491             :                                        &denom_hash);
     492             :     /* create JSON for this coin */
     493           0 :     j_coin = json_pack (
     494             :       "{s:o, s:o,"                   /* contribution/coin_pub */
     495             :       " s:s, s:o,"          /* exchange_url / denom_pub */
     496             :       " s:o, s:o}",          /* ub_sig / coin_sig */
     497             :       "contribution",
     498             :       TALER_JSON_from_amount (&pc->amount_with_fee),
     499             :       "coin_pub",
     500           0 :       GNUNET_JSON_from_data_auto (&pc->coin_pub),
     501             :       "exchange_url",
     502             :       pc->exchange_url,
     503             :       "h_denom",
     504             :       GNUNET_JSON_from_data_auto (&denom_hash),
     505             :       "ub_sig",
     506           0 :       GNUNET_JSON_from_rsa_signature (pc->denom_sig.rsa_signature),
     507             :       "coin_sig",
     508           0 :       GNUNET_JSON_from_data_auto (&pc->coin_sig));
     509           0 :     if (0 !=
     510           0 :         json_array_append_new (j_coins,
     511             :                                j_coin))
     512             :     {
     513           0 :       GNUNET_break (0);
     514           0 :       json_decref (j_coins);
     515           0 :       return NULL;
     516             :     }
     517             :   }
     518             : 
     519           0 :   pay_obj = json_pack ("{ s:o }",
     520             :                        "coins",
     521             :                        j_coins);
     522           0 :   if (NULL == pay_obj)
     523             :   {
     524           0 :     GNUNET_break (0);
     525           0 :     return NULL;
     526             :   }
     527           0 :   if (NULL != session_id)
     528             :   {
     529           0 :     if (0 != json_object_set (pay_obj,
     530             :                               "session_id",
     531             :                               json_string (session_id)))
     532             :     {
     533           0 :       GNUNET_break (0);
     534           0 :       json_decref (pay_obj);
     535           0 :       return NULL;
     536             :     }
     537             :   }
     538             : 
     539           0 :   oph = GNUNET_new (struct TALER_MERCHANT_OrderPayHandle);
     540           0 :   oph->ctx = ctx;
     541           0 :   oph->pay_cb = pay_cb;
     542           0 :   oph->pay_cb_cls = pay_cb_cls;
     543             :   {
     544             :     char *path;
     545             : 
     546           0 :     GNUNET_asprintf (&path,
     547             :                      "orders/%s/pay",
     548             :                      order_id);
     549           0 :     oph->url = TALER_url_join (merchant_url,
     550             :                                path,
     551             :                                NULL);
     552           0 :     GNUNET_free (path);
     553             :   }
     554           0 :   if (NULL == oph->url)
     555             :   {
     556           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     557             :                 "Could not construct request URL.\n");
     558           0 :     json_decref (pay_obj);
     559           0 :     GNUNET_free (oph);
     560           0 :     return NULL;
     561             :   }
     562           0 :   oph->num_coins = num_coins;
     563           0 :   oph->coins = GNUNET_new_array (num_coins,
     564             :                                  struct TALER_MERCHANT_PaidCoin);
     565           0 :   memcpy (oph->coins,
     566             :           coins,
     567             :           num_coins * sizeof (struct TALER_MERCHANT_PaidCoin));
     568             : 
     569           0 :   eh = curl_easy_init ();
     570           0 :   if (GNUNET_OK !=
     571           0 :       TALER_curl_easy_post (&oph->post_ctx,
     572             :                             eh,
     573             :                             pay_obj))
     574             :   {
     575           0 :     GNUNET_break (0);
     576           0 :     json_decref (pay_obj);
     577           0 :     GNUNET_free (oph);
     578           0 :     return NULL;
     579             :   }
     580             : 
     581           0 :   json_decref (pay_obj);
     582           0 :   GNUNET_assert (CURLE_OK ==
     583             :                  curl_easy_setopt (eh,
     584             :                                    CURLOPT_URL,
     585             :                                    oph->url));
     586           0 :   oph->job = GNUNET_CURL_job_add2 (ctx,
     587             :                                    eh,
     588           0 :                                    oph->post_ctx.headers,
     589             :                                    &handle_pay_finished,
     590             :                                    oph);
     591           0 :   return oph;
     592             : }
     593             : 
     594             : 
     595             : /**
     596             :  * Pay a merchant.  API for wallets that have the coin's private keys.
     597             :  * _NOTE_: this function does NOT calculate each coin amount in order
     598             :  * to match the contract total price.  This calculation is to be made
     599             :  * by the logic using this library.
     600             :  *
     601             :  * @param ctx the execution loop context
     602             :  * @param merchant_url base URL of the merchant's backend
     603             :  * @param session_id session to pay for, or NULL for none
     604             :  * @param h_contract_terms hashcode of the proposal being paid
     605             :  * @param amount total value of the contract to be paid to the merchant
     606             :  * @param max_fee maximum fee covered by the merchant (according to the contract)
     607             :  * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
     608             :  * @param merchant_sig signature from the merchant over the original contract
     609             :  * @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant
     610             :  * @param refund_deadline date until which the merchant can issue a refund to the customer via the merchant (can be zero if refunds are not allowed)
     611             :  * @param pay_deadline maximum time limit to pay for this contract
     612             :  * @param h_wire hash of the merchant’s account details
     613             :  * @param order_id order id of the proposal being paid
     614             :  * @param num_coins number of coins used to pay
     615             :  * @param coins array of coins we use to pay
     616             :  * @param pay_cb the callback to call when a reply for this request is available
     617             :  * @param pay_cb_cls closure for @a pay_cb
     618             :  * @return a handle for this request
     619             :  */
     620             : struct TALER_MERCHANT_OrderPayHandle *
     621           0 : TALER_MERCHANT_order_pay (struct GNUNET_CURL_Context *ctx,
     622             :                           const char *merchant_url,
     623             :                           const char *session_id,
     624             :                           const struct GNUNET_HashCode *h_contract_terms,
     625             :                           const struct TALER_Amount *amount,
     626             :                           const struct TALER_Amount *max_fee,
     627             :                           const struct TALER_MerchantPublicKeyP *merchant_pub,
     628             :                           const struct TALER_MerchantSignatureP *merchant_sig,
     629             :                           struct GNUNET_TIME_Absolute timestamp,
     630             :                           struct GNUNET_TIME_Absolute refund_deadline,
     631             :                           struct GNUNET_TIME_Absolute pay_deadline,
     632             :                           const struct GNUNET_HashCode *h_wire,
     633             :                           const char *order_id,
     634             :                           unsigned int num_coins,
     635             :                           const struct TALER_MERCHANT_PayCoin coins[],
     636             :                           TALER_MERCHANT_OrderPayCallback pay_cb,
     637             :                           void *pay_cb_cls)
     638             : {
     639           0 :   (void) GNUNET_TIME_round_abs (&timestamp);
     640           0 :   (void) GNUNET_TIME_round_abs (&pay_deadline);
     641           0 :   (void) GNUNET_TIME_round_abs (&refund_deadline);
     642           0 :   if (GNUNET_YES !=
     643           0 :       TALER_amount_cmp_currency (amount,
     644             :                                  max_fee))
     645             :   {
     646           0 :     GNUNET_break (0);
     647           0 :     return NULL;
     648             :   }
     649             : 
     650           0 :   {
     651           0 :     struct TALER_MERCHANT_PaidCoin pc[num_coins];
     652           0 :     struct TALER_DepositRequestPS dr = {
     653           0 :       .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT),
     654           0 :       .purpose.size = htonl (sizeof (dr)),
     655             :       .h_contract_terms = *h_contract_terms,
     656             :       .h_wire = *h_wire,
     657           0 :       .wallet_timestamp = GNUNET_TIME_absolute_hton (timestamp),
     658           0 :       .refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline),
     659             :       .merchant = *merchant_pub
     660             :     };
     661             : 
     662           0 :     for (unsigned int i = 0; i<num_coins; i++)
     663             :     {
     664           0 :       const struct TALER_MERCHANT_PayCoin *coin = &coins[i]; // coin priv.
     665           0 :       struct TALER_MERCHANT_PaidCoin *p = &pc[i]; // coin pub.
     666             :       struct TALER_Amount fee;
     667             : 
     668             :       /* prepare 'dr' for this coin to generate coin signature */
     669           0 :       GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv,
     670             :                                           &dr.coin_pub.eddsa_pub);
     671           0 :       TALER_amount_hton (&dr.amount_with_fee,
     672             :                          &coin->amount_with_fee);
     673           0 :       if (0 >
     674           0 :           TALER_amount_subtract (&fee,
     675             :                                  &coin->amount_with_fee,
     676             :                                  &coin->amount_without_fee))
     677             :       {
     678             :         /* Integer underflow, fee larger than total amount?
     679             :            This should not happen (client violated API!) */
     680           0 :         GNUNET_break (0);
     681           0 :         return NULL;
     682             :       }
     683           0 :       TALER_amount_hton (&dr.deposit_fee,
     684             :                          &fee);
     685           0 :       GNUNET_CRYPTO_rsa_public_key_hash (coin->denom_pub.rsa_public_key,
     686             :                                          &dr.h_denom_pub);
     687           0 :       GNUNET_CRYPTO_eddsa_sign (&coin->coin_priv.eddsa_priv,
     688             :                                 &dr,
     689             :                                 &p->coin_sig.eddsa_signature);
     690           0 :       p->denom_pub = coin->denom_pub;
     691           0 :       p->denom_sig = coin->denom_sig;
     692           0 :       p->denom_value = coin->denom_value;
     693           0 :       p->coin_pub = dr.coin_pub;
     694           0 :       p->amount_with_fee = coin->amount_with_fee;
     695           0 :       p->amount_without_fee = coin->amount_without_fee;
     696           0 :       p->exchange_url = coin->exchange_url;
     697             :     }
     698             :     {
     699             :       struct TALER_MERCHANT_OrderPayHandle *oph;
     700             : 
     701           0 :       oph = TALER_MERCHANT_order_pay_frontend (ctx,
     702             :                                                merchant_url,
     703             :                                                order_id,
     704             :                                                session_id,
     705             :                                                num_coins,
     706             :                                                pc,
     707             :                                                pay_cb,
     708             :                                                pay_cb_cls);
     709           0 :       if (NULL == oph)
     710           0 :         return NULL;
     711           0 :       oph->h_contract_terms = *h_contract_terms;
     712           0 :       oph->merchant_pub = *merchant_pub;
     713           0 :       oph->am_wallet = true;
     714           0 :       return oph;
     715             :     }
     716             :   }
     717             : }
     718             : 
     719             : 
     720             : /**
     721             :  * Cancel a pay request.  This function cannot be used on a request handle if
     722             :  * a response is already served for it.
     723             :  *
     724             :  * @param oph the pay request handle
     725             :  */
     726             : void
     727           0 : TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *oph)
     728             : {
     729           0 :   if (NULL != oph->job)
     730             :   {
     731           0 :     GNUNET_CURL_job_cancel (oph->job);
     732           0 :     oph->job = NULL;
     733             :   }
     734           0 :   TALER_curl_easy_post_finished (&oph->post_ctx);
     735           0 :   GNUNET_free (oph->coins);
     736           0 :   GNUNET_free (oph->url);
     737           0 :   GNUNET_free (oph);
     738           0 : }
     739             : 
     740             : 
     741             : /* end of merchant_api_post_order_pay.c */

Generated by: LCOV version 1.14