LCOV - code coverage report
Current view: top level - lib - merchant_api_pay.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 165 228 72.4 %
Date: 2018-05-16 06:16:58 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014, 2015, 2016, 2017 GNUnet e.V. and INRIA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Lesser General Public License as published by the Free Software
       7             :   Foundation; either version 2.1, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU Lesser General Public License along with
      14             :   TALER; see the file COPYING.LGPL.  If not, see
      15             :   <http://www.gnu.org/licenses/>
      16             : */
      17             : /**
      18             :  * @file lib/merchant_api_pay.c
      19             :  * @brief Implementation of the /pay request of the merchant's HTTP API
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "platform.h"
      23             : #include <curl/curl.h>
      24             : #include <jansson.h>
      25             : #include <microhttpd.h> /* just for HTTP status codes */
      26             : #include <gnunet/gnunet_util_lib.h>
      27             : #include <gnunet/gnunet_curl_lib.h>
      28             : #include "taler_merchant_service.h"
      29             : #include <taler/taler_json_lib.h>
      30             : #include <taler/taler_signatures.h>
      31             : #include <taler/taler_exchange_service.h>
      32             : 
      33             : 
      34             : /**
      35             :  * @brief A Pay Handle
      36             :  */
      37             : struct TALER_MERCHANT_Pay
      38             : {
      39             : 
      40             :   /**
      41             :    * The url for this request.
      42             :    */
      43             :   char *url;
      44             : 
      45             :   /**
      46             :    * JSON encoding of the request to POST.
      47             :    */
      48             :   char *json_enc;
      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_PayCallback pay_cb;
      59             : 
      60             :   /**
      61             :    * Closure for @a pay_cb.
      62             :    */
      63             :   void *pay_cb_cls;
      64             : 
      65             :   /**
      66             :    * Function to call with the result in "abort-refund" @e mode.
      67             :    */
      68             :   TALER_MERCHANT_PayRefundCallback abort_cb;
      69             : 
      70             :   /**
      71             :    * Closure for @a abort_cb.
      72             :    */
      73             :   void *abort_cb_cls;
      74             : 
      75             :   /**
      76             :    * Operational mode, either "pay" or "abort-refund".
      77             :    */
      78             :   const char *mode;
      79             : 
      80             :   /**
      81             :    * Reference to the execution context.
      82             :    */
      83             :   struct GNUNET_CURL_Context *ctx;
      84             : 
      85             :   /**
      86             :    * The coins we are paying with.
      87             :    */
      88             :   struct TALER_MERCHANT_PaidCoin *coins;
      89             : 
      90             :   /**
      91             :    * Number of @e coins we are paying with.
      92             :    */
      93             :   unsigned int num_coins;
      94             : 
      95             :   /**
      96             :    * Hash of the contract, only available in "abort-refund" mode.
      97             :    */
      98             :   struct GNUNET_HashCode h_contract_terms;
      99             : 
     100             : };
     101             : 
     102             : 
     103             : /**
     104             :  * Check that the response for a /pay refund is well-formed,
     105             :  * and call the application callback with the result if it is
     106             :  * OK. Otherwise returns #GNUNET_SYSERR.
     107             :  *
     108             :  * @param ph handle to operation that created the reply
     109             :  * @param json the reply to parse
     110             :  * @return #GNUNET_OK on success
     111             :  */
     112             : static int
     113           3 : check_abort_refund (struct TALER_MERCHANT_Pay *ph,
     114             :                     const json_t *json)
     115             : {
     116             :   json_t *refunds;
     117             :   unsigned int num_refunds;
     118             :   struct TALER_MerchantPublicKeyP merchant_pub;
     119           3 :   struct GNUNET_JSON_Specification spec[] = {
     120             :     GNUNET_JSON_spec_json ("refund_permissions", &refunds),
     121             :     GNUNET_JSON_spec_fixed_auto ("merchant_pub", &merchant_pub),
     122             :     GNUNET_JSON_spec_end()
     123             :   };
     124             : 
     125           3 :   if (GNUNET_OK !=
     126           3 :       GNUNET_JSON_parse (json,
     127             :                          spec,
     128             :                          NULL, NULL))
     129             :   {
     130           0 :     GNUNET_break_op (0);
     131           0 :     return GNUNET_SYSERR;
     132             :   }
     133           3 :   num_refunds = json_array_size (refunds);
     134           3 :   {
     135           3 :     struct TALER_MERCHANT_RefundEntry res[GNUNET_NZL(num_refunds)];
     136             : 
     137          12 :     for (unsigned int i=0;i<num_refunds;i++)
     138             :     {
     139           3 :       struct TALER_MerchantSignatureP *sig = &res[i].merchant_sig;
     140           3 :       json_t *refund = json_array_get (refunds, i);
     141           9 :       struct GNUNET_JSON_Specification spec_detail[] = {
     142             :         GNUNET_JSON_spec_fixed_auto ("merchant_sig",
     143             :                                      sig),
     144           3 :         GNUNET_JSON_spec_fixed_auto ("coin_pub",
     145             :                                      &res[i].coin_pub),
     146           3 :         GNUNET_JSON_spec_uint64 ("rtransaction_id",
     147             :                                  &res[i].rtransaction_id),
     148             :         GNUNET_JSON_spec_end()
     149             :       };
     150             :       struct TALER_RefundRequestPS rr;
     151             :       int found;
     152             : 
     153           3 :       if (GNUNET_OK !=
     154           3 :           GNUNET_JSON_parse (refund,
     155             :                              spec_detail,
     156             :                              NULL, NULL))
     157             :       {
     158           0 :         GNUNET_break_op (0);
     159           0 :         GNUNET_JSON_parse_free (spec);
     160           0 :         return GNUNET_SYSERR;
     161             :       }
     162             : 
     163           3 :       rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
     164           3 :       rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
     165           3 :       rr.h_contract_terms = ph->h_contract_terms;
     166           3 :       rr.coin_pub = res[i].coin_pub;
     167           3 :       rr.merchant = merchant_pub;
     168           3 :       rr.rtransaction_id = GNUNET_htonll (res[i].rtransaction_id);
     169           3 :       found = -1;
     170           9 :       for (unsigned int j=0;j<ph->num_coins;j++)
     171             :       {
     172           6 :         if (0 == memcmp (&ph->coins[j].coin_pub,
     173           6 :                          &res[i].coin_pub,
     174             :                          sizeof (struct TALER_CoinSpendPublicKeyP)))
     175             :         {
     176           3 :           TALER_amount_hton (&rr.refund_amount,
     177           3 :                              &ph->coins[j].amount_with_fee);
     178           3 :           TALER_amount_hton (&rr.refund_fee,
     179           3 :                              &ph->coins[j].refund_fee);
     180           3 :           found = j;
     181             :         }
     182             :       }
     183           3 :       if (-1 == found)
     184             :       {
     185           0 :         GNUNET_break_op (0);
     186           0 :         return GNUNET_SYSERR;
     187             :       }
     188             : 
     189           3 :       if (GNUNET_OK !=
     190           3 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
     191             :                                       &rr.purpose,
     192           3 :                                       &sig->eddsa_sig,
     193             :                                       &merchant_pub.eddsa_pub))
     194             :       {
     195           0 :         GNUNET_break_op (0);
     196           0 :         return GNUNET_SYSERR;
     197             :       }
     198             :     }
     199           6 :     ph->abort_cb (ph->abort_cb_cls,
     200             :                   MHD_HTTP_OK,
     201             :                   TALER_EC_NONE,
     202             :                   &merchant_pub,
     203           3 :                   &ph->h_contract_terms,
     204             :                   num_refunds,
     205             :                   res,
     206             :                   json);
     207           3 :     ph->abort_cb = NULL;
     208             :   }
     209           3 :   GNUNET_JSON_parse_free (spec);
     210           3 :   return GNUNET_OK;
     211             : }
     212             : 
     213             : 
     214             : /**
     215             :  * We got a 403 response back from the exchange (or the merchant).
     216             :  * Now we need to check the provided cryptographic proof that the
     217             :  * coin was actually already spent!
     218             :  *
     219             :  * @param pc handle of the original coin we paid with
     220             :  * @param json cryptographic proof of coin's transaction history as
     221             :  *        was returned by the exchange/merchant
     222             :  * @return #GNUNET_OK if proof checks out
     223             :  */
     224             : static int
     225           9 : check_coin_history (const struct TALER_MERCHANT_PaidCoin *pc,
     226             :                     json_t *json)
     227             : {
     228             :   struct TALER_Amount spent;
     229             :   struct TALER_Amount spent_plus_contrib;
     230             : 
     231           9 :   if (GNUNET_OK !=
     232           9 :       TALER_EXCHANGE_verify_coin_history (pc->amount_with_fee.currency,
     233             :                                           &pc->coin_pub,
     234             :                                           json,
     235             :                                           &spent))
     236             :   {
     237             :     /* Exchange's history fails to verify */
     238           0 :     GNUNET_break_op (0);
     239           0 :     return GNUNET_SYSERR;
     240             :   }
     241           9 :   if (GNUNET_OK !=
     242           9 :       TALER_amount_add (&spent_plus_contrib,
     243             :                         &spent,
     244             :                         &pc->amount_with_fee))
     245             :   {
     246             :     /* We got an integer overflow? Bad application! */
     247           0 :     GNUNET_break (0);
     248           0 :     return GNUNET_SYSERR;
     249             :   }
     250           9 :   if (-1 != TALER_amount_cmp (&pc->denom_value,
     251             :                               &spent_plus_contrib))
     252             :   {
     253             :     /* according to our calculations, the transaction should
     254             :        have still worked, exchange error! */
     255           0 :     GNUNET_break_op (0);
     256           0 :     return GNUNET_SYSERR;
     257             :   }
     258           9 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     259             :               "Accepting proof of double-spending\n");
     260           9 :   return GNUNET_OK;
     261             : }
     262             : 
     263             : 
     264             : /**
     265             :  * We got a 403 response back from the exchange (or the merchant).
     266             :  * Now we need to check the provided cryptographic proof that the
     267             :  * coin was actually already spent!
     268             :  *
     269             :  * @param ph handle of the original pay operation
     270             :  * @param json cryptographic proof returned by the exchange/merchant
     271             :  * @return #GNUNET_OK if proof checks out
     272             :  */
     273             : static int
     274           9 : check_forbidden (struct TALER_MERCHANT_Pay *ph,
     275             :                  const json_t *json)
     276             : {
     277             :   json_t *history;
     278             :   struct TALER_CoinSpendPublicKeyP coin_pub;
     279           9 :   struct GNUNET_JSON_Specification spec[] = {
     280             :     GNUNET_JSON_spec_json ("history", &history),
     281             :     GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub),
     282             :     GNUNET_JSON_spec_end()
     283             :   };
     284             :   int ret;
     285             : 
     286           9 :   if (GNUNET_OK !=
     287           9 :       GNUNET_JSON_parse (json,
     288             :                          spec,
     289             :                          NULL, NULL))
     290             :   {
     291           0 :     GNUNET_break_op (0);
     292           0 :     return GNUNET_SYSERR;
     293             :   }
     294          15 :   for (unsigned int i=0;i<ph->num_coins;i++)
     295             :   {
     296          15 :     if (0 == memcmp (&ph->coins[i].coin_pub,
     297             :                      &coin_pub,
     298             :                      sizeof (struct TALER_CoinSpendPublicKeyP)))
     299             :     {
     300           9 :       ret = check_coin_history (&ph->coins[i],
     301             :                                 history);
     302           9 :       GNUNET_JSON_parse_free (spec);
     303           9 :       return ret;
     304             :     }
     305             :   }
     306           0 :   GNUNET_break_op (0); /* complaint is not about any of the coins
     307             :                           that we actually paid with... */
     308           0 :   GNUNET_JSON_parse_free (spec);
     309           0 :   return GNUNET_SYSERR;
     310             : }
     311             : 
     312             : 
     313             : /**
     314             :  * Function called when we're done processing the
     315             :  * HTTP /pay request.
     316             :  *
     317             :  * @param cls the `struct TALER_MERCHANT_Pay`
     318             :  * @param response_code HTTP response code, 0 on error
     319             :  * @param json response body, NULL if not in JSON
     320             :  */
     321             : static void
     322          30 : handle_pay_finished (void *cls,
     323             :                      long response_code,
     324             :                      const json_t *json)
     325             : {
     326          30 :   struct TALER_MERCHANT_Pay *ph = cls;
     327             : 
     328          30 :   ph->job = NULL;
     329          30 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     330             :               "/pay completed with response code %u\n",
     331             :               (unsigned int) response_code);
     332          30 :   if (0 == strcasecmp (ph->mode,
     333             :                        "pay"))
     334             :   {
     335          24 :     switch (response_code)
     336             :     {
     337             :     case 0:
     338           0 :       break;
     339             :     case MHD_HTTP_OK:
     340          15 :       break;
     341             :     case MHD_HTTP_BAD_REQUEST:
     342             :       /* This should never happen, either us or the merchant is buggy
     343             :          (or API version conflict); just pass JSON reply to the application */
     344           0 :       break;
     345             :     case MHD_HTTP_FORBIDDEN:
     346           9 :       if (GNUNET_OK != check_forbidden (ph,
     347             :                                         json))
     348             :       {
     349           0 :         GNUNET_break_op (0);
     350           0 :         response_code = 0;
     351             :       }
     352           9 :       break;
     353             :     case MHD_HTTP_UNAUTHORIZED:
     354             :       /* Nothing really to verify, merchant says one of the signatures is
     355             :          invalid; as we checked them, this should never happen, we
     356             :          should pass the JSON reply to the application */
     357           0 :       break;
     358             :     case MHD_HTTP_NOT_FOUND:
     359             :       /* Nothing really to verify, this should never
     360             :          happen, we should pass the JSON reply to the application */
     361           0 :       break;
     362             :     case MHD_HTTP_INTERNAL_SERVER_ERROR:
     363             :       /* Server had an internal issue; we should retry, but this API
     364             :          leaves this to the application */
     365           0 :       break;
     366             :     default:
     367             :       /* unexpected response code */
     368           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     369             :                   "Unexpected response code %u\n",
     370             :                   (unsigned int) response_code);
     371           0 :       GNUNET_break (0);
     372           0 :       response_code = 0;
     373           0 :       break;
     374             :     }
     375          24 :     ph->pay_cb (ph->pay_cb_cls,
     376             :                 response_code,
     377             :                 TALER_JSON_get_error_code (json),
     378             :                 json);
     379             :   }
     380             :   else
     381             :   {
     382           6 :     GNUNET_assert (0 == strcasecmp (ph->mode,
     383             :                                     "abort-refund"));
     384             : 
     385           6 :     switch (response_code)
     386             :     {
     387             :     case 0:
     388           0 :       break;
     389             :     case MHD_HTTP_OK:
     390           3 :       if (GNUNET_OK ==
     391           3 :           check_abort_refund (ph,
     392             :                               json))
     393             :       {
     394           3 :         TALER_MERCHANT_pay_cancel (ph);
     395           3 :         return;
     396             :       }
     397           0 :       response_code = 0;
     398           0 :       break;
     399             :     case MHD_HTTP_BAD_REQUEST:
     400             :       /* This should never happen, either us or the merchant is buggy
     401             :          (or API version conflict); just pass JSON reply to the application */
     402           0 :       break;
     403             :     case MHD_HTTP_FORBIDDEN:
     404           3 :       break;
     405             :     case MHD_HTTP_UNAUTHORIZED:
     406             :       /* Nothing really to verify, merchant says one of the signatures is
     407             :          invalid; as we checked them, this should never happen, we
     408             :          should pass the JSON reply to the application */
     409           0 :       break;
     410             :     case MHD_HTTP_NOT_FOUND:
     411             :       /* Nothing really to verify, this should never
     412             :          happen, we should pass the JSON reply to the application */
     413           0 :       break;
     414             :     case MHD_HTTP_INTERNAL_SERVER_ERROR:
     415             :       /* Server had an internal issue; we should retry, but this API
     416             :          leaves this to the application */
     417           0 :       break;
     418             :     default:
     419             :       /* unexpected response code */
     420           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     421             :                   "Unexpected response code %u\n",
     422             :                   (unsigned int) response_code);
     423           0 :       GNUNET_break (0);
     424           0 :       response_code = 0;
     425           0 :       break;
     426             :     }
     427           3 :     ph->abort_cb (ph->abort_cb_cls,
     428             :                   response_code,
     429             :                   TALER_JSON_get_error_code (json),
     430             :                   NULL,
     431             :                   NULL,
     432             :                   0,
     433             :                   NULL,
     434             :                   json);
     435             :   }
     436             : 
     437          27 :   TALER_MERCHANT_pay_cancel (ph);
     438             : }
     439             : 
     440             : 
     441             : /**
     442             :  * Issue /pay request. Generic version for the various
     443             :  * variants of the API.
     444             :  *
     445             :  * @param ctx the execution loop context
     446             :  * @param merchant_url base URL of the merchant's backend
     447             :  * @param merchant_pub public key of the merchant
     448             :  * @param num_coins number of coins used to pay
     449             :  * @param coins array of coins we use to pay
     450             :  * @param mode mode string to use ("pay" or "abort-refund").
     451             :  * @param pay_cb the callback to call when a reply for this request is available
     452             :  * @param pay_cb_cls closure for @a pay_cb
     453             :  * @param abort_cb callback to call for the abort-refund variant
     454             :  * @param abort_cb_cls closure for @a abort_cb
     455             :  * @return a handle for this request
     456             :  */
     457             : static struct TALER_MERCHANT_Pay *
     458          30 : request_pay_generic (struct GNUNET_CURL_Context *ctx,
     459             :                      const char *merchant_url,
     460             :                      const struct TALER_MerchantPublicKeyP *merchant_pub,
     461             :                      const char *order_id,
     462             :                      unsigned int num_coins,
     463             :                      const struct TALER_MERCHANT_PaidCoin *coins,
     464             :                      const char *mode,
     465             :                      TALER_MERCHANT_PayCallback pay_cb,
     466             :                      void *pay_cb_cls,
     467             :                      TALER_MERCHANT_PayRefundCallback abort_cb,
     468             :                      void *abort_cb_cls)
     469             : {
     470             :   struct TALER_MERCHANT_Pay *ph;
     471             :   json_t *pay_obj;
     472             :   json_t *j_coins;
     473             :   CURL *eh;
     474             :   struct TALER_Amount total_fee;
     475             :   struct TALER_Amount total_amount;
     476             : 
     477          30 :   if (0 == num_coins)
     478             :   {
     479           0 :     GNUNET_break (0);
     480           0 :     return NULL;
     481             :   }
     482          30 :   j_coins = json_array ();
     483         144 :   for (unsigned int i=0;i<num_coins;i++)
     484             :   {
     485             :     json_t *j_coin;
     486          42 :     const struct TALER_MERCHANT_PaidCoin *pc = &coins[i];
     487             :     struct TALER_Amount fee;
     488             : 
     489          42 :     if (GNUNET_SYSERR ==
     490          42 :         TALER_amount_subtract (&fee,
     491             :                                &pc->amount_with_fee,
     492             :                                &pc->amount_without_fee))
     493             :     {
     494             :       /* Integer underflow, fee larger than total amount?
     495             :          This should not happen (client violated API!) */
     496           0 :       GNUNET_break (0);
     497           0 :       json_decref (j_coins);
     498           0 :       return NULL;
     499             :     }
     500          42 :     if (0 == i)
     501             :     {
     502          30 :       total_fee = fee;
     503          30 :       total_amount = pc->amount_with_fee;
     504             :     }
     505             :     else
     506             :     {
     507          12 :       if ( (GNUNET_OK !=
     508          12 :             TALER_amount_add (&total_fee,
     509             :                               &total_fee,
     510          12 :                               &fee)) ||
     511             :            (GNUNET_OK !=
     512          12 :             TALER_amount_add (&total_amount,
     513             :                               &total_amount,
     514             :                               &pc->amount_with_fee)) )
     515             :       {
     516             :         /* integer overflow */
     517           0 :         GNUNET_break (0);
     518           0 :         json_decref (j_coins);
     519           0 :         return NULL;
     520             :       }
     521             :     }
     522             : 
     523             :     /* create JSON for this coin */
     524         168 :     j_coin = json_pack ("{s:o, s:o," /* contribution/coin_pub */
     525             :                         " s:s, s:o," /* exchange_url / denom_pub */
     526             :                         " s:o, s:o}", /* ub_sig / coin_sig */
     527             :                         "contribution", TALER_JSON_from_amount (&pc->amount_with_fee),
     528          42 :                         "coin_pub", GNUNET_JSON_from_data_auto (&pc->coin_pub),
     529             :                         "exchange_url", pc->exchange_url,
     530          42 :                         "denom_pub", GNUNET_JSON_from_rsa_public_key (pc->denom_pub.rsa_public_key),
     531          42 :                         "ub_sig", GNUNET_JSON_from_rsa_signature (pc->denom_sig.rsa_signature),
     532          42 :                         "coin_sig", GNUNET_JSON_from_data_auto (&pc->coin_sig)
     533             :                         );
     534          42 :     if (0 !=
     535          42 :         json_array_append_new (j_coins,
     536             :                                j_coin))
     537             :     {
     538           0 :       GNUNET_break (0);
     539           0 :       json_decref (j_coins);
     540           0 :       return NULL;
     541             :     }
     542             :   }
     543             : 
     544          30 :   pay_obj = json_pack ("{"
     545             :                        " s:s," /* mode */
     546             :                        " s:o," /* coins */
     547             :                        " s:s," /* order_id */
     548             :                        " s:o," /* merchant_pub */
     549             :                        "}",
     550             :                        "mode", mode,
     551             :                        "coins", j_coins,
     552             :                        "order_id", order_id,
     553             :                        "merchant_pub", GNUNET_JSON_from_data_auto (merchant_pub));
     554          30 :   if (NULL == pay_obj)
     555             :   {
     556           0 :     GNUNET_break (0);
     557           0 :     return NULL;
     558             :   }
     559          30 :   ph = GNUNET_new (struct TALER_MERCHANT_Pay);
     560          30 :   ph->ctx = ctx;
     561          30 :   ph->mode = mode;
     562          30 :   ph->abort_cb = abort_cb;
     563          30 :   ph->abort_cb_cls = abort_cb_cls;
     564          30 :   ph->pay_cb = pay_cb;
     565          30 :   ph->pay_cb_cls = pay_cb_cls;
     566          30 :   ph->url = TALER_url_join (merchant_url, "/public/pay", NULL);
     567          30 :   ph->num_coins = num_coins;
     568          30 :   ph->coins = GNUNET_new_array (num_coins,
     569             :                                 struct TALER_MERCHANT_PaidCoin);
     570          30 :   memcpy (ph->coins,
     571             :           coins,
     572             :           num_coins * sizeof (struct TALER_MERCHANT_PaidCoin));
     573             : 
     574          30 :   eh = curl_easy_init ();
     575          30 :   GNUNET_assert (NULL != (ph->json_enc =
     576             :                           json_dumps (pay_obj,
     577             :                                       JSON_COMPACT)));
     578          30 :   json_decref (pay_obj);
     579          30 :   GNUNET_assert (CURLE_OK ==
     580             :                  curl_easy_setopt (eh,
     581             :                                    CURLOPT_URL,
     582             :                                    ph->url));
     583          30 :   GNUNET_assert (CURLE_OK ==
     584             :                  curl_easy_setopt (eh,
     585             :                                    CURLOPT_POSTFIELDS,
     586             :                                    ph->json_enc));
     587          30 :   GNUNET_assert (CURLE_OK ==
     588             :                  curl_easy_setopt (eh,
     589             :                                    CURLOPT_POSTFIELDSIZE,
     590             :                                    strlen (ph->json_enc)));
     591          30 :   ph->job = GNUNET_CURL_job_add (ctx,
     592             :                                  eh,
     593             :                                  GNUNET_YES,
     594             :                                  &handle_pay_finished,
     595             :                                  ph);
     596          30 :   return ph;
     597             : }
     598             : 
     599             : 
     600             : /**
     601             :  * Pay a merchant.  API for wallets that have the coin's private keys.
     602             :  * _NOTE_: this function does NOT calculate each coin amount in order
     603             :  * to match the contract total price.  This calculation is to be made
     604             :  * by the logic using this library.
     605             :  *
     606             :  * @param ctx the execution loop context
     607             :  * @param merchant_url base URL of the merchant's backend
     608             :  * @param instance which merchant instance will receive this payment
     609             :  * @param h_contract_terms hashcode of the proposal being paid
     610             :  * @param amount total value of the contract to be paid to the merchant
     611             :  * @param max_fee maximum fee covered by the merchant (according to the contract)
     612             :  * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
     613             :  * @param merchant_sig signature from the merchant over the original contract
     614             :  * @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant
     615             :  * @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)
     616             :  * @param pay_deadline maximum time limit to pay for this contract
     617             :  * @param h_wire hash of the merchant’s account details
     618             :  * @param order_id order id of the proposal being paid
     619             :  * @param num_coins number of coins used to pay
     620             :  * @param coins array of coins we use to pay
     621             :  * @param pay_cb the callback to call when a reply for this request is available
     622             :  * @param pay_cb_cls closure for @a pay_cb
     623             :  * @return a handle for this request
     624             :  */
     625             : static struct TALER_MERCHANT_Pay *
     626          30 : prepare_pay_generic (struct GNUNET_CURL_Context *ctx,
     627             :                      const char *merchant_url,
     628             :                      const char *instance,
     629             :                      const struct GNUNET_HashCode *h_contract_terms,
     630             :                      const struct TALER_Amount *amount,
     631             :                      const struct TALER_Amount *max_fee,
     632             :                      const struct TALER_MerchantPublicKeyP *merchant_pub,
     633             :                      const struct TALER_MerchantSignatureP *merchant_sig,
     634             :                      struct GNUNET_TIME_Absolute timestamp,
     635             :                      struct GNUNET_TIME_Absolute refund_deadline,
     636             :                      struct GNUNET_TIME_Absolute pay_deadline,
     637             :                      const struct GNUNET_HashCode *h_wire,
     638             :                      const char *order_id,
     639             :                      unsigned int num_coins,
     640             :                      const struct TALER_MERCHANT_PayCoin *coins,
     641             :                      const char *mode,
     642             :                      TALER_MERCHANT_PayCallback pay_cb,
     643             :                      void *pay_cb_cls,
     644             :                      TALER_MERCHANT_PayRefundCallback abort_cb,
     645             :                      void *abort_cb_cls)
     646          30 : {
     647             :   struct TALER_DepositRequestPS dr;
     648          30 :   struct TALER_MERCHANT_PaidCoin pc[num_coins];
     649             : 
     650          30 :   (void) GNUNET_TIME_round_abs (&timestamp);
     651          30 :   (void) GNUNET_TIME_round_abs (&pay_deadline);
     652          30 :   (void) GNUNET_TIME_round_abs (&refund_deadline);
     653             : 
     654          30 :   if (GNUNET_YES !=
     655          30 :       TALER_amount_cmp_currency (amount,
     656             :                                  max_fee))
     657             :   {
     658           0 :     GNUNET_break (0);
     659           0 :     return NULL;
     660             :   }
     661             : 
     662          30 :   dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
     663          30 :   dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
     664          30 :   dr.h_contract_terms = *h_contract_terms;
     665          30 :   dr.h_wire = *h_wire;
     666          30 :   dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
     667          30 :   dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
     668          30 :   dr.merchant = *merchant_pub;
     669             : 
     670          72 :   for (unsigned int i=0;i<num_coins;i++)
     671             :   {
     672          42 :     const struct TALER_MERCHANT_PayCoin *coin = &coins[i]; // coin priv.
     673          42 :     struct TALER_MERCHANT_PaidCoin *p = &pc[i]; // coin pub.
     674             :     struct TALER_Amount fee;
     675             : 
     676             :     /* prepare 'dr' for this coin to generate coin signature */
     677          42 :     GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv,
     678             :                                         &dr.coin_pub.eddsa_pub);
     679          42 :     TALER_amount_hton (&dr.amount_with_fee,
     680             :                        &coin->amount_with_fee);
     681          42 :     if (GNUNET_SYSERR ==
     682          42 :         TALER_amount_subtract (&fee,
     683             :                                &coin->amount_with_fee,
     684             :                                &coin->amount_without_fee))
     685             :     {
     686             :       /* Integer underflow, fee larger than total amount?
     687             :          This should not happen (client violated API!) */
     688           0 :       GNUNET_break (0);
     689           0 :       return NULL;
     690             :     }
     691          42 :     TALER_amount_hton (&dr.deposit_fee,
     692             :                        &fee);
     693             :     {
     694          42 :       TALER_LOG_DEBUG ("... amount_with_fee was %s\n",
     695             :                        TALER_amount2s (&coin->amount_with_fee));
     696          42 :       TALER_LOG_DEBUG ("... fee was %s\n",
     697             :                        TALER_amount2s (&fee));
     698             :     }
     699             : 
     700          42 :     GNUNET_CRYPTO_eddsa_sign (&coin->coin_priv.eddsa_priv,
     701             :                               &dr.purpose,
     702             :                               &p->coin_sig.eddsa_signature);
     703          42 :     p->denom_pub = coin->denom_pub;
     704          42 :     p->denom_sig = coin->denom_sig;
     705          42 :     p->denom_value = coin->denom_value;
     706          42 :     p->coin_pub = dr.coin_pub;
     707          42 :     p->amount_with_fee = coin->amount_with_fee;
     708          42 :     p->amount_without_fee = coin->amount_without_fee;
     709          42 :     p->refund_fee = coin->refund_fee;
     710          42 :     p->exchange_url = coin->exchange_url;
     711             :   }
     712          30 :   return request_pay_generic (ctx,
     713             :                               merchant_url,
     714             :                               merchant_pub,
     715             :                               order_id,
     716             :                               num_coins,
     717             :                               pc,
     718             :                               mode,
     719             :                               pay_cb,
     720             :                               pay_cb_cls,
     721             :                               abort_cb,
     722             :                               abort_cb_cls);
     723             : }
     724             : 
     725             : 
     726             : /**
     727             :  * Pay a merchant.  API for wallets that have the coin's private keys.
     728             :  * _NOTE_: this function does NOT calculate each coin amount in order
     729             :  * to match the contract total price.  This calculation is to be made
     730             :  * by the logic using this library.
     731             :  *
     732             :  * @param ctx the execution loop context
     733             :  * @param merchant_url base URL of the merchant's backend
     734             :  * @param instance which merchant instance will receive this payment
     735             :  * @param h_contract_terms hashcode of the proposal being paid
     736             :  * @param amount total value of the contract to be paid to the merchant
     737             :  * @param max_fee maximum fee covered by the merchant (according to the contract)
     738             :  * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
     739             :  * @param merchant_sig signature from the merchant over the original contract
     740             :  * @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant
     741             :  * @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)
     742             :  * @param pay_deadline maximum time limit to pay for this contract
     743             :  * @param h_wire hash of the merchant’s account details
     744             :  * @param order_id order id of the proposal being paid
     745             :  * @param num_coins number of coins used to pay
     746             :  * @param coins array of coins we use to pay
     747             :  * @param pay_cb the callback to call when a reply for this request is available
     748             :  * @param pay_cb_cls closure for @a pay_cb
     749             :  * @return a handle for this request
     750             :  */
     751             : struct TALER_MERCHANT_Pay *
     752          24 : TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx,
     753             :                            const char *merchant_url,
     754             :                            const char *instance,
     755             :                            const struct GNUNET_HashCode *h_contract_terms,
     756             :                            const struct TALER_Amount *amount,
     757             :                            const struct TALER_Amount *max_fee,
     758             :                            const struct TALER_MerchantPublicKeyP *merchant_pub,
     759             :                            const struct TALER_MerchantSignatureP *merchant_sig,
     760             :                            struct GNUNET_TIME_Absolute timestamp,
     761             :                            struct GNUNET_TIME_Absolute refund_deadline,
     762             :                            struct GNUNET_TIME_Absolute pay_deadline,
     763             :                            const struct GNUNET_HashCode *h_wire,
     764             :                            const char *order_id,
     765             :                            unsigned int num_coins,
     766             :                            const struct TALER_MERCHANT_PayCoin *coins,
     767             :                            TALER_MERCHANT_PayCallback pay_cb,
     768             :                            void *pay_cb_cls)
     769             : {
     770          24 :   return prepare_pay_generic (ctx,
     771             :                               merchant_url,
     772             :                               instance,
     773             :                               h_contract_terms,
     774             :                               amount,
     775             :                               max_fee,
     776             :                               merchant_pub,
     777             :                               merchant_sig,
     778             :                               timestamp,
     779             :                               refund_deadline,
     780             :                               pay_deadline,
     781             :                               h_wire,
     782             :                               order_id,
     783             :                               num_coins,
     784             :                               coins,
     785             :                               "pay",
     786             :                               pay_cb,
     787             :                               pay_cb_cls,
     788             :                               NULL,
     789             :                               NULL);
     790             : }
     791             : 
     792             : 
     793             : /**
     794             :  * Run a payment abort operation, asking for refunds for coins
     795             :  * that were previously spend on a /pay that failed to go through.
     796             :  *
     797             :  * @param ctx execution context
     798             :  * @param merchant_url base URL of the merchant
     799             :  * @param instance which merchant instance will receive this payment
     800             :  * @param h_wire hash of the merchant’s account details
     801             :  * @param h_contract hash of the contact of the merchant with the customer
     802             :  * @param transaction_id transaction id for the transaction between merchant and customer
     803             :  * @param amount total value of the contract to be paid to the merchant
     804             :  * @param max_fee maximum fee covered by the merchant (according to the contract)
     805             :  * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
     806             :  * @param merchant_sig signature from the merchant over the original contract
     807             :  * @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant
     808             :  * @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)
     809             :  * @param pay_deadline maximum time limit to pay for this contract
     810             :  * @param num_coins number of coins used to pay
     811             :  * @param coins array of coins we use to pay
     812             :  * @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s private key.
     813             :  * @param payref_cb the callback to call when a reply for this request is available
     814             :  * @param payref_cb_cls closure for @a pay_cb
     815             :  * @return a handle for this request
     816             :  */
     817             : struct TALER_MERCHANT_Pay *
     818           6 : TALER_MERCHANT_pay_abort (struct GNUNET_CURL_Context *ctx,
     819             :                           const char *merchant_url,
     820             :                           const char *instance,
     821             :                           const struct GNUNET_HashCode *h_contract,
     822             :                           const struct TALER_Amount *amount,
     823             :                           const struct TALER_Amount *max_fee,
     824             :                           const struct TALER_MerchantPublicKeyP *merchant_pub,
     825             :                           const struct TALER_MerchantSignatureP *merchant_sig,
     826             :                           struct GNUNET_TIME_Absolute timestamp,
     827             :                           struct GNUNET_TIME_Absolute refund_deadline,
     828             :                           struct GNUNET_TIME_Absolute pay_deadline,
     829             :                           const struct GNUNET_HashCode *h_wire,
     830             :                           const char *order_id,
     831             :                           unsigned int num_coins,
     832             :                           const struct TALER_MERCHANT_PayCoin *coins,
     833             :                           TALER_MERCHANT_PayRefundCallback payref_cb,
     834             :                           void *payref_cb_cls)
     835             : {
     836             :   struct TALER_MERCHANT_Pay *ph;
     837             : 
     838           6 :   ph = prepare_pay_generic (ctx,
     839             :                             merchant_url,
     840             :                             instance,
     841             :                             h_contract,
     842             :                             amount,
     843             :                             max_fee,
     844             :                             merchant_pub,
     845             :                             merchant_sig,
     846             :                             timestamp,
     847             :                             refund_deadline,
     848             :                             pay_deadline,
     849             :                             h_wire,
     850             :                             order_id,
     851             :                             num_coins,
     852             :                             coins,
     853             :                             "abort-refund",
     854             :                             NULL,
     855             :                             NULL,
     856             :                             payref_cb,
     857             :                             payref_cb_cls);
     858           6 :   ph->h_contract_terms = *h_contract;
     859           6 :   return ph;
     860             : }
     861             : 
     862             : 
     863             : /**
     864             :  * PAY a merchant.  API for frontends talking to backends. Here,
     865             :  * the frontend does not have the coin's private keys, but just
     866             :  * the public keys and signatures.  Note the subtle difference
     867             :  * in the type of @a coins compared to #TALER_MERCHANT_pay().
     868             :  *
     869             :  * @param ctx the execution loop context
     870             :  * @param merchant_url base URL of the merchant's backend
     871             :  * @param merchant_pub public key of the merchant
     872             :  * @param num_coins number of coins used to pay
     873             :  * @param coins array of coins we use to pay
     874             :  * @param pay_cb the callback to call when a reply for this request is available
     875             :  * @param pay_cb_cls closure for @a pay_cb
     876             :  * @return a handle for this request
     877             :  */
     878             : struct TALER_MERCHANT_Pay *
     879           0 : TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx,
     880             :                              const char *merchant_url,
     881             :                              const struct TALER_MerchantPublicKeyP *merchant_pub,
     882             :                              const char *order_id,
     883             :                              unsigned int num_coins,
     884             :                              const struct TALER_MERCHANT_PaidCoin *coins,
     885             :                              TALER_MERCHANT_PayCallback pay_cb,
     886             :                              void *pay_cb_cls)
     887             : {
     888           0 :   return request_pay_generic (ctx,
     889             :                               merchant_url,
     890             :                               merchant_pub,
     891             :                               order_id,
     892             :                               num_coins,
     893             :                               coins,
     894             :                               "pay",
     895             :                               pay_cb,
     896             :                               pay_cb_cls,
     897             :                               NULL,
     898             :                               NULL);
     899             : }
     900             : 
     901             : 
     902             : /**
     903             :  * Cancel a pay permission request.  This function cannot be used
     904             :  * on a request handle if a response is already served for it.
     905             :  *
     906             :  * @param pay the pay permission request handle
     907             :  */
     908             : void
     909          30 : TALER_MERCHANT_pay_cancel (struct TALER_MERCHANT_Pay *pay)
     910             : {
     911          30 :   if (NULL != pay->job)
     912             :   {
     913           0 :     GNUNET_CURL_job_cancel (pay->job);
     914           0 :     pay->job = NULL;
     915             :   }
     916          30 :   GNUNET_free (pay->coins);
     917          30 :   GNUNET_free (pay->url);
     918          30 :   GNUNET_free (pay->json_enc);
     919          30 :   GNUNET_free (pay);
     920          30 : }
     921             : 
     922             : 
     923             : /* end of merchant_api_pay.c */

Generated by: LCOV version 1.13