Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2026 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 src/lib/merchant_api_post-orders-ORDER_ID-pay.c
21 : * @brief Implementation of the POST /orders/$ORDER_ID/pay request
22 : * @author Christian Grothoff
23 : * @author Marcello Stanisci
24 : */
25 : #include "platform.h"
26 : #include <curl/curl.h>
27 : #include <jansson.h>
28 : #include <microhttpd.h> /* just for HTTP status codes */
29 : #include <gnunet/gnunet_util_lib.h>
30 : #include <gnunet/gnunet_curl_lib.h>
31 : #include <taler/merchant/post-orders-ORDER_ID-pay.h>
32 : #include "merchant_api_curl_defaults.h"
33 : #include "merchant_api_common.h"
34 : #include <taler/taler_json_lib.h>
35 : #include <taler/taler_curl_lib.h>
36 : #include <taler/taler_signatures.h>
37 : #include <donau/donau_service.h>
38 : #include <donau/donau_json_lib.h>
39 :
40 : /**
41 : * Maximum number of exchange base URLs we accept in an (untrusted)
42 : * response before considering it malformed. Bounds the stack VLA used
43 : * to parse the array.
44 : */
45 : #define MAX_EXCHANGE_BASE_URLS 1024
46 :
47 : /**
48 : * Handle for a POST /orders/$ORDER_ID/pay operation.
49 : */
50 : struct TALER_MERCHANT_PostOrdersPayHandle
51 : {
52 : /**
53 : * Base URL of the merchant backend.
54 : */
55 : char *base_url;
56 :
57 : /**
58 : * The full URL for this request.
59 : */
60 : char *url;
61 :
62 : /**
63 : * Handle for the request.
64 : */
65 : struct GNUNET_CURL_Job *job;
66 :
67 : /**
68 : * Function to call with the result.
69 : */
70 : TALER_MERCHANT_PostOrdersPayCallback cb;
71 :
72 : /**
73 : * Closure for @a cb.
74 : */
75 : TALER_MERCHANT_POST_ORDERS_PAY_RESULT_CLOSURE *cb_cls;
76 :
77 : /**
78 : * Reference to the execution context.
79 : */
80 : struct GNUNET_CURL_Context *ctx;
81 :
82 : /**
83 : * Minor context that holds body and headers.
84 : */
85 : struct TALER_CURL_PostContext post_ctx;
86 :
87 : /**
88 : * Order identifier.
89 : */
90 : char *order_id;
91 :
92 : /**
93 : * The coins we are paying with (frontend mode, already signed).
94 : */
95 : struct TALER_MERCHANT_PostOrdersPayPaidCoin *paid_coins;
96 :
97 : /**
98 : * Number of @e paid_coins.
99 : */
100 : unsigned int num_paid_coins;
101 :
102 : /**
103 : * Hash of the contract terms (wallet mode).
104 : */
105 : struct TALER_PrivateContractHashP h_contract_terms;
106 :
107 : /**
108 : * Public key of the merchant (wallet mode).
109 : */
110 : struct TALER_MerchantPublicKeyP merchant_pub;
111 :
112 : /**
113 : * Merchant signature (wallet mode).
114 : */
115 : struct TALER_MerchantSignatureP merchant_sig;
116 :
117 : /**
118 : * Total payment amount (wallet mode).
119 : */
120 : struct TALER_Amount amount;
121 :
122 : /**
123 : * Maximum fee (wallet mode).
124 : */
125 : struct TALER_Amount max_fee;
126 :
127 : /**
128 : * Contract timestamp (wallet mode).
129 : */
130 : struct GNUNET_TIME_Timestamp timestamp;
131 :
132 : /**
133 : * Refund deadline (wallet mode).
134 : */
135 : struct GNUNET_TIME_Timestamp refund_deadline;
136 :
137 : /**
138 : * Payment deadline (wallet mode).
139 : */
140 : struct GNUNET_TIME_Timestamp pay_deadline;
141 :
142 : /**
143 : * Hash of merchant wire details (wallet mode).
144 : */
145 : struct TALER_MerchantWireHashP h_wire;
146 :
147 : /**
148 : * Choice index (wallet mode).
149 : */
150 : int choice_index;
151 :
152 : /**
153 : * Coins with private keys (wallet mode).
154 : */
155 : struct TALER_MERCHANT_PostOrdersPayCoin *coins;
156 :
157 : /**
158 : * Number of @e coins (wallet mode).
159 : */
160 : unsigned int num_coins;
161 :
162 : /**
163 : * Optional session identifier.
164 : */
165 : char *session_id;
166 :
167 : /**
168 : * Optional wallet data (JSON).
169 : */
170 : json_t *wallet_data;
171 :
172 : /**
173 : * Used tokens (public form, frontend mode).
174 : */
175 : struct TALER_MERCHANT_PostOrdersPayUsedToken *used_tokens;
176 :
177 : /**
178 : * Number of @e used_tokens.
179 : */
180 : unsigned int num_used_tokens;
181 :
182 : /**
183 : * Use tokens (private form, wallet mode).
184 : */
185 : struct TALER_MERCHANT_PostOrdersPayUseToken *use_tokens;
186 :
187 : /**
188 : * Number of @e use_tokens.
189 : */
190 : unsigned int num_use_tokens;
191 :
192 : /**
193 : * Output tokens (wallet mode).
194 : */
195 : struct TALER_MERCHANT_PostOrdersPayOutputToken *output_tokens;
196 :
197 : /**
198 : * Number of @e output_tokens.
199 : */
200 : unsigned int num_output_tokens;
201 :
202 : /**
203 : * Output tokens as JSON array (frontend mode).
204 : */
205 : json_t *output_tokens_json;
206 :
207 : /**
208 : * Base URL of the selected donau for donation receipts.
209 : */
210 : char *donau_url;
211 :
212 : /**
213 : * Tax year used for the donau.
214 : */
215 : unsigned int donau_year;
216 :
217 : /**
218 : * Array of blinded donation receipts.
219 : */
220 : struct DONAU_BlindedUniqueDonorIdentifierKeyPair *donau_bkps;
221 :
222 : /**
223 : * Length of the @e donau_bkps array.
224 : */
225 : size_t num_donau_bkps;
226 :
227 : /**
228 : * Set to true if this is the wallet mode (private keys available).
229 : */
230 : bool am_wallet;
231 : };
232 :
233 :
234 : /**
235 : * Parse blindly signed output tokens from JSON response.
236 : *
237 : * @param token_sigs the JSON array with the token signatures, can be NULL
238 : * @param[out] tokens where to store the parsed tokens
239 : * @param[out] num_tokens where to store the length of the @a tokens array
240 : * @return #GNUNET_YES on success
241 : */
242 : static enum GNUNET_GenericReturnValue
243 32 : parse_tokens (const json_t *token_sigs,
244 : struct TALER_MERCHANT_PostOrdersPayOutputToken **tokens,
245 : unsigned int *num_tokens)
246 : {
247 32 : GNUNET_array_grow (*tokens,
248 : *num_tokens,
249 : json_array_size (token_sigs));
250 :
251 37 : for (unsigned int i = 0; i < (*num_tokens); i++)
252 : {
253 5 : struct TALER_MERCHANT_PostOrdersPayOutputToken *token = &(*tokens)[i];
254 : struct GNUNET_JSON_Specification spec[] = {
255 5 : TALER_JSON_spec_blinded_token_issue_sig ("blind_sig",
256 : &token->blinded_sig),
257 5 : GNUNET_JSON_spec_end ()
258 : };
259 : const json_t *jtoken
260 5 : = json_array_get (token_sigs,
261 : i);
262 :
263 5 : if (NULL == jtoken)
264 : {
265 0 : GNUNET_break (0);
266 0 : return GNUNET_SYSERR;
267 : }
268 5 : if (GNUNET_OK !=
269 5 : GNUNET_JSON_parse (jtoken,
270 : spec,
271 : NULL, NULL))
272 : {
273 0 : GNUNET_break (0);
274 0 : return GNUNET_SYSERR;
275 : }
276 : }
277 :
278 32 : return GNUNET_YES;
279 : }
280 :
281 :
282 : /**
283 : * Function called when we're done processing the
284 : * HTTP POST /orders/$ORDER_ID/pay request.
285 : *
286 : * @param cls the `struct TALER_MERCHANT_PostOrdersPayHandle`
287 : * @param response_code HTTP response code, 0 on error
288 : * @param response response body, NULL if not in JSON
289 : */
290 : static void
291 46 : handle_pay_finished (void *cls,
292 : long response_code,
293 : const void *response)
294 : {
295 46 : struct TALER_MERCHANT_PostOrdersPayHandle *poph = cls;
296 46 : const json_t *json = response;
297 46 : struct TALER_MERCHANT_PostOrdersPayResponse pr = {
298 46 : .hr.http_status = (unsigned int) response_code,
299 : .hr.reply = json
300 : };
301 :
302 46 : poph->job = NULL;
303 46 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
304 : "POST /orders/$ID/pay completed with response code %u\n",
305 : (unsigned int) response_code);
306 46 : switch (response_code)
307 : {
308 0 : case 0:
309 0 : pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
310 0 : break;
311 32 : case MHD_HTTP_OK:
312 32 : if (poph->am_wallet)
313 : {
314 32 : const json_t *token_sigs = NULL;
315 : struct GNUNET_JSON_Specification spec[] = {
316 32 : GNUNET_JSON_spec_fixed_auto ("sig",
317 : &pr.details.ok.merchant_sig),
318 32 : GNUNET_JSON_spec_mark_optional (
319 : GNUNET_JSON_spec_string ("pos_confirmation",
320 : &pr.details.ok.pos_confirmation),
321 : NULL),
322 32 : GNUNET_JSON_spec_mark_optional (
323 : GNUNET_JSON_spec_array_const ("token_sigs",
324 : &token_sigs),
325 : NULL),
326 32 : GNUNET_JSON_spec_end ()
327 : };
328 :
329 32 : if (GNUNET_OK !=
330 32 : GNUNET_JSON_parse (json,
331 : spec,
332 : NULL, NULL))
333 : {
334 0 : GNUNET_break_op (0);
335 0 : pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
336 0 : pr.hr.http_status = 0;
337 0 : pr.hr.hint = "sig field missing in response";
338 0 : break;
339 : }
340 :
341 32 : if (GNUNET_OK !=
342 32 : parse_tokens (token_sigs,
343 : &pr.details.ok.tokens,
344 : &pr.details.ok.num_tokens))
345 : {
346 0 : GNUNET_break_op (0);
347 0 : pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
348 0 : pr.hr.http_status = 0;
349 0 : pr.hr.hint = "failed to parse token_sigs field in response";
350 0 : break;
351 : }
352 :
353 32 : if (GNUNET_OK !=
354 32 : TALER_merchant_pay_verify (&poph->h_contract_terms,
355 32 : &poph->merchant_pub,
356 : &pr.details.ok.merchant_sig))
357 : {
358 0 : GNUNET_break_op (0);
359 0 : pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
360 0 : pr.hr.http_status = 0;
361 0 : pr.hr.hint = "signature invalid";
362 : }
363 : }
364 32 : break;
365 2 : case MHD_HTTP_BAD_REQUEST:
366 2 : pr.hr.ec = TALER_JSON_get_error_code (json);
367 2 : pr.hr.hint = TALER_JSON_get_error_hint (json);
368 2 : break;
369 0 : case MHD_HTTP_PAYMENT_REQUIRED:
370 0 : pr.hr.ec = TALER_JSON_get_error_code (json);
371 0 : pr.hr.hint = TALER_JSON_get_error_hint (json);
372 0 : break;
373 0 : case MHD_HTTP_FORBIDDEN:
374 0 : pr.hr.ec = TALER_JSON_get_error_code (json);
375 0 : pr.hr.hint = TALER_JSON_get_error_hint (json);
376 0 : break;
377 0 : case MHD_HTTP_NOT_FOUND:
378 0 : pr.hr.ec = TALER_JSON_get_error_code (json);
379 0 : pr.hr.hint = TALER_JSON_get_error_hint (json);
380 0 : break;
381 0 : case MHD_HTTP_REQUEST_TIMEOUT:
382 0 : pr.hr.ec = TALER_JSON_get_error_code (json);
383 0 : pr.hr.hint = TALER_JSON_get_error_hint (json);
384 0 : break;
385 12 : case MHD_HTTP_CONFLICT:
386 12 : TALER_MERCHANT_parse_error_details_ (json,
387 : MHD_HTTP_CONFLICT,
388 : &pr.hr);
389 : {
390 12 : const char *eu = json_string_value (
391 12 : json_object_get (json, "exchange_url"));
392 :
393 12 : pr.details.conflict.exchange_url = eu;
394 12 : pr.details.conflict.exchange_ec = pr.hr.exchange_code;
395 : pr.details.conflict.exchange_http_status
396 12 : = pr.hr.exchange_http_status;
397 : }
398 12 : break;
399 0 : case MHD_HTTP_GONE:
400 0 : TALER_MERCHANT_parse_error_details_ (json,
401 : response_code,
402 : &pr.hr);
403 0 : break;
404 0 : case MHD_HTTP_PRECONDITION_FAILED:
405 0 : TALER_MERCHANT_parse_error_details_ (json,
406 : response_code,
407 : &pr.hr);
408 0 : break;
409 0 : case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
410 : {
411 0 : json_t *ebus = json_object_get (json,
412 : "exchange_base_urls");
413 0 : if (NULL == ebus)
414 : {
415 0 : GNUNET_break_op (0);
416 0 : pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
417 0 : pr.hr.http_status = 0;
418 : pr.hr.hint
419 0 : = "failed to parse exchange_base_urls field in response";
420 0 : break;
421 : }
422 : {
423 0 : size_t alen = json_array_size (ebus);
424 :
425 0 : if (alen > MAX_EXCHANGE_BASE_URLS)
426 : {
427 : /* Bound the stack VLA below by an untrusted response. */
428 0 : GNUNET_break_op (0);
429 0 : pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
430 0 : pr.hr.http_status = 0;
431 0 : pr.hr.hint = "too many exchange_base_urls in response";
432 0 : break;
433 : }
434 0 : {
435 0 : const char *ebua[GNUNET_NZL (alen)];
436 : size_t idx;
437 : json_t *jebu;
438 0 : bool ok = true;
439 :
440 0 : json_array_foreach (ebus, idx, jebu)
441 : {
442 0 : ebua[idx] = json_string_value (jebu);
443 0 : if (NULL == ebua[idx])
444 : {
445 0 : GNUNET_break_op (0);
446 0 : pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
447 0 : pr.hr.http_status = 0;
448 : pr.hr.hint
449 0 : = "non-string value in exchange_base_urls in response";
450 0 : ok = false;
451 0 : break;
452 : }
453 : }
454 0 : if (! ok)
455 0 : break;
456 : pr.details.unavailable_for_legal_reasons.num_exchanges
457 0 : = (unsigned int) alen;
458 : pr.details.unavailable_for_legal_reasons.exchanges
459 0 : = ebua;
460 0 : poph->cb (poph->cb_cls,
461 : &pr);
462 0 : TALER_MERCHANT_post_orders_pay_cancel (poph);
463 0 : return;
464 : }
465 : }
466 : }
467 : break;
468 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
469 0 : TALER_MERCHANT_parse_error_details_ (json,
470 : response_code,
471 : &pr.hr);
472 0 : break;
473 0 : case MHD_HTTP_NOT_IMPLEMENTED:
474 0 : TALER_MERCHANT_parse_error_details_ (json,
475 : response_code,
476 : &pr.hr);
477 0 : break;
478 0 : case MHD_HTTP_BAD_GATEWAY:
479 0 : TALER_MERCHANT_parse_error_details_ (json,
480 : response_code,
481 : &pr.hr);
482 : {
483 0 : const char *eu = json_string_value (
484 0 : json_object_get (json, "exchange_url"));
485 :
486 0 : pr.details.bad_gateway.exchange_url = eu;
487 0 : pr.details.bad_gateway.exchange_ec = pr.hr.exchange_code;
488 : pr.details.bad_gateway.exchange_http_status
489 0 : = pr.hr.exchange_http_status;
490 : }
491 0 : break;
492 0 : case MHD_HTTP_GATEWAY_TIMEOUT:
493 0 : TALER_MERCHANT_parse_error_details_ (json,
494 : response_code,
495 : &pr.hr);
496 0 : break;
497 0 : default:
498 0 : TALER_MERCHANT_parse_error_details_ (json,
499 : response_code,
500 : &pr.hr);
501 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
502 : "Unexpected response code %u/%d\n",
503 : (unsigned int) response_code,
504 : (int) pr.hr.ec);
505 0 : GNUNET_break_op (0);
506 0 : break;
507 : }
508 46 : poph->cb (poph->cb_cls,
509 : &pr);
510 46 : if (poph->am_wallet &&
511 : (MHD_HTTP_OK == response_code) )
512 : {
513 37 : for (unsigned int i = 0; i < pr.details.ok.num_tokens; i++)
514 : {
515 5 : struct GNUNET_CRYPTO_BlindedSignature *bs
516 5 : = pr.details.ok.tokens[i].blinded_sig.signature;
517 :
518 5 : if (NULL != bs)
519 5 : GNUNET_CRYPTO_blinded_sig_decref (bs);
520 : }
521 32 : GNUNET_array_grow (pr.details.ok.tokens,
522 : pr.details.ok.num_tokens,
523 : 0);
524 : }
525 46 : TALER_MERCHANT_post_orders_pay_cancel (poph);
526 : }
527 :
528 :
529 : struct TALER_MERCHANT_PostOrdersPayHandle *
530 0 : TALER_MERCHANT_post_orders_pay_frontend_create (
531 : struct GNUNET_CURL_Context *ctx,
532 : const char *url,
533 : const char *order_id,
534 : unsigned int num_coins,
535 : const struct TALER_MERCHANT_PostOrdersPayPaidCoin coins[static num_coins])
536 0 : {
537 : struct TALER_MERCHANT_PostOrdersPayHandle *poph;
538 :
539 0 : poph = GNUNET_new (struct TALER_MERCHANT_PostOrdersPayHandle);
540 0 : poph->ctx = ctx;
541 0 : poph->base_url = GNUNET_strdup (url);
542 0 : poph->order_id = GNUNET_strdup (order_id);
543 0 : poph->am_wallet = false;
544 0 : poph->num_paid_coins = num_coins;
545 0 : poph->paid_coins = GNUNET_new_array (
546 : num_coins,
547 : struct TALER_MERCHANT_PostOrdersPayPaidCoin);
548 0 : for (unsigned int i = 0; i < num_coins; i++)
549 : {
550 0 : struct TALER_MERCHANT_PostOrdersPayPaidCoin *dst = &poph->paid_coins[i];
551 0 : const struct TALER_MERCHANT_PostOrdersPayPaidCoin *src = &coins[i];
552 :
553 0 : *dst = *src;
554 : /* deep copy fields that need it */
555 0 : TALER_denom_pub_copy (&dst->denom_pub,
556 : &src->denom_pub);
557 0 : TALER_denom_sig_copy (&dst->denom_sig,
558 : &src->denom_sig);
559 0 : dst->exchange_url = GNUNET_strdup (src->exchange_url);
560 : }
561 0 : return poph;
562 : }
563 :
564 :
565 : struct TALER_MERCHANT_PostOrdersPayHandle *
566 46 : TALER_MERCHANT_post_orders_pay_create (
567 : struct GNUNET_CURL_Context *ctx,
568 : const char *url,
569 : const char *order_id,
570 : const struct TALER_PrivateContractHashP *h_contract_terms,
571 : const struct TALER_Amount *amount,
572 : const struct TALER_Amount *max_fee,
573 : const struct TALER_MerchantPublicKeyP *merchant_pub,
574 : const struct TALER_MerchantSignatureP *merchant_sig,
575 : struct GNUNET_TIME_Timestamp timestamp,
576 : struct GNUNET_TIME_Timestamp refund_deadline,
577 : struct GNUNET_TIME_Timestamp pay_deadline,
578 : const struct TALER_MerchantWireHashP *h_wire,
579 : unsigned int num_coins,
580 : const struct TALER_MERCHANT_PostOrdersPayCoin coins[static num_coins])
581 46 : {
582 : struct TALER_MERCHANT_PostOrdersPayHandle *poph;
583 :
584 46 : if (GNUNET_YES !=
585 46 : TALER_amount_cmp_currency (amount,
586 : max_fee))
587 : {
588 0 : GNUNET_break (0);
589 0 : return NULL;
590 : }
591 :
592 46 : poph = GNUNET_new (struct TALER_MERCHANT_PostOrdersPayHandle);
593 46 : poph->ctx = ctx;
594 46 : poph->base_url = GNUNET_strdup (url);
595 46 : poph->order_id = GNUNET_strdup (order_id);
596 46 : poph->am_wallet = true;
597 46 : poph->h_contract_terms = *h_contract_terms;
598 46 : poph->choice_index = -1;
599 46 : poph->amount = *amount;
600 46 : poph->max_fee = *max_fee;
601 46 : poph->merchant_pub = *merchant_pub;
602 46 : poph->merchant_sig = *merchant_sig;
603 46 : poph->timestamp = timestamp;
604 46 : poph->refund_deadline = refund_deadline;
605 46 : poph->pay_deadline = pay_deadline;
606 46 : poph->h_wire = *h_wire;
607 46 : poph->num_coins = num_coins;
608 46 : poph->coins = GNUNET_new_array (num_coins,
609 : struct TALER_MERCHANT_PostOrdersPayCoin);
610 94 : for (unsigned int i = 0; i < num_coins; i++)
611 : {
612 48 : struct TALER_MERCHANT_PostOrdersPayCoin *dst = &poph->coins[i];
613 48 : const struct TALER_MERCHANT_PostOrdersPayCoin *src = &coins[i];
614 :
615 48 : *dst = *src;
616 48 : TALER_denom_pub_copy (&dst->denom_pub,
617 : &src->denom_pub);
618 48 : TALER_denom_sig_copy (&dst->denom_sig,
619 : &src->denom_sig);
620 48 : dst->exchange_url = GNUNET_strdup (src->exchange_url);
621 : }
622 46 : return poph;
623 : }
624 :
625 :
626 : enum GNUNET_GenericReturnValue
627 35 : TALER_MERCHANT_post_orders_pay_set_options_ (
628 : struct TALER_MERCHANT_PostOrdersPayHandle *poph,
629 : unsigned int num_options,
630 : const struct TALER_MERCHANT_PostOrdersPayOptionValue *options)
631 : {
632 70 : for (unsigned int i = 0; i < num_options; i++)
633 : {
634 70 : switch (options[i].option)
635 : {
636 35 : case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_END:
637 35 : return GNUNET_OK;
638 11 : case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_SESSION_ID:
639 11 : GNUNET_free (poph->session_id);
640 11 : if (NULL != options[i].details.session_id)
641 11 : poph->session_id = GNUNET_strdup (options[i].details.session_id);
642 11 : break;
643 0 : case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_WALLET_DATA:
644 0 : json_decref (poph->wallet_data);
645 0 : poph->wallet_data = NULL;
646 0 : if (NULL != options[i].details.wallet_data)
647 0 : poph->wallet_data = json_incref (
648 0 : (json_t *) options[i].details.wallet_data);
649 0 : break;
650 0 : case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_USED_TOKENS:
651 0 : GNUNET_free (poph->used_tokens);
652 0 : poph->num_used_tokens = options[i].details.used_tokens.num;
653 0 : if (0 < poph->num_used_tokens)
654 : {
655 0 : poph->used_tokens = GNUNET_new_array (
656 : poph->num_used_tokens,
657 : struct TALER_MERCHANT_PostOrdersPayUsedToken);
658 0 : GNUNET_memcpy (poph->used_tokens,
659 : options[i].details.used_tokens.tokens,
660 : poph->num_used_tokens
661 : * sizeof (struct TALER_MERCHANT_PostOrdersPayUsedToken));
662 : }
663 0 : break;
664 4 : case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_USE_TOKENS:
665 4 : GNUNET_free (poph->use_tokens);
666 4 : poph->num_use_tokens = options[i].details.use_tokens.num;
667 4 : if (0 < poph->num_use_tokens)
668 : {
669 4 : poph->use_tokens = GNUNET_new_array (
670 : poph->num_use_tokens,
671 : struct TALER_MERCHANT_PostOrdersPayUseToken);
672 4 : GNUNET_memcpy (poph->use_tokens,
673 : options[i].details.use_tokens.tokens,
674 : poph->num_use_tokens
675 : * sizeof (struct TALER_MERCHANT_PostOrdersPayUseToken));
676 : }
677 4 : break;
678 6 : case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_OUTPUT_TOKENS:
679 6 : GNUNET_free (poph->output_tokens);
680 6 : poph->num_output_tokens = options[i].details.output_tokens.num;
681 6 : if (0 < poph->num_output_tokens)
682 : {
683 6 : poph->output_tokens = GNUNET_new_array (
684 : poph->num_output_tokens,
685 : struct TALER_MERCHANT_PostOrdersPayOutputToken);
686 6 : GNUNET_memcpy (
687 : poph->output_tokens,
688 : options[i].details.output_tokens.tokens,
689 : poph->num_output_tokens
690 : * sizeof (struct TALER_MERCHANT_PostOrdersPayOutputToken));
691 : }
692 6 : break;
693 0 : case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_OUTPUT_TOKENS_JSON:
694 0 : json_decref (poph->output_tokens_json);
695 0 : poph->output_tokens_json = NULL;
696 0 : if (NULL != options[i].details.output_tokens_json)
697 0 : poph->output_tokens_json = json_incref (
698 0 : options[i].details.output_tokens_json);
699 0 : break;
700 1 : case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_OUTPUT_DONAU:
701 1 : if (NULL != poph->donau_url)
702 : {
703 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
704 : "Only one set of donation receipts is allowed to be specified\n");
705 0 : return GNUNET_NO;
706 : }
707 : poph->donau_url
708 1 : = GNUNET_strdup (options[i].details.output_donau.donau_base_url);
709 : poph->donau_year
710 1 : = options[i].details.output_donau.year;
711 1 : poph->num_donau_bkps = options[i].details.output_donau.num_bkps;
712 1 : poph->donau_bkps = GNUNET_new_array (
713 : poph->num_donau_bkps,
714 : struct DONAU_BlindedUniqueDonorIdentifierKeyPair);
715 2 : for (size_t j=0; j<poph->num_donau_bkps; j++)
716 : {
717 1 : const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *src
718 1 : = &options[i].details.output_donau.bkps[j];
719 1 : struct DONAU_BlindedUniqueDonorIdentifierKeyPair *dst
720 1 : = &poph->donau_bkps[j];
721 :
722 1 : dst->h_donation_unit_pub = src->h_donation_unit_pub;
723 : dst->blinded_udi.blinded_message
724 1 : = GNUNET_CRYPTO_blinded_message_incref (
725 1 : src->blinded_udi.blinded_message);
726 : }
727 1 : break;
728 13 : case TALER_MERCHANT_POST_ORDERS_PAY_OPTION_CHOICE_INDEX:
729 13 : poph->choice_index = options[i].details.choice_index;
730 13 : break;
731 0 : default:
732 0 : GNUNET_break (0);
733 0 : return GNUNET_SYSERR;
734 : }
735 : }
736 0 : return GNUNET_OK;
737 : }
738 :
739 :
740 : enum TALER_ErrorCode
741 46 : TALER_MERCHANT_post_orders_pay_start (
742 : struct TALER_MERCHANT_PostOrdersPayHandle *poph,
743 : TALER_MERCHANT_PostOrdersPayCallback cb,
744 : TALER_MERCHANT_POST_ORDERS_PAY_RESULT_CLOSURE *cb_cls)
745 : {
746 : json_t *pay_obj;
747 : json_t *j_coins;
748 46 : json_t *j_tokens = NULL;
749 46 : json_t *j_output_tokens = NULL;
750 : CURL *eh;
751 :
752 46 : poph->cb = cb;
753 46 : poph->cb_cls = cb_cls;
754 : {
755 : char *path;
756 :
757 46 : GNUNET_asprintf (&path,
758 : "orders/%s/pay",
759 : poph->order_id);
760 46 : poph->url = TALER_url_join (poph->base_url,
761 : path,
762 : NULL);
763 46 : GNUNET_free (path);
764 : }
765 46 : if (NULL == poph->url)
766 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
767 :
768 46 : if (poph->am_wallet)
769 : {
770 : /* Wallet mode: sign coins and tokens, build wallet_data */
771 46 : json_t *wallet_data = poph->wallet_data;
772 46 : json_t *j_donau_data = NULL;
773 : struct GNUNET_HashCode wallet_data_hash;
774 :
775 46 : if (NULL != poph->donau_url)
776 : {
777 : json_t *budis;
778 :
779 1 : budis = json_array ();
780 1 : GNUNET_assert (NULL != budis);
781 2 : for (size_t i=0; i<poph->num_donau_bkps; i++)
782 : {
783 1 : const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkp
784 1 : = &poph->donau_bkps[i];
785 1 : json_t *budikeypair = GNUNET_JSON_PACK (
786 : GNUNET_JSON_pack_data_auto ("h_donation_unit_pub",
787 : &bkp->h_donation_unit_pub),
788 : DONAU_JSON_pack_blinded_donation_identifier ("blinded_udi",
789 : &bkp->blinded_udi));
790 :
791 1 : GNUNET_assert (0 ==
792 : json_array_append_new (budis,
793 : budikeypair));
794 : }
795 :
796 1 : j_donau_data = GNUNET_JSON_PACK (
797 : GNUNET_JSON_pack_string ("url",
798 : poph->donau_url),
799 : GNUNET_JSON_pack_int64 ("year",
800 : poph->donau_year),
801 : GNUNET_JSON_pack_array_steal ("budikeypairs",
802 : budis));
803 : }
804 :
805 : /* Build output token envelopes JSON if we have output tokens */
806 46 : if (0 < poph->num_output_tokens)
807 : {
808 6 : j_output_tokens = json_array ();
809 6 : GNUNET_assert (NULL != j_output_tokens);
810 12 : for (unsigned int i = 0; i < poph->num_output_tokens; i++)
811 : {
812 : json_t *j_token_ev;
813 6 : const struct TALER_MERCHANT_PostOrdersPayOutputToken *ev
814 6 : = &poph->output_tokens[i];
815 :
816 6 : j_token_ev = GNUNET_JSON_PACK (
817 : TALER_JSON_pack_token_envelope (NULL,
818 : &ev->envelope));
819 6 : if (0 !=
820 6 : json_array_append_new (j_output_tokens,
821 : j_token_ev))
822 : {
823 0 : GNUNET_break (0);
824 0 : json_decref (j_output_tokens);
825 0 : json_decref (j_donau_data);
826 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
827 : }
828 : }
829 : }
830 40 : else if (NULL != poph->output_tokens_json)
831 : {
832 0 : j_output_tokens = json_incref (poph->output_tokens_json);
833 : }
834 :
835 : /* Build wallet_data if choice_index is valid */
836 46 : if (0 <= poph->choice_index)
837 : {
838 13 : if (NULL == wallet_data)
839 : {
840 13 : wallet_data = GNUNET_JSON_PACK (
841 : GNUNET_JSON_pack_int64 ("choice_index",
842 : poph->choice_index),
843 : GNUNET_JSON_pack_allow_null (
844 : GNUNET_JSON_pack_object_incref ("donau",
845 : j_donau_data)),
846 : GNUNET_JSON_pack_allow_null (
847 : GNUNET_JSON_pack_array_incref ("tokens_evs",
848 : j_output_tokens)));
849 : }
850 13 : TALER_json_hash (wallet_data,
851 : &wallet_data_hash);
852 : }
853 46 : json_decref (j_donau_data);
854 46 : j_donau_data = NULL;
855 :
856 46 : if ( (0 < poph->num_use_tokens || 0 < poph->num_output_tokens
857 40 : || NULL != poph->output_tokens_json)
858 6 : && (0 > poph->choice_index) )
859 : {
860 0 : GNUNET_break (0);
861 0 : json_decref (j_output_tokens);
862 0 : if ( (NULL == poph->wallet_data) &&
863 : (NULL != wallet_data) )
864 0 : json_decref (wallet_data);
865 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
866 : }
867 :
868 : /* Sign coins */
869 46 : j_coins = json_array ();
870 46 : GNUNET_assert (NULL != j_coins);
871 94 : for (unsigned int i = 0; i < poph->num_coins; i++)
872 : {
873 48 : const struct TALER_MERCHANT_PostOrdersPayCoin *coin = &poph->coins[i];
874 : struct TALER_CoinSpendPublicKeyP coin_pub;
875 : struct TALER_CoinSpendSignatureP coin_sig;
876 : struct TALER_Amount fee;
877 : struct TALER_DenominationHashP h_denom_pub;
878 : json_t *j_coin;
879 :
880 48 : if (0 >
881 48 : TALER_amount_subtract (&fee,
882 : &coin->amount_with_fee,
883 : &coin->amount_without_fee))
884 : {
885 0 : GNUNET_break (0);
886 0 : json_decref (j_coins);
887 0 : json_decref (j_output_tokens);
888 0 : if ( (NULL == poph->wallet_data) &&
889 : (NULL != wallet_data) )
890 0 : json_decref (wallet_data);
891 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
892 : }
893 48 : TALER_denom_pub_hash (&coin->denom_pub,
894 : &h_denom_pub);
895 96 : TALER_wallet_deposit_sign (&coin->amount_with_fee,
896 : &fee,
897 48 : &poph->h_wire,
898 48 : &poph->h_contract_terms,
899 48 : (0 <= poph->choice_index)
900 : ? &wallet_data_hash
901 : : NULL,
902 48 : GNUNET_is_zero (&coin->h_age_commitment)
903 : ? NULL
904 : : &coin->h_age_commitment,
905 : NULL /* h_extensions */,
906 : &h_denom_pub,
907 : poph->timestamp,
908 48 : &poph->merchant_pub,
909 : poph->refund_deadline,
910 : &coin->coin_priv,
911 : &coin_sig);
912 48 : GNUNET_CRYPTO_eddsa_key_get_public (&coin->coin_priv.eddsa_priv,
913 : &coin_pub.eddsa_pub);
914 48 : j_coin = GNUNET_JSON_PACK (
915 : TALER_JSON_pack_amount ("contribution",
916 : &coin->amount_with_fee),
917 : GNUNET_JSON_pack_data_auto ("coin_pub",
918 : &coin_pub),
919 : GNUNET_JSON_pack_string ("exchange_url",
920 : coin->exchange_url),
921 : GNUNET_JSON_pack_data_auto ("h_denom",
922 : &h_denom_pub),
923 : TALER_JSON_pack_denom_sig ("ub_sig",
924 : &coin->denom_sig),
925 : GNUNET_JSON_pack_data_auto ("coin_sig",
926 : &coin_sig));
927 48 : if (0 !=
928 48 : json_array_append_new (j_coins,
929 : j_coin))
930 : {
931 0 : GNUNET_break (0);
932 0 : json_decref (j_coins);
933 0 : json_decref (j_output_tokens);
934 0 : if ( (NULL == poph->wallet_data) &&
935 : (NULL != wallet_data) )
936 0 : json_decref (wallet_data);
937 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
938 : }
939 : }
940 :
941 : /* Sign use tokens */
942 46 : if (0 < poph->num_use_tokens)
943 : {
944 4 : j_tokens = json_array ();
945 4 : GNUNET_assert (NULL != j_tokens);
946 8 : for (unsigned int i = 0; i < poph->num_use_tokens; i++)
947 : {
948 4 : const struct TALER_MERCHANT_PostOrdersPayUseToken *token
949 4 : = &poph->use_tokens[i];
950 : struct TALER_TokenUseSignatureP token_sig;
951 : struct TALER_TokenUsePublicKeyP token_pub;
952 : json_t *j_token;
953 :
954 4 : TALER_wallet_token_use_sign (&poph->h_contract_terms,
955 : &wallet_data_hash,
956 : &token->token_priv,
957 : &token_sig);
958 4 : GNUNET_CRYPTO_eddsa_key_get_public (
959 : &token->token_priv.private_key,
960 : &token_pub.public_key);
961 4 : j_token = GNUNET_JSON_PACK (
962 : GNUNET_JSON_pack_data_auto ("token_sig",
963 : &token_sig),
964 : GNUNET_JSON_pack_data_auto ("token_pub",
965 : &token_pub),
966 : GNUNET_JSON_pack_data_auto (
967 : "h_issue",
968 : &token->issue_pub.public_key->pub_key_hash),
969 : TALER_JSON_pack_token_issue_sig ("ub_sig",
970 : &token->ub_sig));
971 4 : if (0 !=
972 4 : json_array_append_new (j_tokens,
973 : j_token))
974 : {
975 0 : GNUNET_break (0);
976 0 : json_decref (j_coins);
977 0 : json_decref (j_tokens);
978 0 : json_decref (j_output_tokens);
979 0 : if ( (NULL == poph->wallet_data) &&
980 : (NULL != wallet_data) )
981 0 : json_decref (wallet_data);
982 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
983 : }
984 : }
985 : }
986 :
987 46 : pay_obj = GNUNET_JSON_PACK (
988 : GNUNET_JSON_pack_array_steal ("coins",
989 : j_coins),
990 : GNUNET_JSON_pack_allow_null (
991 : GNUNET_JSON_pack_array_steal ("tokens",
992 : j_tokens)),
993 : GNUNET_JSON_pack_allow_null (
994 : GNUNET_JSON_pack_object_incref ("wallet_data",
995 : wallet_data)),
996 : GNUNET_JSON_pack_allow_null (
997 : GNUNET_JSON_pack_string ("session_id",
998 : poph->session_id)));
999 46 : if ( (NULL == poph->wallet_data) &&
1000 : (NULL != wallet_data) )
1001 13 : json_decref (wallet_data);
1002 46 : json_decref (j_output_tokens);
1003 : }
1004 : else
1005 : {
1006 : /* Frontend mode: coins are already signed */
1007 0 : j_coins = json_array ();
1008 0 : GNUNET_assert (NULL != j_coins);
1009 0 : for (unsigned int i = 0; i < poph->num_paid_coins; i++)
1010 : {
1011 0 : const struct TALER_MERCHANT_PostOrdersPayPaidCoin *pc
1012 0 : = &poph->paid_coins[i];
1013 : struct TALER_DenominationHashP denom_hash;
1014 : json_t *j_coin;
1015 :
1016 0 : TALER_denom_pub_hash (&pc->denom_pub,
1017 : &denom_hash);
1018 0 : j_coin = GNUNET_JSON_PACK (
1019 : TALER_JSON_pack_amount ("contribution",
1020 : &pc->amount_with_fee),
1021 : GNUNET_JSON_pack_data_auto ("coin_pub",
1022 : &pc->coin_pub),
1023 : GNUNET_JSON_pack_string ("exchange_url",
1024 : pc->exchange_url),
1025 : GNUNET_JSON_pack_data_auto ("h_denom",
1026 : &denom_hash),
1027 : TALER_JSON_pack_denom_sig ("ub_sig",
1028 : &pc->denom_sig),
1029 : GNUNET_JSON_pack_data_auto ("coin_sig",
1030 : &pc->coin_sig));
1031 0 : if (0 !=
1032 0 : json_array_append_new (j_coins,
1033 : j_coin))
1034 : {
1035 0 : GNUNET_break (0);
1036 0 : json_decref (j_coins);
1037 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
1038 : }
1039 : }
1040 :
1041 : /* Build used tokens JSON (frontend mode) */
1042 0 : if (0 < poph->num_used_tokens)
1043 : {
1044 0 : j_tokens = json_array ();
1045 0 : GNUNET_assert (NULL != j_tokens);
1046 0 : for (unsigned int i = 0; i < poph->num_used_tokens; i++)
1047 : {
1048 0 : const struct TALER_MERCHANT_PostOrdersPayUsedToken *ut
1049 0 : = &poph->used_tokens[i];
1050 : json_t *j_token;
1051 :
1052 0 : j_token = GNUNET_JSON_PACK (
1053 : GNUNET_JSON_pack_data_auto ("token_sig",
1054 : &ut->token_sig),
1055 : GNUNET_JSON_pack_data_auto ("token_pub",
1056 : &ut->token_pub),
1057 : GNUNET_JSON_pack_data_auto (
1058 : "h_issue",
1059 : &ut->issue_pub.public_key->pub_key_hash),
1060 : TALER_JSON_pack_token_issue_sig ("ub_sig",
1061 : &ut->ub_sig));
1062 0 : if (0 !=
1063 0 : json_array_append_new (j_tokens,
1064 : j_token))
1065 : {
1066 0 : GNUNET_break (0);
1067 0 : json_decref (j_coins);
1068 0 : json_decref (j_tokens);
1069 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
1070 : }
1071 : }
1072 : }
1073 :
1074 0 : pay_obj = GNUNET_JSON_PACK (
1075 : GNUNET_JSON_pack_array_steal ("coins",
1076 : j_coins),
1077 : GNUNET_JSON_pack_allow_null (
1078 : GNUNET_JSON_pack_array_steal ("tokens",
1079 : j_tokens)),
1080 : GNUNET_JSON_pack_allow_null (
1081 : GNUNET_JSON_pack_object_incref ("wallet_data",
1082 : poph->wallet_data)),
1083 : GNUNET_JSON_pack_allow_null (
1084 : GNUNET_JSON_pack_string ("session_id",
1085 : poph->session_id)));
1086 : }
1087 :
1088 46 : eh = TALER_MERCHANT_curl_easy_get_ (poph->url);
1089 92 : if ( (NULL == eh) ||
1090 : (GNUNET_OK !=
1091 46 : TALER_curl_easy_post (&poph->post_ctx,
1092 : eh,
1093 : pay_obj)) )
1094 : {
1095 0 : GNUNET_break (0);
1096 0 : json_decref (pay_obj);
1097 0 : if (NULL != eh)
1098 0 : curl_easy_cleanup (eh);
1099 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
1100 : }
1101 46 : json_decref (pay_obj);
1102 92 : poph->job = GNUNET_CURL_job_add2 (poph->ctx,
1103 : eh,
1104 46 : poph->post_ctx.headers,
1105 : &handle_pay_finished,
1106 : poph);
1107 46 : if (NULL == poph->job)
1108 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
1109 46 : return TALER_EC_NONE;
1110 : }
1111 :
1112 :
1113 : void
1114 46 : TALER_MERCHANT_post_orders_pay_cancel (
1115 : struct TALER_MERCHANT_PostOrdersPayHandle *poph)
1116 : {
1117 46 : if (NULL != poph->job)
1118 : {
1119 0 : GNUNET_CURL_job_cancel (poph->job);
1120 0 : poph->job = NULL;
1121 : }
1122 46 : TALER_curl_easy_post_finished (&poph->post_ctx);
1123 46 : if (NULL != poph->paid_coins)
1124 : {
1125 0 : for (unsigned int i = 0; i < poph->num_paid_coins; i++)
1126 : {
1127 0 : TALER_denom_pub_free (&poph->paid_coins[i].denom_pub);
1128 0 : TALER_denom_sig_free (&poph->paid_coins[i].denom_sig);
1129 0 : GNUNET_free (poph->paid_coins[i].exchange_url);
1130 : }
1131 0 : GNUNET_free (poph->paid_coins);
1132 : }
1133 46 : if (NULL != poph->coins)
1134 : {
1135 94 : for (unsigned int i = 0; i < poph->num_coins; i++)
1136 : {
1137 48 : TALER_denom_pub_free (&poph->coins[i].denom_pub);
1138 48 : TALER_denom_sig_free (&poph->coins[i].denom_sig);
1139 48 : GNUNET_free (poph->coins[i].exchange_url);
1140 : }
1141 46 : GNUNET_free (poph->coins);
1142 : }
1143 47 : for (size_t j = 0; j<poph->num_donau_bkps; j++)
1144 : {
1145 1 : struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bpk
1146 1 : = &poph->donau_bkps[j];
1147 :
1148 1 : GNUNET_CRYPTO_blinded_message_decref (bpk->blinded_udi.blinded_message);
1149 : }
1150 46 : GNUNET_free (poph->donau_bkps);
1151 46 : GNUNET_free (poph->donau_url);
1152 46 : GNUNET_free (poph->used_tokens);
1153 46 : GNUNET_free (poph->use_tokens);
1154 46 : GNUNET_free (poph->output_tokens);
1155 46 : json_decref (poph->output_tokens_json);
1156 46 : json_decref (poph->wallet_data);
1157 46 : GNUNET_free (poph->session_id);
1158 46 : GNUNET_free (poph->order_id);
1159 46 : GNUNET_free (poph->url);
1160 46 : GNUNET_free (poph->base_url);
1161 46 : GNUNET_free (poph);
1162 46 : }
1163 :
1164 :
1165 : /* end of merchant_api_post-orders-ORDER_ID-pay-new.c */
|