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 22 : parse_tokens (const json_t *token_sigs,
137 : struct TALER_MERCHANT_OutputToken **tokens,
138 : unsigned int *num_tokens)
139 : {
140 22 : GNUNET_array_grow (*tokens,
141 : *num_tokens,
142 : json_array_size (token_sigs));
143 :
144 26 : for (unsigned int i = 0; i<(*num_tokens); i++)
145 : {
146 4 : struct TALER_MERCHANT_OutputToken *token = &(*tokens)[i];
147 : struct GNUNET_JSON_Specification spec[] = {
148 4 : TALER_JSON_spec_blinded_token_issue_sig ("blind_sig",
149 : &token->blinded_sig),
150 4 : GNUNET_JSON_spec_end ()
151 : };
152 : const json_t *jtoken
153 4 : = json_array_get (token_sigs,
154 : i);
155 :
156 4 : if (NULL == jtoken)
157 : {
158 0 : GNUNET_break (0);
159 0 : return GNUNET_SYSERR;
160 : }
161 4 : if (GNUNET_OK !=
162 4 : GNUNET_JSON_parse (jtoken,
163 : spec,
164 : NULL, NULL))
165 : {
166 0 : GNUNET_break (0);
167 0 : return GNUNET_SYSERR;
168 : }
169 : }
170 :
171 22 : 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 34 : handle_pay_finished (void *cls,
185 : long response_code,
186 : const void *response)
187 : {
188 34 : struct TALER_MERCHANT_OrderPayHandle *oph = cls;
189 34 : const json_t *json = response;
190 34 : struct TALER_MERCHANT_PayResponse pr = {
191 34 : .hr.http_status = (unsigned int) response_code,
192 : .hr.reply = json
193 : };
194 :
195 34 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
196 : "Received /pay response with status code %u\n",
197 : (unsigned int) response_code);
198 :
199 34 : json_dumpf (json,
200 : stderr,
201 : JSON_INDENT (2));
202 :
203 34 : oph->job = NULL;
204 34 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
205 : "/pay completed with response code %u\n",
206 : (unsigned int) response_code);
207 34 : switch (response_code)
208 : {
209 0 : case 0:
210 0 : pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
211 0 : break;
212 22 : case MHD_HTTP_OK:
213 22 : if (oph->am_wallet)
214 : {
215 22 : const json_t *token_sigs = NULL;
216 : struct GNUNET_JSON_Specification spec[] = {
217 22 : GNUNET_JSON_spec_fixed_auto ("sig",
218 : &pr.details.ok.merchant_sig),
219 22 : GNUNET_JSON_spec_mark_optional (
220 : GNUNET_JSON_spec_string ("pos_confirmation",
221 : &pr.details.ok.pos_confirmation),
222 : NULL),
223 22 : GNUNET_JSON_spec_mark_optional (
224 : GNUNET_JSON_spec_array_const ("token_sigs",
225 : &token_sigs),
226 : NULL),
227 22 : GNUNET_JSON_spec_end ()
228 : };
229 :
230 22 : if (GNUNET_OK !=
231 22 : 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 22 : if (GNUNET_OK !=
243 22 : 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 22 : if (GNUNET_OK !=
255 22 : TALER_merchant_pay_verify (&oph->h_contract_terms,
256 22 : &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 22 : 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 2 : case MHD_HTTP_BAD_REQUEST:
274 2 : pr.hr.ec = TALER_JSON_get_error_code (json);
275 2 : 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 2 : 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 10 : case MHD_HTTP_CONFLICT:
304 10 : TALER_MERCHANT_parse_error_details_ (json,
305 : MHD_HTTP_CONFLICT,
306 : &pr.hr);
307 10 : 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 34 : oph->pay_cb (oph->pay_cb_cls,
412 : &pr);
413 34 : TALER_MERCHANT_order_pay_cancel (oph);
414 : }
415 :
416 :
417 : struct TALER_MERCHANT_OrderPayHandle *
418 34 : 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 34 : {
432 : struct TALER_MERCHANT_OrderPayHandle *oph;
433 : json_t *pay_obj;
434 : json_t *j_coins;
435 34 : json_t *j_tokens = NULL;
436 : CURL *eh;
437 : struct TALER_Amount total_fee;
438 : struct TALER_Amount total_amount;
439 :
440 34 : j_coins = json_array ();
441 34 : GNUNET_assert (NULL != j_coins);
442 74 : for (unsigned int i = 0; i<num_coins; i++)
443 : {
444 : json_t *j_coin;
445 40 : const struct TALER_MERCHANT_PaidCoin *pc = &coins[i];
446 : struct TALER_Amount fee;
447 : struct TALER_DenominationHashP denom_hash;
448 :
449 40 : if (0 >
450 40 : 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 40 : if (0 == i)
461 : {
462 30 : total_fee = fee;
463 30 : total_amount = pc->amount_with_fee;
464 : }
465 : else
466 : {
467 10 : if ( (0 >
468 10 : TALER_amount_add (&total_fee,
469 : &total_fee,
470 10 : &fee)) ||
471 : (0 >
472 10 : 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 40 : TALER_denom_pub_hash (&pc->denom_pub,
484 : &denom_hash);
485 : /* create JSON for this coin */
486 40 : 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 40 : if (0 !=
500 40 : 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 34 : if (0 < num_tokens)
510 : {
511 4 : j_tokens = json_array ();
512 4 : GNUNET_assert (NULL != j_tokens);
513 8 : for (unsigned int i = 0; i<num_tokens; i++)
514 : {
515 : json_t *j_token;
516 4 : const struct TALER_MERCHANT_UsedToken *ut = &tokens[i];
517 :
518 4 : 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 4 : if (0 !=
528 4 : 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 34 : 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 34 : json_dumpf (pay_obj,
552 : stderr,
553 : JSON_INDENT (2));
554 :
555 34 : oph = GNUNET_new (struct TALER_MERCHANT_OrderPayHandle);
556 34 : oph->ctx = ctx;
557 34 : oph->pay_cb = pay_cb;
558 34 : oph->pay_cb_cls = pay_cb_cls;
559 : {
560 : char *path;
561 :
562 34 : GNUNET_asprintf (&path,
563 : "orders/%s/pay",
564 : order_id);
565 34 : oph->url = TALER_url_join (merchant_url,
566 : path,
567 : NULL);
568 34 : GNUNET_free (path);
569 : }
570 34 : 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 34 : oph->num_coins = num_coins;
579 34 : oph->coins = GNUNET_new_array (num_coins,
580 : struct TALER_MERCHANT_PaidCoin);
581 34 : GNUNET_memcpy (oph->coins,
582 : coins,
583 : num_coins * sizeof (struct TALER_MERCHANT_PaidCoin));
584 :
585 34 : eh = TALER_MERCHANT_curl_easy_get_ (oph->url);
586 34 : if (GNUNET_OK !=
587 34 : 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 34 : json_decref (pay_obj);
599 68 : oph->job = GNUNET_CURL_job_add2 (ctx,
600 : eh,
601 34 : oph->post_ctx.headers,
602 : &handle_pay_finished,
603 : oph);
604 34 : return oph;
605 : }
606 :
607 :
608 : struct TALER_MERCHANT_OrderPayHandle *
609 34 : 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 34 : {
634 34 : json_t *j_output_tokens = NULL;
635 34 : const json_t *wallet_data = NULL;
636 : struct GNUNET_HashCode wallet_data_hash;
637 :
638 34 : if (GNUNET_YES !=
639 34 : TALER_amount_cmp_currency (amount,
640 : max_fee))
641 : {
642 0 : GNUNET_break (0);
643 0 : return NULL;
644 : }
645 34 : 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 34 : if (0 < num_output_tokens)
653 : {
654 : /* Build token envelopes json array. */
655 6 : j_output_tokens = json_array ();
656 6 : GNUNET_assert (NULL != j_output_tokens);
657 12 : for (unsigned int i = 0; i<num_output_tokens; i++)
658 : {
659 : json_t *j_token_ev;
660 6 : const struct TALER_MERCHANT_OutputToken *ev = &output_tokens[i];
661 :
662 6 : j_token_ev = GNUNET_JSON_PACK (
663 : TALER_JSON_pack_token_envelope (NULL,
664 : &ev->envelope));
665 :
666 6 : if (0 !=
667 6 : 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 34 : if (0 <= choice_index)
677 : {
678 6 : 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 6 : TALER_json_hash (wallet_data,
685 : &wallet_data_hash);
686 : }
687 34 : {
688 34 : struct TALER_MERCHANT_PaidCoin pc[num_coins];
689 34 : struct TALER_MERCHANT_UsedToken ut[num_tokens];
690 :
691 74 : for (unsigned int i = 0; i<num_coins; i++)
692 : {
693 40 : const struct TALER_MERCHANT_PayCoin *coin = &coins[i]; // coin priv.
694 40 : struct TALER_MERCHANT_PaidCoin *p = &pc[i]; // coin pub.
695 : struct TALER_Amount fee;
696 : struct TALER_DenominationHashP h_denom_pub;
697 :
698 40 : if (0 >
699 40 : 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 40 : TALER_denom_pub_hash (&coin->denom_pub,
709 : &h_denom_pub);
710 40 : 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 40 : 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 40 : p->denom_pub = coin->denom_pub;
726 40 : p->denom_sig = coin->denom_sig;
727 40 : p->denom_value = coin->denom_value;
728 40 : GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv,
729 : &p->coin_pub.eddsa_pub);
730 40 : p->amount_with_fee = coin->amount_with_fee;
731 40 : p->amount_without_fee = coin->amount_without_fee;
732 40 : p->exchange_url = coin->exchange_url;
733 : }
734 38 : for (unsigned int i = 0; i<num_tokens; i++)
735 : {
736 4 : const struct TALER_MERCHANT_UseToken *token = &tokens[i];
737 4 : struct TALER_MERCHANT_UsedToken *t = &ut[i];
738 :
739 4 : 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 4 : t->ub_sig = token->ub_sig;
744 4 : t->issue_pub = token->issue_pub;
745 4 : 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 34 : 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 34 : if (NULL == oph)
764 0 : return NULL;
765 34 : oph->h_contract_terms = *h_contract_terms;
766 34 : oph->merchant_pub = *merchant_pub;
767 34 : oph->am_wallet = true;
768 34 : return oph;
769 : }
770 : }
771 : }
772 :
773 :
774 : void
775 34 : TALER_MERCHANT_order_pay_cancel (struct TALER_MERCHANT_OrderPayHandle *oph)
776 : {
777 34 : if (NULL != oph->job)
778 : {
779 0 : GNUNET_CURL_job_cancel (oph->job);
780 0 : oph->job = NULL;
781 : }
782 34 : TALER_curl_easy_post_finished (&oph->post_ctx);
783 34 : json_decref (oph->error_history);
784 34 : json_decref (oph->full_reply);
785 34 : GNUNET_free (oph->coins);
786 34 : GNUNET_free (oph->url);
787 34 : GNUNET_free (oph);
788 34 : }
789 :
790 :
791 : /* end of merchant_api_post_order_pay.c */
|