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