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