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-07-14 06:17:23 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           6 :       for (unsigned int j=0;j<ph->num_coins;j++)
     171             :       {
     172           3 :         if (0 == memcmp (&ph->coins[j].coin_pub,
     173           3 :                          &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          12 :   for (unsigned int i=0;i<ph->num_coins;i++)
     295             :   {
     296          12 :     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          34 : handle_pay_finished (void *cls,
     323             :                      long response_code,
     324             :                      const json_t *json)
     325             : {
     326          34 :   struct TALER_MERCHANT_Pay *ph = cls;
     327             : 
     328          34 :   ph->job = NULL;
     329          34 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     330             :               "/pay completed with response code %u\n",
     331             :               (unsigned int) response_code);
     332          34 :   if (0 == strcasecmp (ph->mode,
     333             :                        "pay"))
     334             :   {
     335          28 :     switch (response_code)
     336             :     {
     337             :     case 0:
     338           0 :       break;
     339             :     case MHD_HTTP_OK:
     340             :     /* Tolerating Not Acceptable because sometimes
     341             :      * - especially in tests - we might want to POST
     342             :      * coins one at a time.  */
     343             :     case MHD_HTTP_NOT_ACCEPTABLE:
     344          19 :       break;
     345             :     case MHD_HTTP_BAD_REQUEST:
     346             :       /* This should never happen, either us or the merchant is buggy
     347             :          (or API version conflict); just pass JSON reply to the application */
     348           0 :       break;
     349             :     case MHD_HTTP_FORBIDDEN:
     350           9 :       if (GNUNET_OK != check_forbidden (ph,
     351             :                                         json))
     352             :       {
     353           0 :         GNUNET_break_op (0);
     354           0 :         response_code = 0;
     355             :       }
     356           9 :       break;
     357             :     case MHD_HTTP_UNAUTHORIZED:
     358             :       /* Nothing really to verify, merchant says one of the signatures is
     359             :          invalid; as we checked them, this should never happen, we
     360             :          should pass the JSON reply to the application */
     361           0 :       break;
     362             :     case MHD_HTTP_NOT_FOUND:
     363             :       /* Nothing really to verify, this should never
     364             :          happen, we should pass the JSON reply to the application */
     365           0 :       break;
     366             :     case MHD_HTTP_INTERNAL_SERVER_ERROR:
     367             :       /* Server had an internal issue; we should retry, but this API
     368             :          leaves this to the application */
     369           0 :       break;
     370             :     default:
     371             :       /* unexpected response code */
     372           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     373             :                   "Unexpected response code %u\n",
     374             :                   (unsigned int) response_code);
     375           0 :       GNUNET_break (0);
     376           0 :       response_code = 0;
     377           0 :       break;
     378             :     }
     379          28 :     ph->pay_cb (ph->pay_cb_cls,
     380             :                 response_code,
     381             :                 TALER_JSON_get_error_code (json),
     382             :                 json);
     383             :   }
     384             :   else
     385             :   {
     386           6 :     GNUNET_assert (0 == strcasecmp (ph->mode,
     387             :                                     "abort-refund"));
     388             : 
     389           6 :     switch (response_code)
     390             :     {
     391             :     case 0:
     392           0 :       break;
     393             :     case MHD_HTTP_OK:
     394           3 :       if (GNUNET_OK ==
     395           3 :           check_abort_refund (ph,
     396             :                               json))
     397             :       {
     398           3 :         TALER_MERCHANT_pay_cancel (ph);
     399           3 :         return;
     400             :       }
     401           0 :       response_code = 0;
     402           0 :       break;
     403             :     case MHD_HTTP_BAD_REQUEST:
     404             :       /* This should never happen, either us or the merchant is buggy
     405             :          (or API version conflict); just pass JSON reply to the application */
     406           0 :       break;
     407             :     case MHD_HTTP_FORBIDDEN:
     408           3 :       break;
     409             :     case MHD_HTTP_UNAUTHORIZED:
     410             :       /* Nothing really to verify, merchant says one of the signatures is
     411             :          invalid; as we checked them, this should never happen, we
     412             :          should pass the JSON reply to the application */
     413           0 :       break;
     414             :     case MHD_HTTP_NOT_FOUND:
     415             :       /* Nothing really to verify, this should never
     416             :          happen, we should pass the JSON reply to the application */
     417           0 :       break;
     418             :     case MHD_HTTP_INTERNAL_SERVER_ERROR:
     419             :       /* Server had an internal issue; we should retry, but this API
     420             :          leaves this to the application */
     421           0 :       break;
     422             :     default:
     423             :       /* unexpected response code */
     424           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     425             :                   "Unexpected response code %u\n",
     426             :                   (unsigned int) response_code);
     427           0 :       GNUNET_break (0);
     428           0 :       response_code = 0;
     429           0 :       break;
     430             :     }
     431           3 :     ph->abort_cb (ph->abort_cb_cls,
     432             :                   response_code,
     433             :                   TALER_JSON_get_error_code (json),
     434             :                   NULL,
     435             :                   NULL,
     436             :                   0,
     437             :                   NULL,
     438             :                   json);
     439             :   }
     440             : 
     441          31 :   TALER_MERCHANT_pay_cancel (ph);
     442             : }
     443             : 
     444             : 
     445             : /**
     446             :  * Issue /pay request. Generic version for the various
     447             :  * variants of the API.
     448             :  *
     449             :  * @param ctx the execution loop context
     450             :  * @param merchant_url base URL of the merchant's backend
     451             :  * @param merchant_pub public key of the merchant
     452             :  * @param num_coins number of coins used to pay
     453             :  * @param coins array of coins we use to pay
     454             :  * @param mode mode string to use ("pay" or "abort-refund").
     455             :  * @param pay_cb the callback to call when a reply for this request is available
     456             :  * @param pay_cb_cls closure for @a pay_cb
     457             :  * @param abort_cb callback to call for the abort-refund variant
     458             :  * @param abort_cb_cls closure for @a abort_cb
     459             :  * @return a handle for this request
     460             :  */
     461             : static struct TALER_MERCHANT_Pay *
     462          34 : request_pay_generic (struct GNUNET_CURL_Context *ctx,
     463             :                      const char *merchant_url,
     464             :                      const struct TALER_MerchantPublicKeyP *merchant_pub,
     465             :                      const char *order_id,
     466             :                      unsigned int num_coins,
     467             :                      const struct TALER_MERCHANT_PaidCoin *coins,
     468             :                      const char *mode,
     469             :                      TALER_MERCHANT_PayCallback pay_cb,
     470             :                      void *pay_cb_cls,
     471             :                      TALER_MERCHANT_PayRefundCallback abort_cb,
     472             :                      void *abort_cb_cls)
     473             : {
     474             :   struct TALER_MERCHANT_Pay *ph;
     475             :   json_t *pay_obj;
     476             :   json_t *j_coins;
     477             :   CURL *eh;
     478             :   struct TALER_Amount total_fee;
     479             :   struct TALER_Amount total_amount;
     480             : 
     481          34 :   if (0 == num_coins)
     482             :   {
     483           0 :     GNUNET_break (0);
     484           0 :     return NULL;
     485             :   }
     486          34 :   j_coins = json_array ();
     487         148 :   for (unsigned int i=0;i<num_coins;i++)
     488             :   {
     489             :     json_t *j_coin;
     490          40 :     const struct TALER_MERCHANT_PaidCoin *pc = &coins[i];
     491             :     struct TALER_Amount fee;
     492             : 
     493          40 :     if (GNUNET_SYSERR ==
     494          40 :         TALER_amount_subtract (&fee,
     495             :                                &pc->amount_with_fee,
     496             :                                &pc->amount_without_fee))
     497             :     {
     498             :       /* Integer underflow, fee larger than total amount?
     499             :          This should not happen (client violated API!) */
     500           0 :       GNUNET_break (0);
     501           0 :       json_decref (j_coins);
     502           0 :       return NULL;
     503             :     }
     504          40 :     if (0 == i)
     505             :     {
     506          34 :       total_fee = fee;
     507          34 :       total_amount = pc->amount_with_fee;
     508             :     }
     509             :     else
     510             :     {
     511           6 :       if ( (GNUNET_OK !=
     512           6 :             TALER_amount_add (&total_fee,
     513             :                               &total_fee,
     514           6 :                               &fee)) ||
     515             :            (GNUNET_OK !=
     516           6 :             TALER_amount_add (&total_amount,
     517             :                               &total_amount,
     518             :                               &pc->amount_with_fee)) )
     519             :       {
     520             :         /* integer overflow */
     521           0 :         GNUNET_break (0);
     522           0 :         json_decref (j_coins);
     523           0 :         return NULL;
     524             :       }
     525             :     }
     526             : 
     527             :     /* create JSON for this coin */
     528         160 :     j_coin = json_pack ("{s:o, s:o," /* contribution/coin_pub */
     529             :                         " s:s, s:o," /* exchange_url / denom_pub */
     530             :                         " s:o, s:o}", /* ub_sig / coin_sig */
     531             :                         "contribution", TALER_JSON_from_amount (&pc->amount_with_fee),
     532          40 :                         "coin_pub", GNUNET_JSON_from_data_auto (&pc->coin_pub),
     533             :                         "exchange_url", pc->exchange_url,
     534          40 :                         "denom_pub", GNUNET_JSON_from_rsa_public_key (pc->denom_pub.rsa_public_key),
     535          40 :                         "ub_sig", GNUNET_JSON_from_rsa_signature (pc->denom_sig.rsa_signature),
     536          40 :                         "coin_sig", GNUNET_JSON_from_data_auto (&pc->coin_sig)
     537             :                         );
     538          40 :     if (0 !=
     539          40 :         json_array_append_new (j_coins,
     540             :                                j_coin))
     541             :     {
     542           0 :       GNUNET_break (0);
     543           0 :       json_decref (j_coins);
     544           0 :       return NULL;
     545             :     }
     546             :   }
     547             : 
     548          34 :   pay_obj = json_pack ("{"
     549             :                        " s:s," /* mode */
     550             :                        " s:o," /* coins */
     551             :                        " s:s," /* order_id */
     552             :                        " s:o," /* merchant_pub */
     553             :                        "}",
     554             :                        "mode", mode,
     555             :                        "coins", j_coins,
     556             :                        "order_id", order_id,
     557             :                        "merchant_pub", GNUNET_JSON_from_data_auto (merchant_pub));
     558          34 :   if (NULL == pay_obj)
     559             :   {
     560           0 :     GNUNET_break (0);
     561           0 :     return NULL;
     562             :   }
     563          34 :   ph = GNUNET_new (struct TALER_MERCHANT_Pay);
     564          34 :   ph->ctx = ctx;
     565          34 :   ph->mode = mode;
     566          34 :   ph->abort_cb = abort_cb;
     567          34 :   ph->abort_cb_cls = abort_cb_cls;
     568          34 :   ph->pay_cb = pay_cb;
     569          34 :   ph->pay_cb_cls = pay_cb_cls;
     570          34 :   ph->url = TALER_url_join (merchant_url, "/public/pay", NULL);
     571          34 :   ph->num_coins = num_coins;
     572          34 :   ph->coins = GNUNET_new_array (num_coins,
     573             :                                 struct TALER_MERCHANT_PaidCoin);
     574          34 :   memcpy (ph->coins,
     575             :           coins,
     576             :           num_coins * sizeof (struct TALER_MERCHANT_PaidCoin));
     577             : 
     578          34 :   eh = curl_easy_init ();
     579          34 :   GNUNET_assert (NULL != (ph->json_enc =
     580             :                           json_dumps (pay_obj,
     581             :                                       JSON_COMPACT)));
     582          34 :   json_decref (pay_obj);
     583          34 :   GNUNET_assert (CURLE_OK ==
     584             :                  curl_easy_setopt (eh,
     585             :                                    CURLOPT_URL,
     586             :                                    ph->url));
     587          34 :   GNUNET_assert (CURLE_OK ==
     588             :                  curl_easy_setopt (eh,
     589             :                                    CURLOPT_POSTFIELDS,
     590             :                                    ph->json_enc));
     591          34 :   GNUNET_assert (CURLE_OK ==
     592             :                  curl_easy_setopt (eh,
     593             :                                    CURLOPT_POSTFIELDSIZE,
     594             :                                    strlen (ph->json_enc)));
     595          34 :   ph->job = GNUNET_CURL_job_add (ctx,
     596             :                                  eh,
     597             :                                  GNUNET_YES,
     598             :                                  &handle_pay_finished,
     599             :                                  ph);
     600          34 :   return ph;
     601             : }
     602             : 
     603             : 
     604             : /**
     605             :  * Pay a merchant.  API for wallets that have the coin's private keys.
     606             :  * _NOTE_: this function does NOT calculate each coin amount in order
     607             :  * to match the contract total price.  This calculation is to be made
     608             :  * by the logic using this library.
     609             :  *
     610             :  * @param ctx the execution loop context
     611             :  * @param merchant_url base URL of the merchant's backend
     612             :  * @param instance which merchant instance will receive this payment
     613             :  * @param h_contract_terms hashcode of the proposal being paid
     614             :  * @param amount total value of the contract to be paid to the merchant
     615             :  * @param max_fee maximum fee covered by the merchant (according to the contract)
     616             :  * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
     617             :  * @param merchant_sig signature from the merchant over the original contract
     618             :  * @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant
     619             :  * @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)
     620             :  * @param pay_deadline maximum time limit to pay for this contract
     621             :  * @param h_wire hash of the merchant’s account details
     622             :  * @param order_id order id of the proposal being paid
     623             :  * @param num_coins number of coins used to pay
     624             :  * @param coins array of coins we use to pay
     625             :  * @param pay_cb the callback to call when a reply for this request is available
     626             :  * @param pay_cb_cls closure for @a pay_cb
     627             :  * @return a handle for this request
     628             :  */
     629             : static struct TALER_MERCHANT_Pay *
     630          34 : prepare_pay_generic (struct GNUNET_CURL_Context *ctx,
     631             :                      const char *merchant_url,
     632             :                      const char *instance,
     633             :                      const struct GNUNET_HashCode *h_contract_terms,
     634             :                      const struct TALER_Amount *amount,
     635             :                      const struct TALER_Amount *max_fee,
     636             :                      const struct TALER_MerchantPublicKeyP *merchant_pub,
     637             :                      const struct TALER_MerchantSignatureP *merchant_sig,
     638             :                      struct GNUNET_TIME_Absolute timestamp,
     639             :                      struct GNUNET_TIME_Absolute refund_deadline,
     640             :                      struct GNUNET_TIME_Absolute pay_deadline,
     641             :                      const struct GNUNET_HashCode *h_wire,
     642             :                      const char *order_id,
     643             :                      unsigned int num_coins,
     644             :                      const struct TALER_MERCHANT_PayCoin *coins,
     645             :                      const char *mode,
     646             :                      TALER_MERCHANT_PayCallback pay_cb,
     647             :                      void *pay_cb_cls,
     648             :                      TALER_MERCHANT_PayRefundCallback abort_cb,
     649             :                      void *abort_cb_cls)
     650          34 : {
     651             :   struct TALER_DepositRequestPS dr;
     652          34 :   struct TALER_MERCHANT_PaidCoin pc[num_coins];
     653             : 
     654          34 :   (void) GNUNET_TIME_round_abs (&timestamp);
     655          34 :   (void) GNUNET_TIME_round_abs (&pay_deadline);
     656          34 :   (void) GNUNET_TIME_round_abs (&refund_deadline);
     657             : 
     658          34 :   if (GNUNET_YES !=
     659          34 :       TALER_amount_cmp_currency (amount,
     660             :                                  max_fee))
     661             :   {
     662           0 :     GNUNET_break (0);
     663           0 :     return NULL;
     664             :   }
     665             : 
     666          34 :   dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
     667          34 :   dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
     668          34 :   dr.h_contract_terms = *h_contract_terms;
     669          34 :   dr.h_wire = *h_wire;
     670          34 :   dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
     671          34 :   dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
     672          34 :   dr.merchant = *merchant_pub;
     673             : 
     674          74 :   for (unsigned int i=0;i<num_coins;i++)
     675             :   {
     676          40 :     const struct TALER_MERCHANT_PayCoin *coin = &coins[i]; // coin priv.
     677          40 :     struct TALER_MERCHANT_PaidCoin *p = &pc[i]; // coin pub.
     678             :     struct TALER_Amount fee;
     679             : 
     680             :     /* prepare 'dr' for this coin to generate coin signature */
     681          40 :     GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv,
     682             :                                         &dr.coin_pub.eddsa_pub);
     683          40 :     TALER_amount_hton (&dr.amount_with_fee,
     684             :                        &coin->amount_with_fee);
     685          40 :     if (GNUNET_SYSERR ==
     686          40 :         TALER_amount_subtract (&fee,
     687             :                                &coin->amount_with_fee,
     688             :                                &coin->amount_without_fee))
     689             :     {
     690             :       /* Integer underflow, fee larger than total amount?
     691             :          This should not happen (client violated API!) */
     692           0 :       GNUNET_break (0);
     693           0 :       return NULL;
     694             :     }
     695          40 :     TALER_amount_hton (&dr.deposit_fee,
     696             :                        &fee);
     697             :     {
     698          40 :       TALER_LOG_DEBUG ("... amount_with_fee was %s\n",
     699             :                        TALER_amount2s (&coin->amount_with_fee));
     700          40 :       TALER_LOG_DEBUG ("... fee was %s\n",
     701             :                        TALER_amount2s (&fee));
     702             :     }
     703             : 
     704          40 :     GNUNET_CRYPTO_eddsa_sign (&coin->coin_priv.eddsa_priv,
     705             :                               &dr.purpose,
     706             :                               &p->coin_sig.eddsa_signature);
     707          40 :     p->denom_pub = coin->denom_pub;
     708          40 :     p->denom_sig = coin->denom_sig;
     709          40 :     p->denom_value = coin->denom_value;
     710          40 :     p->coin_pub = dr.coin_pub;
     711          40 :     p->amount_with_fee = coin->amount_with_fee;
     712          40 :     p->amount_without_fee = coin->amount_without_fee;
     713          40 :     p->refund_fee = coin->refund_fee;
     714          40 :     p->exchange_url = coin->exchange_url;
     715             :   }
     716          34 :   return request_pay_generic (ctx,
     717             :                               merchant_url,
     718             :                               merchant_pub,
     719             :                               order_id,
     720             :                               num_coins,
     721             :                               pc,
     722             :                               mode,
     723             :                               pay_cb,
     724             :                               pay_cb_cls,
     725             :                               abort_cb,
     726             :                               abort_cb_cls);
     727             : }
     728             : 
     729             : 
     730             : /**
     731             :  * Pay a merchant.  API for wallets that have the coin's private keys.
     732             :  * _NOTE_: this function does NOT calculate each coin amount in order
     733             :  * to match the contract total price.  This calculation is to be made
     734             :  * by the logic using this library.
     735             :  *
     736             :  * @param ctx the execution loop context
     737             :  * @param merchant_url base URL of the merchant's backend
     738             :  * @param instance which merchant instance will receive this payment
     739             :  * @param h_contract_terms hashcode of the proposal being paid
     740             :  * @param amount total value of the contract to be paid to the merchant
     741             :  * @param max_fee maximum fee covered by the merchant (according to the contract)
     742             :  * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
     743             :  * @param merchant_sig signature from the merchant over the original contract
     744             :  * @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant
     745             :  * @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)
     746             :  * @param pay_deadline maximum time limit to pay for this contract
     747             :  * @param h_wire hash of the merchant’s account details
     748             :  * @param order_id order id of the proposal being paid
     749             :  * @param num_coins number of coins used to pay
     750             :  * @param coins array of coins we use to pay
     751             :  * @param pay_cb the callback to call when a reply for this request is available
     752             :  * @param pay_cb_cls closure for @a pay_cb
     753             :  * @return a handle for this request
     754             :  */
     755             : struct TALER_MERCHANT_Pay *
     756          28 : TALER_MERCHANT_pay_wallet (struct GNUNET_CURL_Context *ctx,
     757             :                            const char *merchant_url,
     758             :                            const char *instance,
     759             :                            const struct GNUNET_HashCode *h_contract_terms,
     760             :                            const struct TALER_Amount *amount,
     761             :                            const struct TALER_Amount *max_fee,
     762             :                            const struct TALER_MerchantPublicKeyP *merchant_pub,
     763             :                            const struct TALER_MerchantSignatureP *merchant_sig,
     764             :                            struct GNUNET_TIME_Absolute timestamp,
     765             :                            struct GNUNET_TIME_Absolute refund_deadline,
     766             :                            struct GNUNET_TIME_Absolute pay_deadline,
     767             :                            const struct GNUNET_HashCode *h_wire,
     768             :                            const char *order_id,
     769             :                            unsigned int num_coins,
     770             :                            const struct TALER_MERCHANT_PayCoin *coins,
     771             :                            TALER_MERCHANT_PayCallback pay_cb,
     772             :                            void *pay_cb_cls)
     773             : {
     774          28 :   return prepare_pay_generic (ctx,
     775             :                               merchant_url,
     776             :                               instance,
     777             :                               h_contract_terms,
     778             :                               amount,
     779             :                               max_fee,
     780             :                               merchant_pub,
     781             :                               merchant_sig,
     782             :                               timestamp,
     783             :                               refund_deadline,
     784             :                               pay_deadline,
     785             :                               h_wire,
     786             :                               order_id,
     787             :                               num_coins,
     788             :                               coins,
     789             :                               "pay",
     790             :                               pay_cb,
     791             :                               pay_cb_cls,
     792             :                               NULL,
     793             :                               NULL);
     794             : }
     795             : 
     796             : 
     797             : /**
     798             :  * Run a payment abort operation, asking for refunds for coins
     799             :  * that were previously spend on a /pay that failed to go through.
     800             :  *
     801             :  * @param ctx execution context
     802             :  * @param merchant_url base URL of the merchant
     803             :  * @param instance which merchant instance will receive this payment
     804             :  * @param h_wire hash of the merchant’s account details
     805             :  * @param h_contract hash of the contact of the merchant with the customer
     806             :  * @param transaction_id transaction id for the transaction between merchant and customer
     807             :  * @param amount total value of the contract to be paid to the merchant
     808             :  * @param max_fee maximum fee covered by the merchant (according to the contract)
     809             :  * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
     810             :  * @param merchant_sig signature from the merchant over the original contract
     811             :  * @param timestamp timestamp when the contract was finalized, must match approximately the current time of the merchant
     812             :  * @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)
     813             :  * @param pay_deadline maximum time limit to pay for this contract
     814             :  * @param num_coins number of coins used to pay
     815             :  * @param coins array of coins we use to pay
     816             :  * @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT made by the customer with the coin’s private key.
     817             :  * @param payref_cb the callback to call when a reply for this request is available
     818             :  * @param payref_cb_cls closure for @a pay_cb
     819             :  * @return a handle for this request
     820             :  */
     821             : struct TALER_MERCHANT_Pay *
     822           6 : TALER_MERCHANT_pay_abort (struct GNUNET_CURL_Context *ctx,
     823             :                           const char *merchant_url,
     824             :                           const char *instance,
     825             :                           const struct GNUNET_HashCode *h_contract,
     826             :                           const struct TALER_Amount *amount,
     827             :                           const struct TALER_Amount *max_fee,
     828             :                           const struct TALER_MerchantPublicKeyP *merchant_pub,
     829             :                           const struct TALER_MerchantSignatureP *merchant_sig,
     830             :                           struct GNUNET_TIME_Absolute timestamp,
     831             :                           struct GNUNET_TIME_Absolute refund_deadline,
     832             :                           struct GNUNET_TIME_Absolute pay_deadline,
     833             :                           const struct GNUNET_HashCode *h_wire,
     834             :                           const char *order_id,
     835             :                           unsigned int num_coins,
     836             :                           const struct TALER_MERCHANT_PayCoin *coins,
     837             :                           TALER_MERCHANT_PayRefundCallback payref_cb,
     838             :                           void *payref_cb_cls)
     839             : {
     840             :   struct TALER_MERCHANT_Pay *ph;
     841             : 
     842           6 :   ph = prepare_pay_generic (ctx,
     843             :                             merchant_url,
     844             :                             instance,
     845             :                             h_contract,
     846             :                             amount,
     847             :                             max_fee,
     848             :                             merchant_pub,
     849             :                             merchant_sig,
     850             :                             timestamp,
     851             :                             refund_deadline,
     852             :                             pay_deadline,
     853             :                             h_wire,
     854             :                             order_id,
     855             :                             num_coins,
     856             :                             coins,
     857             :                             "abort-refund",
     858             :                             NULL,
     859             :                             NULL,
     860             :                             payref_cb,
     861             :                             payref_cb_cls);
     862           6 :   ph->h_contract_terms = *h_contract;
     863           6 :   return ph;
     864             : }
     865             : 
     866             : 
     867             : /**
     868             :  * PAY a merchant.  API for frontends talking to backends. Here,
     869             :  * the frontend does not have the coin's private keys, but just
     870             :  * the public keys and signatures.  Note the subtle difference
     871             :  * in the type of @a coins compared to #TALER_MERCHANT_pay().
     872             :  *
     873             :  * @param ctx the execution loop context
     874             :  * @param merchant_url base URL of the merchant's backend
     875             :  * @param merchant_pub public key of the merchant
     876             :  * @param num_coins number of coins used to pay
     877             :  * @param coins array of coins we use to pay
     878             :  * @param pay_cb the callback to call when a reply for this request is available
     879             :  * @param pay_cb_cls closure for @a pay_cb
     880             :  * @return a handle for this request
     881             :  */
     882             : struct TALER_MERCHANT_Pay *
     883           0 : TALER_MERCHANT_pay_frontend (struct GNUNET_CURL_Context *ctx,
     884             :                              const char *merchant_url,
     885             :                              const struct TALER_MerchantPublicKeyP *merchant_pub,
     886             :                              const char *order_id,
     887             :                              unsigned int num_coins,
     888             :                              const struct TALER_MERCHANT_PaidCoin *coins,
     889             :                              TALER_MERCHANT_PayCallback pay_cb,
     890             :                              void *pay_cb_cls)
     891             : {
     892           0 :   return request_pay_generic (ctx,
     893             :                               merchant_url,
     894             :                               merchant_pub,
     895             :                               order_id,
     896             :                               num_coins,
     897             :                               coins,
     898             :                               "pay",
     899             :                               pay_cb,
     900             :                               pay_cb_cls,
     901             :                               NULL,
     902             :                               NULL);
     903             : }
     904             : 
     905             : 
     906             : /**
     907             :  * Cancel a pay permission request.  This function cannot be used
     908             :  * on a request handle if a response is already served for it.
     909             :  *
     910             :  * @param pay the pay permission request handle
     911             :  */
     912             : void
     913          34 : TALER_MERCHANT_pay_cancel (struct TALER_MERCHANT_Pay *pay)
     914             : {
     915          34 :   if (NULL != pay->job)
     916             :   {
     917           0 :     GNUNET_CURL_job_cancel (pay->job);
     918           0 :     pay->job = NULL;
     919             :   }
     920          34 :   GNUNET_free (pay->coins);
     921          34 :   GNUNET_free (pay->url);
     922          34 :   GNUNET_free (pay->json_enc);
     923          34 :   GNUNET_free (pay);
     924          34 : }
     925             : 
     926             : 
     927             : /* end of merchant_api_pay.c */

Generated by: LCOV version 1.13