Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2014-2025 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU Affero General Public License as
7 : published by the Free Software Foundation; either version 3,
8 : or (at your option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not,
17 : see <http://www.gnu.org/licenses/>
18 : */
19 :
20 : /**
21 : * @file taler-merchant-httpd_post-orders-ID-pay.c
22 : * @brief handling of POST /orders/$ID/pay requests
23 : * @author Marcello Stanisci
24 : * @author Christian Grothoff
25 : * @author Florian Dold
26 : */
27 : #include "platform.h"
28 : #include <gnunet/gnunet_common.h>
29 : #include <gnunet/gnunet_db_lib.h>
30 : #include <gnunet/gnunet_json_lib.h>
31 : #include <gnunet/gnunet_time_lib.h>
32 : #include <jansson.h>
33 : #include <microhttpd.h>
34 : #include <stddef.h>
35 : #include <stdint.h>
36 : #include <string.h>
37 : #include <taler/taler_dbevents.h>
38 : #include <taler/taler_error_codes.h>
39 : #include <taler/taler_signatures.h>
40 : #include <taler/taler_json_lib.h>
41 : #include <taler/taler_exchange_service.h>
42 : #include "taler-merchant-httpd.h"
43 : #include "taler-merchant-httpd_exchanges.h"
44 : #include "taler-merchant-httpd_helper.h"
45 : #include "taler-merchant-httpd_post-orders-ID-pay.h"
46 : #include "taler-merchant-httpd_private-get-orders.h"
47 : #include "taler_merchant_util.h"
48 : #include "taler_merchantdb_plugin.h"
49 :
50 :
51 : /**
52 : * How often do we retry the (complex!) database transaction?
53 : */
54 : #define MAX_RETRIES 5
55 :
56 : /**
57 : * Maximum number of coins that we allow per transaction.
58 : * Note that the limit for each batch deposit request to
59 : * the exchange is lower, so we may break a very large
60 : * number of coins up into multiple smaller requests to
61 : * the exchange.
62 : */
63 : #define MAX_COIN_ALLOWED_COINS 1024
64 :
65 : /**
66 : * Maximum number of tokens that we allow as inputs per transaction
67 : */
68 : #define MAX_TOKEN_ALLOWED_INPUTs 64
69 :
70 : /**
71 : * Maximum number of tokens that we allow as outputs per transaction
72 : */
73 : #define MAX_TOKEN_ALLOWED_OUTPUTs 64
74 :
75 : /**
76 : * How often do we ask the exchange again about our
77 : * KYC status? Very rarely, as if the user actively
78 : * changes it, we should usually notice anyway.
79 : */
80 : #define KYC_RETRY_FREQUENCY GNUNET_TIME_UNIT_WEEKS
81 :
82 : /**
83 : * Information we keep for an individual call to the pay handler.
84 : */
85 : struct PayContext;
86 :
87 :
88 : /**
89 : * Different phases of processing the /pay request.
90 : */
91 : enum PayPhase
92 : {
93 : /**
94 : * Initial phase where the request is parsed.
95 : */
96 : PP_PARSE_PAY = 0,
97 :
98 : /**
99 : * Parse wallet data object from the pay request.
100 : */
101 : PP_PARSE_WALLET_DATA,
102 :
103 : /**
104 : * Check database state for the given order.
105 : */
106 : PP_CHECK_CONTRACT,
107 :
108 : /**
109 : * Validate provided tokens and token envelopes.
110 : */
111 : PP_VALIDATE_TOKENS,
112 :
113 : /**
114 : * Check if contract has been paid.
115 : */
116 : PP_CONTRACT_PAID,
117 :
118 : /**
119 : * Execute payment transaction.
120 : */
121 : PP_PAY_TRANSACTION,
122 :
123 : // FIXME: new (optional) phase for DONAU interaction here.
124 :
125 : /**
126 : * Notify other processes about successful payment.
127 : */
128 : PP_PAYMENT_NOTIFICATION,
129 :
130 : /**
131 : * Create final success response.
132 : */
133 : PP_SUCCESS_RESPONSE,
134 :
135 : /**
136 : * Perform batch deposits with exchange(s).
137 : */
138 : PP_BATCH_DEPOSITS,
139 :
140 : /**
141 : * Return response in payment context.
142 : */
143 : PP_RETURN_RESPONSE,
144 :
145 : /**
146 : * An exchange denied a deposit, fail for
147 : * legal reasons.
148 : */
149 : PP_FAIL_LEGAL_REASONS,
150 :
151 : /**
152 : * Return #MHD_YES to end processing.
153 : */
154 : PP_END_YES,
155 :
156 : /**
157 : * Return #MHD_NO to end processing.
158 : */
159 : PP_END_NO
160 : };
161 :
162 :
163 : /**
164 : * Information kept during a pay request for each coin.
165 : */
166 : struct DepositConfirmation
167 : {
168 :
169 : /**
170 : * Reference to the main PayContext
171 : */
172 : struct PayContext *pc;
173 :
174 : /**
175 : * URL of the exchange that issued this coin.
176 : */
177 : char *exchange_url;
178 :
179 : /**
180 : * Details about the coin being deposited.
181 : */
182 : struct TALER_EXCHANGE_CoinDepositDetail cdd;
183 :
184 : /**
185 : * Fee charged by the exchange for the deposit operation of this coin.
186 : */
187 : struct TALER_Amount deposit_fee;
188 :
189 : /**
190 : * Fee charged by the exchange for the refund operation of this coin.
191 : */
192 : struct TALER_Amount refund_fee;
193 :
194 : /**
195 : * If a minimum age was required (i. e. pc->minimum_age is large enough),
196 : * this is the signature of the minimum age (as a single uint8_t), using the
197 : * private key to the corresponding age group. Might be all zeroes for no
198 : * age attestation.
199 : */
200 : struct TALER_AgeAttestationP minimum_age_sig;
201 :
202 : /**
203 : * If a minimum age was required (i. e. pc->minimum_age is large enough),
204 : * this is the age commitment (i. e. age mask and vector of EdDSA public
205 : * keys, one per age group) that went into the mining of the coin. The
206 : * SHA256 hash of the mask and the vector of public keys was bound to the
207 : * key.
208 : */
209 : struct TALER_AgeCommitment age_commitment;
210 :
211 : /**
212 : * Age mask in the denomination that defines the age groups. Only
213 : * applicable, if minimum age was required.
214 : */
215 : struct TALER_AgeMask age_mask;
216 :
217 : /**
218 : * Offset of this coin into the `dc` array of all coins in the
219 : * @e pc.
220 : */
221 : unsigned int index;
222 :
223 : /**
224 : * true, if no field "age_commitment" was found in the JSON blob
225 : */
226 : bool no_age_commitment;
227 :
228 : /**
229 : * True, if no field "minimum_age_sig" was found in the JSON blob
230 : */
231 : bool no_minimum_age_sig;
232 :
233 : /**
234 : * true, if no field "h_age_commitment" was found in the JSON blob
235 : */
236 : bool no_h_age_commitment;
237 :
238 : /**
239 : * true if we found this coin in the database.
240 : */
241 : bool found_in_db;
242 :
243 : /**
244 : * true if we #deposit_paid_check() matched this coin in the database.
245 : */
246 : bool matched_in_db;
247 :
248 : };
249 :
250 : struct TokenUseConfirmation
251 : {
252 :
253 : /**
254 : * Signature on the deposit request made using the token use private key.
255 : */
256 : struct TALER_TokenUseSignatureP sig;
257 :
258 : /**
259 : * Token use public key. This key was blindly signed by the merchant during
260 : * the token issuance process.
261 : */
262 : struct TALER_TokenUsePublicKeyP pub;
263 :
264 : /**
265 : * Unblinded signature on the token use public key done by the merchant.
266 : */
267 : struct TALER_TokenIssueSignature unblinded_sig;
268 :
269 : /**
270 : * Hash of the token issue public key associated with this token.
271 : * Note this is set in the validate_tokens phase.
272 : */
273 : struct TALER_TokenIssuePublicKeyHashP h_issue;
274 :
275 : /**
276 : * true if we found this token in the database.
277 : */
278 : bool found_in_db;
279 :
280 : };
281 :
282 :
283 : /**
284 : * Information about a token envelope.
285 : */
286 : struct TokenEnvelope
287 : {
288 :
289 : /**
290 : * Blinded token use public keys waiting to be signed.
291 : */
292 : struct TALER_TokenEnvelope blinded_token;
293 :
294 : };
295 :
296 :
297 : /**
298 : * (Blindly) signed token to be returned to the wallet.
299 : */
300 : struct SignedOutputToken
301 : {
302 :
303 : /**
304 : * Blinded token use public keys waiting to be signed.
305 : */
306 : struct TALER_BlindedTokenIssueSignature sig;
307 :
308 : /**
309 : * Hash of token issue public key.
310 : */
311 : struct TALER_TokenIssuePublicKeyHashP h_issue;
312 :
313 : };
314 :
315 :
316 : /**
317 : * Information kept during a pay request for each exchange.
318 : */
319 : struct ExchangeGroup
320 : {
321 :
322 : /**
323 : * Payment context this group is part of.
324 : */
325 : struct PayContext *pc;
326 :
327 : /**
328 : * Handle to the batch deposit operation we are performing for this
329 : * exchange, NULL after the operation is done.
330 : */
331 : struct TALER_EXCHANGE_BatchDepositHandle *bdh;
332 :
333 : /**
334 : * Handle for operation to lookup /keys (and auditors) from
335 : * the exchange used for this transaction; NULL if no operation is
336 : * pending.
337 : */
338 : struct TMH_EXCHANGES_KeysOperation *fo;
339 :
340 : /**
341 : * URL of the exchange that issued this coin. Aliases
342 : * the exchange URL of one of the coins, do not free!
343 : */
344 : const char *exchange_url;
345 :
346 : /**
347 : * Total deposit amount in this exchange group.
348 : */
349 : struct TALER_Amount total;
350 :
351 : /**
352 : * Wire fee that applies to this exchange for the
353 : * given payment context's wire method.
354 : */
355 : struct TALER_Amount wire_fee;
356 :
357 : /**
358 : * true if we already tried a forced /keys download.
359 : */
360 : bool tried_force_keys;
361 :
362 : /**
363 : * Did this exchange deny the transaction for legal reasons?
364 : */
365 : bool got_451;
366 : };
367 :
368 :
369 : /**
370 : * Information we keep for an individual call to the /pay handler.
371 : */
372 : struct PayContext
373 : {
374 :
375 : /**
376 : * Stored in a DLL.
377 : */
378 : struct PayContext *next;
379 :
380 : /**
381 : * Stored in a DLL.
382 : */
383 : struct PayContext *prev;
384 :
385 : /**
386 : * MHD connection to return to
387 : */
388 : struct MHD_Connection *connection;
389 :
390 : /**
391 : * Details about the client's request.
392 : */
393 : struct TMH_HandlerContext *hc;
394 :
395 : /**
396 : * Transaction ID given in @e root.
397 : */
398 : const char *order_id;
399 :
400 : /**
401 : * Response to return, NULL if we don't have one yet.
402 : */
403 : struct MHD_Response *response;
404 :
405 : /**
406 : * HTTP status code to use for the reply, i.e 200 for "OK".
407 : * Special value UINT_MAX is used to indicate hard errors
408 : * (no reply, return #MHD_NO).
409 : */
410 : unsigned int response_code;
411 :
412 : /**
413 : * Payment processing phase we are in.
414 : */
415 : enum PayPhase phase;
416 :
417 : /**
418 : * #GNUNET_NO if the @e connection was not suspended,
419 : * #GNUNET_YES if the @e connection was suspended,
420 : * #GNUNET_SYSERR if @e connection was resumed to as
421 : * part of #MH_force_pc_resume during shutdown.
422 : */
423 : enum GNUNET_GenericReturnValue suspended;
424 :
425 : /**
426 : * Results from the phase_parse_pay()
427 : */
428 : struct
429 : {
430 :
431 : /**
432 : * Array with @e num_exchanges exchanges we are depositing
433 : * coins into.
434 : */
435 : struct ExchangeGroup **egs;
436 :
437 : /**
438 : * Array with @e coins_cnt coins we are despositing.
439 : */
440 : struct DepositConfirmation *dc;
441 :
442 : /**
443 : * Array with @e tokens_cnt input tokens passed to this request.
444 : */
445 : struct TokenUseConfirmation *tokens;
446 :
447 : /**
448 : * Optional session id given in @e root.
449 : * NULL if not given.
450 : */
451 : char *session_id;
452 :
453 : /**
454 : * Wallet data json object from the request. Containing additional
455 : * wallet data such as the selected choice_index.
456 : */
457 : const json_t *wallet_data;
458 :
459 : /**
460 : * Number of coins this payment is made of. Length
461 : * of the @e dc array.
462 : */
463 : size_t coins_cnt;
464 :
465 : /**
466 : * Number of input tokens passed to this request. Length
467 : * of the @e tokens array.
468 : */
469 : size_t tokens_cnt;
470 :
471 : /**
472 : * Number of exchanges involved in the payment. Length
473 : * of the @e eg array.
474 : */
475 : unsigned int num_exchanges;
476 :
477 : } parse_pay;
478 :
479 : /**
480 : * Results from the phase_wallet_data()
481 : */
482 : struct
483 : {
484 :
485 : /**
486 : * Array with @e token_envelopes_cnt (blinded) token envelopes.
487 : */
488 : struct TokenEnvelope *token_envelopes;
489 :
490 : /**
491 : * Index of selected choice in the @e contract_terms choices array.
492 : */
493 : int16_t choice_index;
494 :
495 : /**
496 : * Number of token envelopes passed to this request.
497 : * Length of the @e token_envelopes array.
498 : */
499 : size_t token_envelopes_cnt;
500 :
501 : /**
502 : * Hash of the canonicalized wallet data json object.
503 : */
504 : struct GNUNET_HashCode h_wallet_data;
505 :
506 : } parse_wallet_data;
507 :
508 : /**
509 : * Results from the phase_check_contract()
510 : */
511 : struct
512 : {
513 :
514 : /**
515 : * Hashed @e contract_terms.
516 : */
517 : struct TALER_PrivateContractHashP h_contract_terms;
518 :
519 : /**
520 : * Our contract (or NULL if not available).
521 : */
522 : json_t *contract_terms_json;
523 :
524 : /**
525 : * Parsed contract terms, NULL when parsing failed.
526 : */
527 : struct TALER_MERCHANT_Contract *contract_terms;
528 :
529 : /**
530 : * What wire method (of the @e mi) was selected by the wallet?
531 : * Set in #phase_parse_pay().
532 : */
533 : struct TMH_WireMethod *wm;
534 :
535 : /**
536 : * Set to the POS key, if applicable for this order.
537 : */
538 : char *pos_key;
539 :
540 : /**
541 : * Serial number of this order in the database (set once we did the lookup).
542 : */
543 : uint64_t order_serial;
544 :
545 : /**
546 : * Algorithm chosen for generating the confirmation code.
547 : */
548 : enum TALER_MerchantConfirmationAlgorithm pos_alg;
549 :
550 : } check_contract;
551 :
552 : /**
553 : * Results from the phase_validate_tokens()
554 : */
555 : struct
556 : {
557 :
558 : /**
559 : * Maximum fee the merchant is willing to pay, from @e root.
560 : * Note that IF the total fee of the exchange is higher, that is
561 : * acceptable to the merchant if the customer is willing to
562 : * pay the difference
563 : * (i.e. amount - max_fee <= actual_amount - actual_fee).
564 : */
565 : struct TALER_Amount max_fee;
566 :
567 : /**
568 : * Amount from @e root. This is the amount the merchant expects
569 : * to make, minus @e max_fee.
570 : */
571 : struct TALER_Amount brutto;
572 :
573 : /**
574 : * Array with @e output_tokens_len signed tokens returned in
575 : * the response to the wallet.
576 : */
577 : struct SignedOutputToken *output_tokens;
578 :
579 : /**
580 : * Number of output tokens to return in the response.
581 : * Length of the @e output_tokens array.
582 : */
583 : unsigned int output_tokens_len;
584 :
585 : } validate_tokens;
586 :
587 : /**
588 : * Results from the phase_execute_pay_transaction()
589 : */
590 : struct
591 : {
592 :
593 : /**
594 : * Considering all the coins with the "found_in_db" flag
595 : * set, what is the total amount we were so far paid on
596 : * this contract?
597 : */
598 : struct TALER_Amount total_paid;
599 :
600 : /**
601 : * Considering all the coins with the "found_in_db" flag
602 : * set, what is the total amount we had to pay in deposit
603 : * fees so far on this contract?
604 : */
605 : struct TALER_Amount total_fees_paid;
606 :
607 : /**
608 : * Considering all the coins with the "found_in_db" flag
609 : * set, what is the total amount we already refunded?
610 : */
611 : struct TALER_Amount total_refunded;
612 :
613 : /**
614 : * Number of coin deposits pending.
615 : */
616 : unsigned int pending;
617 :
618 : /**
619 : * How often have we retried the 'main' transaction?
620 : */
621 : unsigned int retry_counter;
622 :
623 : /**
624 : * Set to true if the deposit currency of a coin
625 : * does not match the contract currency.
626 : */
627 : bool deposit_currency_mismatch;
628 :
629 : /**
630 : * Set to true if the database contains a (bogus)
631 : * refund for a different currency.
632 : */
633 : bool refund_currency_mismatch;
634 :
635 : } pay_transaction;
636 :
637 : /**
638 : * Results from the phase_batch_deposits()
639 : */
640 : struct
641 : {
642 :
643 : /**
644 : * Task called when the (suspended) processing for
645 : * the /pay request times out.
646 : * Happens when we don't get a response from the exchange.
647 : */
648 : struct GNUNET_SCHEDULER_Task *timeout_task;
649 :
650 : /**
651 : * Number of batch transactions pending.
652 : */
653 : unsigned int pending_at_eg;
654 :
655 : /**
656 : * Did any exchange deny a deposit for legal reasons?
657 : */
658 : bool got_451;
659 :
660 : } batch_deposits;
661 :
662 : };
663 :
664 :
665 : /**
666 : * Head of active pay context DLL.
667 : */
668 : static struct PayContext *pc_head;
669 :
670 : /**
671 : * Tail of active pay context DLL.
672 : */
673 : static struct PayContext *pc_tail;
674 :
675 :
676 : void
677 14 : TMH_force_pc_resume ()
678 : {
679 14 : for (struct PayContext *pc = pc_head;
680 14 : NULL != pc;
681 0 : pc = pc->next)
682 : {
683 0 : if (NULL != pc->batch_deposits.timeout_task)
684 : {
685 0 : GNUNET_SCHEDULER_cancel (pc->batch_deposits.timeout_task);
686 0 : pc->batch_deposits.timeout_task = NULL;
687 : }
688 0 : if (GNUNET_YES == pc->suspended)
689 : {
690 0 : pc->suspended = GNUNET_SYSERR;
691 0 : MHD_resume_connection (pc->connection);
692 : }
693 : }
694 14 : }
695 :
696 :
697 : /**
698 : * Resume payment processing.
699 : *
700 : * @param[in,out] pc payment process to resume
701 : */
702 : static void
703 34 : pay_resume (struct PayContext *pc)
704 : {
705 34 : GNUNET_assert (GNUNET_YES == pc->suspended);
706 34 : pc->suspended = GNUNET_NO;
707 34 : MHD_resume_connection (pc->connection);
708 34 : TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
709 34 : }
710 :
711 :
712 : /**
713 : * Resume the given pay context and send the given response.
714 : * Stores the response in the @a pc and signals MHD to resume
715 : * the connection. Also ensures MHD runs immediately.
716 : *
717 : * @param pc payment context
718 : * @param response_code response code to use
719 : * @param response response data to send back
720 : */
721 : static void
722 6 : resume_pay_with_response (struct PayContext *pc,
723 : unsigned int response_code,
724 : struct MHD_Response *response)
725 : {
726 6 : pc->response_code = response_code;
727 6 : pc->response = response;
728 6 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
729 : "Resuming /pay handling. HTTP status for our reply is %u.\n",
730 : response_code);
731 12 : for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
732 : {
733 6 : struct ExchangeGroup *eg = pc->parse_pay.egs[i];
734 :
735 6 : if (NULL != eg->fo)
736 : {
737 0 : TMH_EXCHANGES_keys4exchange_cancel (eg->fo);
738 0 : eg->fo = NULL;
739 0 : pc->batch_deposits.pending_at_eg--;
740 : }
741 6 : if (NULL != eg->bdh)
742 : {
743 0 : TALER_EXCHANGE_batch_deposit_cancel (eg->bdh);
744 0 : eg->bdh = NULL;
745 0 : pc->batch_deposits.pending_at_eg--;
746 : }
747 : }
748 6 : GNUNET_assert (0 == pc->batch_deposits.pending_at_eg);
749 6 : if (NULL != pc->batch_deposits.timeout_task)
750 : {
751 6 : GNUNET_SCHEDULER_cancel (pc->batch_deposits.timeout_task);
752 6 : pc->batch_deposits.timeout_task = NULL;
753 : }
754 6 : pc->phase = PP_RETURN_RESPONSE;
755 6 : pay_resume (pc);
756 6 : }
757 :
758 :
759 : /**
760 : * Resume payment processing with an error.
761 : *
762 : * @param pc operation to resume
763 : * @param ec taler error code to return
764 : * @param msg human readable error message
765 : */
766 : static void
767 0 : resume_pay_with_error (struct PayContext *pc,
768 : enum TALER_ErrorCode ec,
769 : const char *msg)
770 : {
771 0 : resume_pay_with_response (
772 : pc,
773 : TALER_ErrorCode_get_http_status_safe (ec),
774 : TALER_MHD_make_error (ec,
775 : msg));
776 0 : }
777 :
778 :
779 : /**
780 : * Conclude payment processing for @a pc with the
781 : * given @a res MHD status code.
782 : *
783 : * @param[in,out] pc payment context for final state transition
784 : * @param res MHD return code to end with
785 : */
786 : static void
787 42 : pay_end (struct PayContext *pc,
788 : MHD_RESULT res)
789 : {
790 42 : pc->phase = (MHD_YES == res)
791 : ? PP_END_YES
792 42 : : PP_END_NO;
793 42 : }
794 :
795 :
796 : /**
797 : * Return response stored in @a pc.
798 : *
799 : * @param[in,out] pc payment context we are processing
800 : */
801 : static void
802 6 : phase_return_response (struct PayContext *pc)
803 : {
804 6 : GNUNET_assert (0 != pc->response_code);
805 : /* We are *done* processing the request, just queue the response (!) */
806 6 : if (UINT_MAX == pc->response_code)
807 : {
808 0 : GNUNET_break (0);
809 0 : pay_end (pc,
810 : MHD_NO); /* hard error */
811 0 : return;
812 : }
813 6 : pay_end (pc,
814 : MHD_queue_response (pc->connection,
815 : pc->response_code,
816 : pc->response));
817 : }
818 :
819 :
820 : /**
821 : * Return a response indicating failure for legal reasons.
822 : *
823 : * @param[in,out] pc payment context we are processing
824 : */
825 : static void
826 0 : phase_fail_for_legal_reasons (struct PayContext *pc)
827 : {
828 : json_t *exchanges;
829 :
830 0 : GNUNET_assert (0 == pc->pay_transaction.pending);
831 0 : GNUNET_assert (pc->batch_deposits.got_451);
832 0 : exchanges = json_array ();
833 0 : GNUNET_assert (NULL != exchanges);
834 0 : for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
835 : {
836 0 : struct ExchangeGroup *eg = pc->parse_pay.egs[i];
837 :
838 0 : GNUNET_assert (NULL == eg->fo);
839 0 : GNUNET_assert (NULL == eg->bdh);
840 0 : if (! eg->got_451)
841 0 : continue;
842 0 : GNUNET_assert (
843 : 0 ==
844 : json_array_append_new (
845 : exchanges,
846 : json_string (eg->exchange_url)));
847 : }
848 0 : pay_end (pc,
849 0 : TALER_MHD_REPLY_JSON_PACK (
850 : pc->connection,
851 : MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
852 : GNUNET_JSON_pack_array_steal ("exchange_base_urls",
853 : exchanges)));
854 0 : }
855 :
856 :
857 : /**
858 : * Do database transaction for a completed batch deposit.
859 : *
860 : * @param eg group that completed
861 : * @param dr response from the server
862 : * @return transaction status
863 : */
864 : static enum GNUNET_DB_QueryStatus
865 28 : batch_deposit_transaction (const struct ExchangeGroup *eg,
866 : const struct TALER_EXCHANGE_BatchDepositResult *dr)
867 : {
868 28 : const struct PayContext *pc = eg->pc;
869 : enum GNUNET_DB_QueryStatus qs;
870 : struct TALER_Amount total_without_fees;
871 : uint64_t b_dep_serial;
872 28 : uint32_t off = 0;
873 :
874 28 : GNUNET_assert (GNUNET_OK ==
875 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
876 : &total_without_fees));
877 62 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
878 : {
879 34 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
880 : struct TALER_Amount amount_without_fees;
881 :
882 : /* might want to group deposits by batch more explicitly ... */
883 34 : if (0 != strcmp (eg->exchange_url,
884 34 : dc->exchange_url))
885 0 : continue;
886 34 : if (dc->found_in_db)
887 0 : continue;
888 34 : GNUNET_assert (0 <=
889 : TALER_amount_subtract (&amount_without_fees,
890 : &dc->cdd.amount,
891 : &dc->deposit_fee));
892 34 : GNUNET_assert (0 <=
893 : TALER_amount_add (&total_without_fees,
894 : &total_without_fees,
895 : &amount_without_fees));
896 : }
897 28 : qs = TMH_db->insert_deposit_confirmation (
898 28 : TMH_db->cls,
899 28 : pc->hc->instance->settings.id,
900 : dr->details.ok.deposit_timestamp,
901 : &pc->check_contract.h_contract_terms,
902 28 : eg->exchange_url,
903 28 : pc->check_contract.contract_terms->wire_deadline,
904 : &total_without_fees,
905 : &eg->wire_fee,
906 28 : &pc->check_contract.wm->h_wire,
907 28 : dr->details.ok.exchange_sig,
908 28 : dr->details.ok.exchange_pub,
909 : &b_dep_serial);
910 28 : if (qs <= 0)
911 0 : return qs; /* Entire batch already known or failure, we're done */
912 :
913 62 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
914 : {
915 34 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
916 :
917 : /* might want to group deposits by batch more explicitly ... */
918 34 : if (0 != strcmp (eg->exchange_url,
919 34 : dc->exchange_url))
920 0 : continue;
921 34 : if (dc->found_in_db)
922 0 : continue;
923 : /* FIXME-#9457: We might want to check if the order was fully paid concurrently
924 : by some other wallet here, and if so, issue an auto-refund. Right now,
925 : it is possible to over-pay if two wallets literally make a concurrent
926 : payment, as the earlier check for 'paid' is not in the same transaction
927 : scope as this 'insert' operation. */
928 34 : qs = TMH_db->insert_deposit (
929 34 : TMH_db->cls,
930 : off++, /* might want to group deposits by batch more explicitly ... */
931 : b_dep_serial,
932 34 : &dc->cdd.coin_pub,
933 34 : &dc->cdd.coin_sig,
934 34 : &dc->cdd.amount,
935 34 : &dc->deposit_fee,
936 34 : &dc->refund_fee);
937 34 : if (qs < 0)
938 0 : return qs;
939 34 : GNUNET_break (qs > 0);
940 : }
941 28 : return qs;
942 : }
943 :
944 :
945 : /**
946 : * Handle case where the batch deposit completed
947 : * with a status of #MHD_HTTP_OK.
948 : *
949 : * @param eg group that completed
950 : * @param dr response from the server
951 : */
952 : static void
953 28 : handle_batch_deposit_ok (struct ExchangeGroup *eg,
954 : const struct TALER_EXCHANGE_BatchDepositResult *dr)
955 : {
956 28 : struct PayContext *pc = eg->pc;
957 28 : enum GNUNET_DB_QueryStatus qs
958 : = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
959 :
960 : /* store result to DB */
961 28 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
962 : "Storing successful payment %s (%s) at instance `%s'\n",
963 : pc->hc->infix,
964 : GNUNET_h2s (&pc->check_contract.h_contract_terms.hash),
965 : pc->hc->instance->settings.id);
966 28 : for (unsigned int r = 0; r<MAX_RETRIES; r++)
967 : {
968 28 : TMH_db->preflight (TMH_db->cls);
969 28 : if (GNUNET_OK !=
970 28 : TMH_db->start (TMH_db->cls,
971 : "batch-deposit-insert-confirmation"))
972 : {
973 0 : resume_pay_with_response (
974 : pc,
975 : MHD_HTTP_INTERNAL_SERVER_ERROR,
976 0 : TALER_MHD_MAKE_JSON_PACK (
977 : TALER_JSON_pack_ec (
978 : TALER_EC_GENERIC_DB_START_FAILED),
979 : TMH_pack_exchange_reply (&dr->hr)));
980 0 : return;
981 : }
982 28 : qs = batch_deposit_transaction (eg,
983 : dr);
984 28 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
985 : {
986 0 : TMH_db->rollback (TMH_db->cls);
987 0 : continue;
988 : }
989 28 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
990 : {
991 0 : GNUNET_break (0);
992 0 : resume_pay_with_error (pc,
993 : TALER_EC_GENERIC_DB_COMMIT_FAILED,
994 : "batch_deposit_transaction");
995 : }
996 28 : qs = TMH_db->commit (TMH_db->cls);
997 28 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
998 : {
999 0 : TMH_db->rollback (TMH_db->cls);
1000 0 : continue;
1001 : }
1002 28 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
1003 : {
1004 0 : GNUNET_break (0);
1005 0 : resume_pay_with_error (pc,
1006 : TALER_EC_GENERIC_DB_COMMIT_FAILED,
1007 : "insert_deposit");
1008 : }
1009 28 : break; /* DB transaction succeeded */
1010 : }
1011 28 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
1012 : {
1013 0 : resume_pay_with_error (pc,
1014 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
1015 : "insert_deposit");
1016 0 : return;
1017 : }
1018 :
1019 : /* Transaction is done, mark affected coins as complete as well. */
1020 62 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1021 : {
1022 34 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1023 :
1024 34 : if (0 != strcmp (eg->exchange_url,
1025 34 : pc->parse_pay.dc[i].exchange_url))
1026 0 : continue;
1027 34 : if (dc->found_in_db)
1028 0 : continue;
1029 34 : dc->found_in_db = true; /* well, at least NOW it'd be true ;-) */
1030 34 : pc->pay_transaction.pending--;
1031 : }
1032 : }
1033 :
1034 :
1035 : /**
1036 : * Notify taler-merchant-kyccheck that we got a KYC
1037 : * rule violation notification and should start to
1038 : * check our KYC status.
1039 : *
1040 : * @param eg exchange group we were notified for
1041 : */
1042 : static void
1043 0 : notify_kyc_required (const struct ExchangeGroup *eg)
1044 : {
1045 0 : struct GNUNET_DB_EventHeaderP es = {
1046 0 : .size = htons (sizeof (es)),
1047 0 : .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_RULE_TRIGGERED)
1048 : };
1049 : char *hws;
1050 : char *extra;
1051 :
1052 0 : hws = GNUNET_STRINGS_data_to_string_alloc (
1053 0 : &eg->pc->check_contract.contract_terms->h_wire,
1054 : sizeof (eg->pc->check_contract.contract_terms->h_wire));
1055 0 : GNUNET_asprintf (&extra,
1056 : "%s %s",
1057 : hws,
1058 0 : eg->exchange_url);
1059 0 : GNUNET_free (hws);
1060 0 : TMH_db->event_notify (TMH_db->cls,
1061 : &es,
1062 : extra,
1063 0 : strlen (extra) + 1);
1064 0 : GNUNET_free (extra);
1065 0 : }
1066 :
1067 :
1068 : /**
1069 : * Callback to handle a batch deposit permission's response.
1070 : *
1071 : * @param cls a `struct ExchangeGroup`
1072 : * @param dr HTTP response code details
1073 : */
1074 : static void
1075 34 : batch_deposit_cb (
1076 : void *cls,
1077 : const struct TALER_EXCHANGE_BatchDepositResult *dr)
1078 : {
1079 34 : struct ExchangeGroup *eg = cls;
1080 34 : struct PayContext *pc = eg->pc;
1081 :
1082 34 : eg->bdh = NULL;
1083 34 : pc->batch_deposits.pending_at_eg--;
1084 34 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1085 : "Batch deposit completed with status %u\n",
1086 : dr->hr.http_status);
1087 34 : GNUNET_assert (GNUNET_YES == pc->suspended);
1088 34 : switch (dr->hr.http_status)
1089 : {
1090 28 : case MHD_HTTP_OK:
1091 28 : handle_batch_deposit_ok (eg,
1092 : dr);
1093 28 : if (0 == pc->batch_deposits.pending_at_eg)
1094 : {
1095 28 : pc->phase = PP_PAY_TRANSACTION;
1096 28 : pay_resume (pc);
1097 : }
1098 28 : return;
1099 0 : case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
1100 0 : notify_kyc_required (eg);
1101 0 : eg->got_451 = true;
1102 0 : pc->batch_deposits.got_451 = true;
1103 : /* update pc->pay_transaction.pending */
1104 0 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1105 : {
1106 0 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1107 :
1108 0 : if (0 != strcmp (eg->exchange_url,
1109 0 : pc->parse_pay.dc[i].exchange_url))
1110 0 : continue;
1111 0 : if (dc->found_in_db)
1112 0 : continue;
1113 0 : pc->pay_transaction.pending--;
1114 : }
1115 0 : if (0 == pc->batch_deposits.pending_at_eg)
1116 : {
1117 0 : pc->phase = PP_PAY_TRANSACTION;
1118 0 : pay_resume (pc);
1119 : }
1120 0 : return;
1121 6 : default:
1122 6 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1123 : "Deposit operation failed with HTTP code %u/%d\n",
1124 : dr->hr.http_status,
1125 : (int) dr->hr.ec);
1126 : /* Transaction failed */
1127 6 : if (5 == dr->hr.http_status / 100)
1128 : {
1129 : /* internal server error at exchange */
1130 0 : resume_pay_with_response (pc,
1131 : MHD_HTTP_BAD_GATEWAY,
1132 0 : TALER_MHD_MAKE_JSON_PACK (
1133 : TALER_JSON_pack_ec (
1134 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS),
1135 : TMH_pack_exchange_reply (&dr->hr)));
1136 0 : return;
1137 : }
1138 6 : if (NULL == dr->hr.reply)
1139 : {
1140 : /* We can't do anything meaningful here, the exchange did something wrong */
1141 0 : resume_pay_with_response (
1142 : pc,
1143 : MHD_HTTP_BAD_GATEWAY,
1144 0 : TALER_MHD_MAKE_JSON_PACK (
1145 : TALER_JSON_pack_ec (
1146 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_REPLY_MALFORMED),
1147 : TMH_pack_exchange_reply (&dr->hr)));
1148 0 : return;
1149 : }
1150 :
1151 : /* Forward error, adding the "exchange_url" for which the
1152 : error was being generated */
1153 6 : if (TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS == dr->hr.ec)
1154 : {
1155 6 : resume_pay_with_response (
1156 : pc,
1157 : MHD_HTTP_CONFLICT,
1158 6 : TALER_MHD_MAKE_JSON_PACK (
1159 : TALER_JSON_pack_ec (
1160 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS),
1161 : TMH_pack_exchange_reply (&dr->hr),
1162 : GNUNET_JSON_pack_string ("exchange_url",
1163 : eg->exchange_url)));
1164 6 : return;
1165 : }
1166 0 : resume_pay_with_response (
1167 : pc,
1168 : MHD_HTTP_BAD_GATEWAY,
1169 0 : TALER_MHD_MAKE_JSON_PACK (
1170 : TALER_JSON_pack_ec (
1171 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS),
1172 : TMH_pack_exchange_reply (&dr->hr),
1173 : GNUNET_JSON_pack_string ("exchange_url",
1174 : eg->exchange_url)));
1175 0 : return;
1176 : } /* end switch */
1177 : }
1178 :
1179 :
1180 : /**
1181 : * Force re-downloading keys for @a eg.
1182 : *
1183 : * @param[in,out] eg group to re-download keys for
1184 : */
1185 : static void
1186 : force_keys (struct ExchangeGroup *eg);
1187 :
1188 :
1189 : /**
1190 : * Function called with the result of our exchange keys lookup.
1191 : *
1192 : * @param cls the `struct ExchangeGroup`
1193 : * @param keys the keys of the exchange
1194 : * @param exchange representation of the exchange
1195 : */
1196 : static void
1197 34 : process_pay_with_keys (
1198 : void *cls,
1199 : struct TALER_EXCHANGE_Keys *keys,
1200 : struct TMH_Exchange *exchange)
1201 : {
1202 34 : struct ExchangeGroup *eg = cls;
1203 34 : struct PayContext *pc = eg->pc;
1204 34 : struct TMH_HandlerContext *hc = pc->hc;
1205 : unsigned int group_size;
1206 : struct TALER_Amount max_amount;
1207 :
1208 34 : eg->fo = NULL;
1209 34 : pc->batch_deposits.pending_at_eg--;
1210 34 : GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
1211 34 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1212 : "Processing payment with keys from exchange %s\n",
1213 : eg->exchange_url);
1214 34 : GNUNET_assert (GNUNET_YES == pc->suspended);
1215 34 : if (NULL == keys)
1216 : {
1217 0 : GNUNET_break_op (0);
1218 0 : resume_pay_with_error (
1219 : pc,
1220 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT,
1221 : NULL);
1222 0 : return;
1223 : }
1224 34 : if (! TMH_EXCHANGES_is_below_limit (keys,
1225 : TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION,
1226 34 : &eg->total))
1227 : {
1228 0 : GNUNET_break_op (0);
1229 0 : resume_pay_with_error (
1230 : pc,
1231 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_TRANSACTION_LIMIT_VIOLATION,
1232 : eg->exchange_url);
1233 0 : return;
1234 : }
1235 :
1236 34 : max_amount = eg->total;
1237 34 : if (GNUNET_OK !=
1238 34 : TMH_exchange_check_debit (
1239 34 : pc->hc->instance->settings.id,
1240 : exchange,
1241 34 : pc->check_contract.wm,
1242 : &max_amount))
1243 : {
1244 0 : if (eg->tried_force_keys)
1245 : {
1246 0 : GNUNET_break_op (0);
1247 0 : resume_pay_with_error (
1248 : pc,
1249 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED,
1250 : NULL);
1251 0 : return;
1252 : }
1253 0 : force_keys (eg);
1254 0 : return;
1255 : }
1256 34 : if (-1 ==
1257 34 : TALER_amount_cmp (&max_amount,
1258 34 : &eg->total))
1259 : {
1260 : /* max_amount < eg->total */
1261 0 : GNUNET_break_op (0);
1262 0 : resume_pay_with_error (
1263 : pc,
1264 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_TRANSACTION_LIMIT_VIOLATION,
1265 : eg->exchange_url);
1266 0 : return;
1267 : }
1268 :
1269 34 : if (GNUNET_OK !=
1270 34 : TMH_EXCHANGES_lookup_wire_fee (exchange,
1271 34 : pc->check_contract.wm->wire_method,
1272 : &eg->wire_fee))
1273 : {
1274 0 : if (eg->tried_force_keys)
1275 : {
1276 0 : GNUNET_break_op (0);
1277 0 : resume_pay_with_error (
1278 : pc,
1279 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED,
1280 0 : pc->check_contract.wm->wire_method);
1281 0 : return;
1282 : }
1283 0 : force_keys (eg);
1284 0 : return;
1285 : }
1286 34 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1287 : "Got wire data for %s\n",
1288 : eg->exchange_url);
1289 :
1290 : /* Initiate /batch-deposit operation for all coins of
1291 : the current exchange (!) */
1292 34 : group_size = 0;
1293 76 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1294 : {
1295 42 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1296 : const struct TALER_EXCHANGE_DenomPublicKey *denom_details;
1297 42 : bool is_age_restricted_denom = false;
1298 :
1299 42 : if (0 != strcmp (eg->exchange_url,
1300 42 : pc->parse_pay.dc[i].exchange_url))
1301 0 : continue;
1302 42 : if (dc->found_in_db)
1303 0 : continue;
1304 :
1305 : denom_details
1306 42 : = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
1307 42 : &dc->cdd.h_denom_pub);
1308 42 : if (NULL == denom_details)
1309 : {
1310 0 : if (eg->tried_force_keys)
1311 : {
1312 0 : GNUNET_break_op (0);
1313 0 : resume_pay_with_response (
1314 : pc,
1315 : MHD_HTTP_BAD_REQUEST,
1316 0 : TALER_MHD_MAKE_JSON_PACK (
1317 : TALER_JSON_pack_ec (
1318 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND),
1319 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1320 : &dc->cdd.h_denom_pub),
1321 : GNUNET_JSON_pack_allow_null (
1322 : GNUNET_JSON_pack_object_steal (
1323 : "exchange_keys",
1324 : TALER_EXCHANGE_keys_to_json (keys)))));
1325 0 : return;
1326 : }
1327 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1328 : "Missing denomination %s from exchange %s, updating keys\n",
1329 : GNUNET_h2s (&dc->cdd.h_denom_pub.hash),
1330 : eg->exchange_url);
1331 0 : force_keys (eg);
1332 0 : return;
1333 : }
1334 42 : dc->deposit_fee = denom_details->fees.deposit;
1335 42 : dc->refund_fee = denom_details->fees.refund;
1336 :
1337 42 : if (GNUNET_TIME_absolute_is_past (
1338 : denom_details->expire_deposit.abs_time))
1339 : {
1340 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1341 : "Denomination key offered by client has expired for deposits\n");
1342 0 : resume_pay_with_response (
1343 : pc,
1344 : MHD_HTTP_GONE,
1345 0 : TALER_MHD_MAKE_JSON_PACK (
1346 : TALER_JSON_pack_ec (
1347 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_DEPOSIT_EXPIRED),
1348 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1349 : &denom_details->h_key)));
1350 0 : return;
1351 : }
1352 :
1353 : /* Now that we have the details about the denomination, we can verify age
1354 : * restriction requirements, if applicable. Note that denominations with an
1355 : * age_mask equal to zero always pass the age verification. */
1356 42 : is_age_restricted_denom = (0 != denom_details->key.age_mask.bits);
1357 :
1358 42 : if (is_age_restricted_denom &&
1359 0 : (0 < pc->check_contract.contract_terms->minimum_age))
1360 0 : {
1361 : /* Minimum age given and restricted coin provided: We need to verify the
1362 : * minimum age */
1363 0 : unsigned int code = 0;
1364 :
1365 0 : if (dc->no_age_commitment)
1366 : {
1367 0 : GNUNET_break_op (0);
1368 0 : code = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_MISSING;
1369 0 : goto AGE_FAIL;
1370 : }
1371 0 : dc->age_commitment.mask = denom_details->key.age_mask;
1372 0 : if (((int) (dc->age_commitment.num + 1)) !=
1373 0 : __builtin_popcount (dc->age_commitment.mask.bits))
1374 : {
1375 0 : GNUNET_break_op (0);
1376 0 : code =
1377 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_SIZE_MISMATCH;
1378 0 : goto AGE_FAIL;
1379 : }
1380 0 : if (GNUNET_OK !=
1381 0 : TALER_age_commitment_verify (
1382 0 : &dc->age_commitment,
1383 0 : pc->check_contract.contract_terms->minimum_age,
1384 0 : &dc->minimum_age_sig))
1385 0 : code = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_VERIFICATION_FAILED;
1386 0 : AGE_FAIL:
1387 0 : if (0 < code)
1388 : {
1389 0 : GNUNET_break_op (0);
1390 0 : TALER_age_commitment_free (&dc->age_commitment);
1391 0 : resume_pay_with_response (
1392 : pc,
1393 : MHD_HTTP_BAD_REQUEST,
1394 0 : TALER_MHD_MAKE_JSON_PACK (
1395 : TALER_JSON_pack_ec (code),
1396 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1397 : &denom_details->h_key)));
1398 0 : return;
1399 : }
1400 :
1401 : /* Age restriction successfully verified!
1402 : * Calculate the hash of the age commitment. */
1403 0 : TALER_age_commitment_hash (&dc->age_commitment,
1404 : &dc->cdd.h_age_commitment);
1405 0 : TALER_age_commitment_free (&dc->age_commitment);
1406 : }
1407 42 : else if (is_age_restricted_denom &&
1408 0 : dc->no_h_age_commitment)
1409 : {
1410 : /* The contract did not ask for a minimum_age but the client paid
1411 : * with a coin that has age restriction enabled. We lack the hash
1412 : * of the age commitment in this case in order to verify the coin
1413 : * and to deposit it with the exchange. */
1414 0 : GNUNET_break_op (0);
1415 0 : resume_pay_with_response (
1416 : pc,
1417 : MHD_HTTP_BAD_REQUEST,
1418 0 : TALER_MHD_MAKE_JSON_PACK (
1419 : TALER_JSON_pack_ec (
1420 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_HASH_MISSING),
1421 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1422 : &denom_details->h_key)));
1423 0 : return;
1424 : }
1425 42 : group_size++;
1426 : }
1427 :
1428 34 : if (0 == group_size)
1429 : {
1430 0 : GNUNET_break (0);
1431 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1432 : "Group size zero, %u batch transactions remain pending\n",
1433 : pc->batch_deposits.pending_at_eg);
1434 0 : if (0 == pc->batch_deposits.pending_at_eg)
1435 : {
1436 0 : pc->phase = PP_PAY_TRANSACTION;
1437 0 : pay_resume (pc);
1438 0 : return;
1439 : }
1440 0 : return;
1441 : }
1442 34 : if (group_size > TALER_MAX_COINS)
1443 0 : group_size = TALER_MAX_COINS;
1444 34 : {
1445 34 : struct TALER_EXCHANGE_CoinDepositDetail cdds[group_size];
1446 34 : struct TALER_EXCHANGE_DepositContractDetail dcd = {
1447 34 : .wire_deadline = pc->check_contract.contract_terms->wire_deadline,
1448 34 : .merchant_payto_uri = pc->check_contract.wm->payto_uri,
1449 34 : .wire_salt = pc->check_contract.wm->wire_salt,
1450 : .h_contract_terms = pc->check_contract.h_contract_terms,
1451 : .wallet_data_hash = pc->parse_wallet_data.h_wallet_data,
1452 34 : .wallet_timestamp = pc->check_contract.contract_terms->timestamp,
1453 34 : .merchant_pub = hc->instance->merchant_pub,
1454 34 : .refund_deadline = pc->check_contract.contract_terms->refund_deadline
1455 : };
1456 : enum TALER_ErrorCode ec;
1457 34 : size_t off = 0;
1458 :
1459 34 : TALER_merchant_contract_sign (&pc->check_contract.h_contract_terms,
1460 34 : &pc->hc->instance->merchant_priv,
1461 : &dcd.merchant_sig);
1462 42 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1463 : {
1464 42 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1465 :
1466 42 : if (dc->found_in_db)
1467 0 : continue;
1468 42 : if (0 != strcmp (dc->exchange_url,
1469 : eg->exchange_url))
1470 0 : continue;
1471 42 : cdds[off++] = dc->cdd;
1472 42 : if (off >= group_size)
1473 34 : break;
1474 : }
1475 34 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1476 : "Initiating batch deposit with %u coins\n",
1477 : group_size);
1478 : /* Note: the coin signatures over the wallet_data_hash are
1479 : checked inside of this call */
1480 34 : eg->bdh = TALER_EXCHANGE_batch_deposit (
1481 : TMH_curl_ctx,
1482 : eg->exchange_url,
1483 : keys,
1484 : &dcd,
1485 : group_size,
1486 : cdds,
1487 : &batch_deposit_cb,
1488 : eg,
1489 : &ec);
1490 34 : if (NULL == eg->bdh)
1491 : {
1492 : /* Signature was invalid or some other constraint was not satisfied. If
1493 : the exchange was unavailable, we'd get that information in the
1494 : callback. */
1495 0 : GNUNET_break_op (0);
1496 0 : resume_pay_with_response (
1497 : pc,
1498 : TALER_ErrorCode_get_http_status_safe (ec),
1499 0 : TALER_MHD_MAKE_JSON_PACK (
1500 : TALER_JSON_pack_ec (ec),
1501 : GNUNET_JSON_pack_string ("exchange_url",
1502 : eg->exchange_url)));
1503 0 : return;
1504 : }
1505 34 : pc->batch_deposits.pending_at_eg++;
1506 34 : if (TMH_force_audit)
1507 8 : TALER_EXCHANGE_batch_deposit_force_dc (eg->bdh);
1508 : }
1509 : }
1510 :
1511 :
1512 : static void
1513 0 : force_keys (struct ExchangeGroup *eg)
1514 : {
1515 0 : struct PayContext *pc = eg->pc;
1516 :
1517 0 : eg->tried_force_keys = true;
1518 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1519 : "Forcing /keys download (once)\n");
1520 0 : eg->fo = TMH_EXCHANGES_keys4exchange (
1521 : eg->exchange_url,
1522 : true,
1523 : &process_pay_with_keys,
1524 : eg);
1525 0 : if (NULL == eg->fo)
1526 : {
1527 0 : GNUNET_break_op (0);
1528 0 : resume_pay_with_error (pc,
1529 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNTRUSTED,
1530 : eg->exchange_url);
1531 0 : return;
1532 : }
1533 0 : pc->batch_deposits.pending_at_eg++;
1534 : }
1535 :
1536 :
1537 : /**
1538 : * Handle a timeout for the processing of the pay request.
1539 : *
1540 : * @param cls our `struct PayContext`
1541 : */
1542 : static void
1543 0 : handle_pay_timeout (void *cls)
1544 : {
1545 0 : struct PayContext *pc = cls;
1546 :
1547 0 : pc->batch_deposits.timeout_task = NULL;
1548 0 : GNUNET_assert (GNUNET_YES == pc->suspended);
1549 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1550 : "Resuming pay with error after timeout\n");
1551 0 : resume_pay_with_error (pc,
1552 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT,
1553 : NULL);
1554 0 : }
1555 :
1556 :
1557 : /**
1558 : * Compute the timeout for a /pay request based on the number of coins
1559 : * involved.
1560 : *
1561 : * @param num_coins number of coins
1562 : * @returns timeout for the /pay request
1563 : */
1564 : static struct GNUNET_TIME_Relative
1565 34 : get_pay_timeout (unsigned int num_coins)
1566 : {
1567 : struct GNUNET_TIME_Relative t;
1568 :
1569 : /* FIXME-Performance-Optimization: Do some benchmarking to come up with a
1570 : * better timeout. We've increased this value so the wallet integration
1571 : * test passes again on my (Florian) machine.
1572 : */
1573 34 : t = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
1574 34 : 15 * (1 + (num_coins / 5)));
1575 :
1576 34 : return t;
1577 : }
1578 :
1579 :
1580 : /**
1581 : * Start batch deposits for all exchanges involved
1582 : * in this payment.
1583 : *
1584 : * @param[in,out] pc payment context we are processing
1585 : */
1586 : static void
1587 34 : phase_batch_deposits (struct PayContext *pc)
1588 : {
1589 68 : for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
1590 : {
1591 34 : struct ExchangeGroup *eg = pc->parse_pay.egs[i];
1592 34 : bool have_coins = false;
1593 :
1594 34 : for (size_t j = 0; j<pc->parse_pay.coins_cnt; j++)
1595 : {
1596 34 : struct DepositConfirmation *dc = &pc->parse_pay.dc[j];
1597 :
1598 34 : if (0 != strcmp (eg->exchange_url,
1599 34 : dc->exchange_url))
1600 0 : continue;
1601 34 : if (dc->found_in_db)
1602 0 : continue;
1603 34 : have_coins = true;
1604 34 : break;
1605 : }
1606 34 : if (! have_coins)
1607 0 : continue; /* no coins left to deposit at this exchange */
1608 34 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1609 : "Getting /keys for %s\n",
1610 : eg->exchange_url);
1611 34 : eg->fo = TMH_EXCHANGES_keys4exchange (
1612 : eg->exchange_url,
1613 : false,
1614 : &process_pay_with_keys,
1615 : eg);
1616 34 : if (NULL == eg->fo)
1617 : {
1618 0 : GNUNET_break_op (0);
1619 0 : pay_end (pc,
1620 : TALER_MHD_reply_with_error (
1621 : pc->connection,
1622 : MHD_HTTP_BAD_REQUEST,
1623 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNTRUSTED,
1624 : eg->exchange_url));
1625 0 : return;
1626 : }
1627 34 : pc->batch_deposits.pending_at_eg++;
1628 : }
1629 34 : if (0 == pc->batch_deposits.pending_at_eg)
1630 : {
1631 0 : pc->phase = PP_PAY_TRANSACTION;
1632 0 : pay_resume (pc);
1633 0 : return;
1634 : }
1635 : /* Suspend while we interact with the exchange */
1636 34 : MHD_suspend_connection (pc->connection);
1637 34 : pc->suspended = GNUNET_YES;
1638 34 : GNUNET_assert (NULL == pc->batch_deposits.timeout_task);
1639 : pc->batch_deposits.timeout_task
1640 34 : = GNUNET_SCHEDULER_add_delayed (get_pay_timeout (pc->parse_pay.coins_cnt),
1641 : &handle_pay_timeout,
1642 : pc);
1643 : }
1644 :
1645 :
1646 : /**
1647 : * Build JSON array of blindly signed token envelopes,
1648 : * to be used in the response to the wallet.
1649 : *
1650 : * @param[in,out] pc payment context to use
1651 : */
1652 : static json_t *
1653 4 : build_token_sigs (struct PayContext *pc)
1654 : {
1655 4 : json_t *token_sigs = json_array ();
1656 :
1657 4 : GNUNET_assert (NULL != token_sigs);
1658 8 : for (unsigned int i = 0; i < pc->validate_tokens.output_tokens_len; i++)
1659 : {
1660 4 : GNUNET_assert (0 ==
1661 : json_array_append_new (
1662 : token_sigs,
1663 : GNUNET_JSON_PACK (
1664 : GNUNET_JSON_pack_blinded_sig (
1665 : "blind_sig",
1666 : pc->validate_tokens.output_tokens[i].sig.signature)
1667 : )));
1668 : }
1669 4 : return token_sigs;
1670 : }
1671 :
1672 :
1673 : /**
1674 : * Generate response (payment successful)
1675 : *
1676 : * @param[in,out] pc payment context where the payment was successful
1677 : */
1678 : static void
1679 30 : phase_success_response (struct PayContext *pc)
1680 : {
1681 : struct TALER_MerchantSignatureP sig;
1682 : char *pos_confirmation;
1683 : json_t *token_sigs;
1684 :
1685 : /* Sign on our end (as the payment did go through, even if it may
1686 : have been refunded already) */
1687 30 : TALER_merchant_pay_sign (&pc->check_contract.h_contract_terms,
1688 30 : &pc->hc->instance->merchant_priv,
1689 : &sig);
1690 : /* Build the response */
1691 60 : pos_confirmation = (NULL == pc->check_contract.pos_key)
1692 : ? NULL
1693 30 : : TALER_build_pos_confirmation (pc->check_contract.pos_key,
1694 : pc->check_contract.pos_alg,
1695 2 : &pc->validate_tokens.brutto,
1696 2 : pc->check_contract.contract_terms->timestamp
1697 : );
1698 60 : token_sigs = (0 >= pc->validate_tokens.output_tokens_len)
1699 : ? NULL
1700 30 : : build_token_sigs (pc);
1701 : // FIXME: add signatures obtained from donau to response
1702 30 : pay_end (pc,
1703 30 : TALER_MHD_REPLY_JSON_PACK (
1704 : pc->connection,
1705 : MHD_HTTP_OK,
1706 : GNUNET_JSON_pack_allow_null (
1707 : GNUNET_JSON_pack_string ("pos_confirmation",
1708 : pos_confirmation)),
1709 : GNUNET_JSON_pack_allow_null (
1710 : GNUNET_JSON_pack_array_steal ("token_sigs",
1711 : token_sigs)),
1712 : GNUNET_JSON_pack_data_auto ("sig",
1713 : &sig)));
1714 30 : GNUNET_free (pos_confirmation);
1715 30 : }
1716 :
1717 :
1718 : /**
1719 : * Use database to notify other clients about the
1720 : * payment being completed.
1721 : *
1722 : * @param[in,out] pc context to trigger notification for
1723 : */
1724 : static void
1725 28 : phase_payment_notification (struct PayContext *pc)
1726 : {
1727 : {
1728 28 : struct TMH_OrderPayEventP pay_eh = {
1729 28 : .header.size = htons (sizeof (pay_eh)),
1730 28 : .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_PAID),
1731 28 : .merchant_pub = pc->hc->instance->merchant_pub
1732 : };
1733 :
1734 28 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1735 : "Notifying clients about payment of order %s\n",
1736 : pc->order_id);
1737 28 : GNUNET_CRYPTO_hash (pc->order_id,
1738 : strlen (pc->order_id),
1739 : &pay_eh.h_order_id);
1740 28 : TMH_db->event_notify (TMH_db->cls,
1741 : &pay_eh.header,
1742 : NULL,
1743 : 0);
1744 : }
1745 28 : if ( (NULL != pc->parse_pay.session_id) &&
1746 28 : (NULL != pc->check_contract.contract_terms->fulfillment_url) )
1747 : {
1748 16 : struct TMH_SessionEventP session_eh = {
1749 16 : .header.size = htons (sizeof (session_eh)),
1750 16 : .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED),
1751 16 : .merchant_pub = pc->hc->instance->merchant_pub
1752 : };
1753 :
1754 16 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1755 : "Notifying clients about session change to %s for %s\n",
1756 : pc->parse_pay.session_id,
1757 : pc->check_contract.contract_terms->fulfillment_url);
1758 16 : GNUNET_CRYPTO_hash (pc->parse_pay.session_id,
1759 16 : strlen (pc->parse_pay.session_id),
1760 : &session_eh.h_session_id);
1761 16 : GNUNET_CRYPTO_hash (pc->check_contract.contract_terms->fulfillment_url,
1762 16 : strlen (pc->check_contract.contract_terms->
1763 : fulfillment_url),
1764 : &session_eh.h_fulfillment_url);
1765 16 : TMH_db->event_notify (TMH_db->cls,
1766 : &session_eh.header,
1767 : NULL,
1768 : 0);
1769 : }
1770 28 : pc->phase = PP_SUCCESS_RESPONSE;
1771 28 : }
1772 :
1773 :
1774 : /**
1775 : * Function called with information about a coin that was deposited.
1776 : *
1777 : * @param cls closure
1778 : * @param exchange_url exchange where @a coin_pub was deposited
1779 : * @param coin_pub public key of the coin
1780 : * @param amount_with_fee amount the exchange will deposit for this coin
1781 : * @param deposit_fee fee the exchange will charge for this coin
1782 : * @param refund_fee fee the exchange will charge for refunding this coin
1783 : */
1784 : static void
1785 36 : check_coin_paid (void *cls,
1786 : const char *exchange_url,
1787 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
1788 : const struct TALER_Amount *amount_with_fee,
1789 : const struct TALER_Amount *deposit_fee,
1790 : const struct TALER_Amount *refund_fee)
1791 : {
1792 36 : struct PayContext *pc = cls;
1793 :
1794 84 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1795 : {
1796 48 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1797 :
1798 48 : if (dc->found_in_db)
1799 6 : continue; /* processed earlier, skip "expensive" memcmp() */
1800 : /* Get matching coin from results*/
1801 42 : if ( (0 != GNUNET_memcmp (coin_pub,
1802 34 : &dc->cdd.coin_pub)) ||
1803 : (0 !=
1804 34 : strcmp (exchange_url,
1805 68 : dc->exchange_url)) ||
1806 : (GNUNET_OK !=
1807 34 : TALER_amount_cmp_currency (amount_with_fee,
1808 68 : &dc->cdd.amount)) ||
1809 34 : (0 != TALER_amount_cmp (amount_with_fee,
1810 34 : &dc->cdd.amount)) )
1811 8 : continue; /* does not match, skip */
1812 34 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1813 : "Deposit of coin `%s' already in our DB.\n",
1814 : TALER_B2S (coin_pub));
1815 34 : if ( (GNUNET_OK !=
1816 34 : TALER_amount_cmp_currency (&pc->pay_transaction.total_paid,
1817 34 : amount_with_fee)) ||
1818 : (GNUNET_OK !=
1819 34 : TALER_amount_cmp_currency (&pc->pay_transaction.total_fees_paid,
1820 : deposit_fee)) )
1821 : {
1822 0 : GNUNET_break_op (0);
1823 0 : pc->pay_transaction.deposit_currency_mismatch = true;
1824 0 : break;
1825 : }
1826 34 : GNUNET_assert (0 <=
1827 : TALER_amount_add (&pc->pay_transaction.total_paid,
1828 : &pc->pay_transaction.total_paid,
1829 : amount_with_fee));
1830 34 : GNUNET_assert (0 <=
1831 : TALER_amount_add (&pc->pay_transaction.total_fees_paid,
1832 : &pc->pay_transaction.total_fees_paid,
1833 : deposit_fee));
1834 34 : dc->deposit_fee = *deposit_fee;
1835 34 : dc->refund_fee = *refund_fee;
1836 34 : dc->cdd.amount = *amount_with_fee;
1837 34 : dc->found_in_db = true;
1838 34 : pc->pay_transaction.pending--;
1839 : }
1840 36 : }
1841 :
1842 :
1843 : /**
1844 : * Function called with information about a refund. Check if this coin was
1845 : * claimed by the wallet for the transaction, and if so add the refunded
1846 : * amount to the pc's "total_refunded" amount.
1847 : *
1848 : * @param cls closure with a `struct PayContext`
1849 : * @param coin_pub public coin from which the refund comes from
1850 : * @param refund_amount refund amount which is being taken from @a coin_pub
1851 : */
1852 : static void
1853 0 : check_coin_refunded (void *cls,
1854 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
1855 : const struct TALER_Amount *refund_amount)
1856 : {
1857 0 : struct PayContext *pc = cls;
1858 :
1859 : /* We look at refunds here that apply to the coins
1860 : that the customer is currently trying to pay us with.
1861 :
1862 : Such refunds are not "normal" refunds, but abort-pay refunds, which are
1863 : given in the case that the wallet aborts the payment.
1864 : In the case the wallet then decides to complete the payment *after* doing
1865 : an abort-pay refund (an unusual but possible case), we need
1866 : to make sure that existing refunds are accounted for. */
1867 :
1868 0 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1869 : {
1870 0 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1871 :
1872 : /* Get matching coins from results. */
1873 0 : if (0 != GNUNET_memcmp (coin_pub,
1874 : &dc->cdd.coin_pub))
1875 0 : continue;
1876 0 : if (GNUNET_OK !=
1877 0 : TALER_amount_cmp_currency (&pc->pay_transaction.total_refunded,
1878 : refund_amount))
1879 : {
1880 0 : GNUNET_break (0);
1881 0 : pc->pay_transaction.refund_currency_mismatch = true;
1882 0 : break;
1883 : }
1884 0 : GNUNET_assert (0 <=
1885 : TALER_amount_add (&pc->pay_transaction.total_refunded,
1886 : &pc->pay_transaction.total_refunded,
1887 : refund_amount));
1888 0 : break;
1889 : }
1890 0 : }
1891 :
1892 :
1893 : /**
1894 : * Check whether the amount paid is sufficient to cover the price.
1895 : *
1896 : * @param pc payment context to check
1897 : * @return true if the payment is sufficient, false if it is
1898 : * insufficient
1899 : */
1900 : static bool
1901 30 : check_payment_sufficient (struct PayContext *pc)
1902 : {
1903 : struct TALER_Amount acc_fee;
1904 : struct TALER_Amount acc_amount;
1905 : struct TALER_Amount final_amount;
1906 : struct TALER_Amount total_wire_fee;
1907 : struct TALER_Amount total_needed;
1908 :
1909 30 : if (0 == pc->parse_pay.coins_cnt)
1910 2 : return TALER_amount_is_zero (&pc->validate_tokens.brutto);
1911 28 : GNUNET_assert (GNUNET_OK ==
1912 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
1913 : &total_wire_fee));
1914 56 : for (unsigned int i = 0; i < pc->parse_pay.num_exchanges; i++)
1915 : {
1916 28 : if (GNUNET_OK !=
1917 28 : TALER_amount_cmp_currency (&total_wire_fee,
1918 28 : &pc->parse_pay.egs[i]->wire_fee))
1919 : {
1920 0 : GNUNET_break_op (0);
1921 0 : pay_end (pc,
1922 : TALER_MHD_reply_with_error (pc->connection,
1923 : MHD_HTTP_BAD_REQUEST,
1924 : TALER_EC_GENERIC_CURRENCY_MISMATCH,
1925 : total_wire_fee.currency));
1926 0 : return false;
1927 : }
1928 28 : if (0 >
1929 28 : TALER_amount_add (&total_wire_fee,
1930 : &total_wire_fee,
1931 28 : &pc->parse_pay.egs[i]->wire_fee))
1932 : {
1933 0 : GNUNET_break (0);
1934 0 : pay_end (pc,
1935 : TALER_MHD_reply_with_error (
1936 : pc->connection,
1937 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1938 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_WIRE_FEE_ADDITION_FAILED,
1939 : "could not add exchange wire fee to total"));
1940 0 : return false;
1941 : }
1942 : }
1943 :
1944 : /**
1945 : * This loops calculates what are the deposit fee / total
1946 : * amount with fee / and wire fee, for all the coins.
1947 : */
1948 28 : GNUNET_assert (GNUNET_OK ==
1949 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
1950 : &acc_fee));
1951 28 : GNUNET_assert (GNUNET_OK ==
1952 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
1953 : &acc_amount));
1954 62 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1955 : {
1956 34 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1957 :
1958 34 : GNUNET_assert (dc->found_in_db);
1959 34 : if ( (GNUNET_OK !=
1960 34 : TALER_amount_cmp_currency (&acc_fee,
1961 68 : &dc->deposit_fee)) ||
1962 : (GNUNET_OK !=
1963 34 : TALER_amount_cmp_currency (&acc_amount,
1964 34 : &dc->cdd.amount)) )
1965 : {
1966 0 : GNUNET_break_op (0);
1967 0 : pay_end (pc,
1968 : TALER_MHD_reply_with_error (
1969 : pc->connection,
1970 : MHD_HTTP_BAD_REQUEST,
1971 : TALER_EC_GENERIC_CURRENCY_MISMATCH,
1972 0 : dc->deposit_fee.currency));
1973 0 : return false;
1974 : }
1975 34 : if ( (0 >
1976 34 : TALER_amount_add (&acc_fee,
1977 34 : &dc->deposit_fee,
1978 34 : &acc_fee)) ||
1979 : (0 >
1980 34 : TALER_amount_add (&acc_amount,
1981 34 : &dc->cdd.amount,
1982 : &acc_amount)) )
1983 : {
1984 0 : GNUNET_break (0);
1985 : /* Overflow in these amounts? Very strange. */
1986 0 : pay_end (pc,
1987 : TALER_MHD_reply_with_error (
1988 : pc->connection,
1989 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1990 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
1991 : "Overflow adding up amounts"));
1992 0 : return false;
1993 : }
1994 34 : if (1 ==
1995 34 : TALER_amount_cmp (&dc->deposit_fee,
1996 34 : &dc->cdd.amount))
1997 : {
1998 0 : GNUNET_break_op (0);
1999 0 : pay_end (pc,
2000 : TALER_MHD_reply_with_error (
2001 : pc->connection,
2002 : MHD_HTTP_BAD_REQUEST,
2003 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_FEES_EXCEED_PAYMENT,
2004 : "Deposit fees exceed coin's contribution"));
2005 0 : return false;
2006 : }
2007 : } /* end deposit loop */
2008 :
2009 28 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2010 : "Amount received from wallet: %s\n",
2011 : TALER_amount2s (&acc_amount));
2012 28 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2013 : "Deposit fee for all coins: %s\n",
2014 : TALER_amount2s (&acc_fee));
2015 28 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2016 : "Total wire fee: %s\n",
2017 : TALER_amount2s (&total_wire_fee));
2018 28 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2019 : "Deposit fee limit for merchant: %s\n",
2020 : TALER_amount2s (&pc->validate_tokens.max_fee));
2021 28 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2022 : "Total refunded amount: %s\n",
2023 : TALER_amount2s (&pc->pay_transaction.total_refunded));
2024 :
2025 : /* Now compare exchange wire fee compared to what we are willing to pay */
2026 28 : if (GNUNET_YES !=
2027 28 : TALER_amount_cmp_currency (&total_wire_fee,
2028 : &acc_fee))
2029 : {
2030 0 : GNUNET_break (0);
2031 0 : pay_end (pc,
2032 : TALER_MHD_reply_with_error (
2033 : pc->connection,
2034 : MHD_HTTP_BAD_REQUEST,
2035 : TALER_EC_GENERIC_CURRENCY_MISMATCH,
2036 : total_wire_fee.currency));
2037 0 : return false;
2038 : }
2039 :
2040 : /* add wire fee to the total fees */
2041 28 : if (0 >
2042 28 : TALER_amount_add (&acc_fee,
2043 : &acc_fee,
2044 : &total_wire_fee))
2045 : {
2046 0 : GNUNET_break (0);
2047 0 : pay_end (pc,
2048 : TALER_MHD_reply_with_error (
2049 : pc->connection,
2050 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2051 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
2052 : "Overflow adding up amounts"));
2053 0 : return false;
2054 : }
2055 28 : if (-1 == TALER_amount_cmp (&pc->validate_tokens.max_fee,
2056 : &acc_fee))
2057 : {
2058 : /**
2059 : * Sum of fees of *all* the different exchanges of all the coins are
2060 : * higher than the fixed limit that the merchant is willing to pay. The
2061 : * difference must be paid by the customer.
2062 : */
2063 : struct TALER_Amount excess_fee;
2064 :
2065 : /* compute fee amount to be covered by customer */
2066 8 : GNUNET_assert (TALER_AAR_RESULT_POSITIVE ==
2067 : TALER_amount_subtract (&excess_fee,
2068 : &acc_fee,
2069 : &pc->validate_tokens.max_fee));
2070 : /* add that to the total */
2071 8 : if (0 >
2072 8 : TALER_amount_add (&total_needed,
2073 : &excess_fee,
2074 8 : &pc->validate_tokens.brutto))
2075 : {
2076 0 : GNUNET_break (0);
2077 0 : pay_end (pc,
2078 : TALER_MHD_reply_with_error (
2079 : pc->connection,
2080 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2081 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
2082 : "Overflow adding up amounts"));
2083 0 : return false;
2084 : }
2085 : }
2086 : else
2087 : {
2088 : /* Fees are fully covered by the merchant, all we require
2089 : is that the total payment is not below the contract's amount */
2090 20 : total_needed = pc->validate_tokens.brutto;
2091 : }
2092 :
2093 : /* Do not count refunds towards the payment */
2094 28 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2095 : "Subtracting total refunds from paid amount: %s\n",
2096 : TALER_amount2s (&pc->pay_transaction.total_refunded));
2097 28 : if (0 >
2098 28 : TALER_amount_subtract (&final_amount,
2099 : &acc_amount,
2100 28 : &pc->pay_transaction.total_refunded))
2101 : {
2102 0 : GNUNET_break (0);
2103 0 : pay_end (pc,
2104 : TALER_MHD_reply_with_error (
2105 : pc->connection,
2106 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2107 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUNDS_EXCEED_PAYMENTS,
2108 : "refunded amount exceeds total payments"));
2109 0 : return false;
2110 : }
2111 :
2112 28 : if (-1 == TALER_amount_cmp (&final_amount,
2113 : &total_needed))
2114 : {
2115 : /* acc_amount < total_needed */
2116 2 : if (-1 < TALER_amount_cmp (&acc_amount,
2117 : &total_needed))
2118 : {
2119 0 : GNUNET_break_op (0);
2120 0 : pay_end (pc,
2121 : TALER_MHD_reply_with_error (
2122 : pc->connection,
2123 : MHD_HTTP_PAYMENT_REQUIRED,
2124 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUNDED,
2125 : "contract not paid up due to refunds"));
2126 0 : return false;
2127 : }
2128 2 : if (-1 < TALER_amount_cmp (&acc_amount,
2129 2 : &pc->validate_tokens.brutto))
2130 : {
2131 0 : GNUNET_break_op (0);
2132 0 : pay_end (pc,
2133 : TALER_MHD_reply_with_error (
2134 : pc->connection,
2135 : MHD_HTTP_BAD_REQUEST,
2136 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_DUE_TO_FEES,
2137 : "contract not paid up due to fees (client may have calculated them badly)"));
2138 0 : return false;
2139 : }
2140 2 : GNUNET_break_op (0);
2141 2 : pay_end (pc,
2142 : TALER_MHD_reply_with_error (
2143 : pc->connection,
2144 : MHD_HTTP_BAD_REQUEST,
2145 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_PAYMENT_INSUFFICIENT,
2146 : "payment insufficient"));
2147 2 : return false;
2148 : }
2149 26 : return true;
2150 : }
2151 :
2152 :
2153 : /**
2154 : * Execute the DB transaction. If required (from
2155 : * soft/serialization errors), the transaction can be
2156 : * restarted here.
2157 : *
2158 : * @param[in,out] pc payment context to transact
2159 : */
2160 : static void
2161 66 : phase_execute_pay_transaction (struct PayContext *pc)
2162 : {
2163 66 : struct TMH_HandlerContext *hc = pc->hc;
2164 66 : const char *instance_id = hc->instance->settings.id;
2165 :
2166 66 : if (pc->batch_deposits.got_451)
2167 : {
2168 0 : pc->phase = PP_FAIL_LEGAL_REASONS;
2169 0 : return;
2170 : }
2171 : /* Avoid re-trying transactions on soft errors forever! */
2172 66 : if (pc->pay_transaction.retry_counter++ > MAX_RETRIES)
2173 : {
2174 0 : GNUNET_break (0);
2175 0 : pay_end (pc,
2176 : TALER_MHD_reply_with_error (pc->connection,
2177 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2178 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
2179 : NULL));
2180 0 : return;
2181 : }
2182 :
2183 : /* Initialize some amount accumulators
2184 : (used in check_coin_paid(), check_coin_refunded()
2185 : and check_payment_sufficient()). */
2186 66 : GNUNET_break (GNUNET_OK ==
2187 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2188 : &pc->pay_transaction.total_paid));
2189 66 : GNUNET_break (GNUNET_OK ==
2190 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2191 : &pc->pay_transaction.total_fees_paid));
2192 66 : GNUNET_break (GNUNET_OK ==
2193 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2194 : &pc->pay_transaction.total_refunded));
2195 142 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
2196 76 : pc->parse_pay.dc[i].found_in_db = false;
2197 66 : pc->pay_transaction.pending = pc->parse_pay.coins_cnt;
2198 :
2199 : /* First, try to see if we have all we need already done */
2200 66 : TMH_db->preflight (TMH_db->cls);
2201 66 : if (GNUNET_OK !=
2202 66 : TMH_db->start (TMH_db->cls,
2203 : "run pay"))
2204 : {
2205 0 : GNUNET_break (0);
2206 0 : pay_end (pc,
2207 : TALER_MHD_reply_with_error (pc->connection,
2208 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2209 : TALER_EC_GENERIC_DB_START_FAILED,
2210 : NULL));
2211 0 : return;
2212 : }
2213 :
2214 68 : for (size_t i = 0; i<pc->parse_pay.tokens_cnt; i++)
2215 : {
2216 4 : struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[i];
2217 : enum GNUNET_DB_QueryStatus qs;
2218 :
2219 : /* Insert used token into database, the unique constraint will
2220 : case an error if this token was used before. */
2221 4 : qs = TMH_db->insert_spent_token (TMH_db->cls,
2222 4 : &pc->check_contract.h_contract_terms,
2223 4 : &tuc->h_issue,
2224 4 : &tuc->pub,
2225 4 : &tuc->sig,
2226 4 : &tuc->unblinded_sig);
2227 :
2228 4 : if (0 > qs)
2229 : {
2230 0 : TMH_db->rollback (TMH_db->cls);
2231 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
2232 0 : return; /* do it again */
2233 : /* Always report on hard error as well to enable diagnostics */
2234 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
2235 0 : pay_end (pc,
2236 : TALER_MHD_reply_with_error (pc->connection,
2237 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2238 : TALER_EC_GENERIC_DB_STORE_FAILED,
2239 : "insert used token"));
2240 0 : return;
2241 : }
2242 4 : else if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2243 : {
2244 : /* UNIQUE constraint violation, meaning this token was already used. */
2245 2 : TMH_db->rollback (TMH_db->cls);
2246 2 : pay_end (pc,
2247 : TALER_MHD_reply_with_error (pc->connection,
2248 : MHD_HTTP_CONFLICT,
2249 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_TOKEN_INVALID,
2250 : NULL));
2251 2 : return;
2252 : }
2253 : }
2254 :
2255 : {
2256 : enum GNUNET_DB_QueryStatus qs;
2257 :
2258 : /* Check if some of these coins already succeeded for _this_ contract. */
2259 64 : qs = TMH_db->lookup_deposits (TMH_db->cls,
2260 : instance_id,
2261 64 : &pc->check_contract.h_contract_terms,
2262 : &check_coin_paid,
2263 : pc);
2264 64 : if (0 > qs)
2265 : {
2266 0 : TMH_db->rollback (TMH_db->cls);
2267 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
2268 0 : return; /* do it again */
2269 : /* Always report on hard error as well to enable diagnostics */
2270 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
2271 0 : pay_end (pc,
2272 : TALER_MHD_reply_with_error (pc->connection,
2273 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2274 : TALER_EC_GENERIC_DB_FETCH_FAILED,
2275 : "lookup deposits"));
2276 0 : return;
2277 : }
2278 64 : if (pc->pay_transaction.deposit_currency_mismatch)
2279 : {
2280 0 : TMH_db->rollback (TMH_db->cls);
2281 0 : GNUNET_break_op (0);
2282 0 : pay_end (pc,
2283 : TALER_MHD_reply_with_error (pc->connection,
2284 : MHD_HTTP_BAD_REQUEST,
2285 : TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
2286 0 : pc->validate_tokens.brutto.currency))
2287 : ;
2288 0 : return;
2289 : }
2290 : }
2291 :
2292 : {
2293 : enum GNUNET_DB_QueryStatus qs;
2294 :
2295 : /* Check if we refunded some of the coins */
2296 64 : qs = TMH_db->lookup_refunds (TMH_db->cls,
2297 : instance_id,
2298 64 : &pc->check_contract.h_contract_terms,
2299 : &check_coin_refunded,
2300 : pc);
2301 64 : if (0 > qs)
2302 : {
2303 0 : TMH_db->rollback (TMH_db->cls);
2304 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
2305 0 : return; /* do it again */
2306 : /* Always report on hard error as well to enable diagnostics */
2307 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
2308 0 : pay_end (pc,
2309 : TALER_MHD_reply_with_error (pc->connection,
2310 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2311 : TALER_EC_GENERIC_DB_FETCH_FAILED,
2312 : "lookup refunds"));
2313 0 : return;
2314 : }
2315 64 : if (pc->pay_transaction.refund_currency_mismatch)
2316 : {
2317 0 : TMH_db->rollback (TMH_db->cls);
2318 0 : pay_end (pc,
2319 : TALER_MHD_reply_with_error (pc->connection,
2320 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2321 : TALER_EC_GENERIC_DB_FETCH_FAILED,
2322 : "refund currency in database does not match order currency"));
2323 0 : return;
2324 : }
2325 : }
2326 :
2327 : /* Check if there are coins that still need to be processed */
2328 64 : if (0 != pc->pay_transaction.pending)
2329 : {
2330 : /* we made no DB changes, so we can just rollback */
2331 34 : TMH_db->rollback (TMH_db->cls);
2332 : /* Ok, we need to first go to the network to process more coins.
2333 : We that interaction in *tiny* transactions (hence the rollback
2334 : above). */
2335 34 : pc->phase = PP_BATCH_DEPOSITS;
2336 34 : return;
2337 : }
2338 :
2339 : /* 0 == pc->pay_transaction.pending: all coins processed, let's see if that was enough */
2340 30 : if (! check_payment_sufficient (pc))
2341 : {
2342 : /* check_payment_sufficient() will have queued an error already.
2343 : We need to still abort the transaction. */
2344 2 : TMH_db->rollback (TMH_db->cls);
2345 2 : return;
2346 : }
2347 : /* Payment succeeded, save in database */
2348 28 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2349 : "Order `%s' (%s) was fully paid\n",
2350 : pc->order_id,
2351 : GNUNET_h2s (&pc->check_contract.h_contract_terms.hash));
2352 : {
2353 : enum GNUNET_DB_QueryStatus qs;
2354 :
2355 28 : qs = TMH_db->mark_contract_paid (TMH_db->cls,
2356 : instance_id,
2357 28 : &pc->check_contract.h_contract_terms,
2358 28 : pc->parse_pay.session_id,
2359 28 : pc->parse_wallet_data.choice_index);
2360 28 : if (qs < 0)
2361 : {
2362 0 : TMH_db->rollback (TMH_db->cls);
2363 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
2364 0 : return; /* do it again */
2365 0 : GNUNET_break (0);
2366 0 : pay_end (pc,
2367 : TALER_MHD_reply_with_error (pc->connection,
2368 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2369 : TALER_EC_GENERIC_DB_STORE_FAILED,
2370 : "mark contract paid"));
2371 0 : return;
2372 : }
2373 : }
2374 :
2375 : /* Store signed output tokens in database. */
2376 32 : for (size_t i = 0; i<pc->validate_tokens.output_tokens_len; i++)
2377 : {
2378 4 : struct SignedOutputToken *output = &pc->validate_tokens.output_tokens[i];
2379 :
2380 : enum GNUNET_DB_QueryStatus qs;
2381 :
2382 4 : qs = TMH_db->insert_issued_token (TMH_db->cls,
2383 4 : &pc->check_contract.h_contract_terms,
2384 4 : &output->h_issue,
2385 4 : &output->sig);
2386 :
2387 4 : if (0 >= qs)
2388 : {
2389 0 : TMH_db->rollback (TMH_db->cls);
2390 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
2391 0 : return; /* do it again */
2392 : /* Always report on hard error as well to enable diagnostics */
2393 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
2394 0 : pay_end (pc,
2395 : TALER_MHD_reply_with_error (pc->connection,
2396 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2397 : TALER_EC_GENERIC_DB_STORE_FAILED,
2398 : "insert output token"));
2399 0 : return;
2400 : }
2401 : }
2402 :
2403 : // FIXME: insert donau blinded inputs (into DB here!),
2404 : // idempotency: if already exists, no problem!
2405 :
2406 28 : TMH_notify_order_change (hc->instance,
2407 : TMH_OSF_CLAIMED | TMH_OSF_PAID,
2408 28 : pc->check_contract.contract_terms->timestamp,
2409 : pc->check_contract.order_serial);
2410 : {
2411 : enum GNUNET_DB_QueryStatus qs;
2412 : json_t *jhook;
2413 :
2414 28 : jhook = GNUNET_JSON_PACK (
2415 : GNUNET_JSON_pack_object_incref ("contract_terms",
2416 : pc->check_contract.contract_terms_json),
2417 : GNUNET_JSON_pack_string ("order_id",
2418 : pc->order_id)
2419 : );
2420 28 : GNUNET_assert (NULL != jhook);
2421 28 : qs = TMH_trigger_webhook (pc->hc->instance->settings.id,
2422 : "pay",
2423 : jhook);
2424 28 : json_decref (jhook);
2425 28 : if (qs < 0)
2426 : {
2427 0 : TMH_db->rollback (TMH_db->cls);
2428 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
2429 0 : return; /* do it again */
2430 0 : GNUNET_break (0);
2431 0 : pay_end (pc,
2432 : TALER_MHD_reply_with_error (pc->connection,
2433 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2434 : TALER_EC_GENERIC_DB_STORE_FAILED,
2435 : "failed to trigger webhooks"));
2436 0 : return;
2437 : }
2438 : }
2439 : {
2440 : enum GNUNET_DB_QueryStatus qs;
2441 :
2442 : /* Now commit! */
2443 28 : qs = TMH_db->commit (TMH_db->cls);
2444 28 : if (0 > qs)
2445 : {
2446 : /* commit failed */
2447 0 : TMH_db->rollback (TMH_db->cls);
2448 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
2449 0 : return; /* do it again */
2450 0 : GNUNET_break (0);
2451 0 : pay_end (pc,
2452 : TALER_MHD_reply_with_error (pc->connection,
2453 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2454 : TALER_EC_GENERIC_DB_COMMIT_FAILED,
2455 : NULL));
2456 0 : return;
2457 : }
2458 : }
2459 : // FIXME: if we have donation receipts, do NEW phase
2460 : // DONAU interaction here, otherwise skip DONAU phase
2461 : // and move to payment notification
2462 28 : pc->phase = PP_PAYMENT_NOTIFICATION;
2463 : }
2464 :
2465 :
2466 : /**
2467 : * Ensures that the expected number of tokens for a @e key
2468 : * are provided as inputs and have valid signatures.
2469 : *
2470 : * @param[in,out] pc payment context we are processing
2471 : * @param family family the tokens should be from
2472 : * @param index number of the input we are handling
2473 : * @param expected_num number of tokens expected
2474 : * @return #GNUNET_YES on success
2475 : */
2476 : static enum GNUNET_GenericReturnValue
2477 4 : find_valid_input_tokens (
2478 : struct PayContext *pc,
2479 : const struct TALER_MERCHANT_ContractTokenFamily *family,
2480 : unsigned int index,
2481 : unsigned int expected_num)
2482 : {
2483 4 : unsigned int num_validated = 0;
2484 : struct GNUNET_TIME_Timestamp now
2485 4 : = GNUNET_TIME_timestamp_get ();
2486 :
2487 8 : for (unsigned int j = 0; j < expected_num; j++)
2488 : {
2489 4 : struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[index + j];
2490 4 : const struct TALER_MERCHANT_ContractTokenFamilyKey *key = NULL;
2491 :
2492 4 : for (unsigned int i=0; i<family->keys_len; i++)
2493 : {
2494 4 : const struct TALER_MERCHANT_ContractTokenFamilyKey *ki
2495 4 : = &family->keys[i];
2496 :
2497 4 : if (GNUNET_TIME_timestamp_cmp (ki->valid_after, >, now) ||
2498 4 : GNUNET_TIME_timestamp_cmp (ki->valid_before, <, now))
2499 : {
2500 0 : continue; /* ki currently not valid */
2501 : }
2502 4 : if (0 ==
2503 4 : GNUNET_memcmp (&ki->pub.public_key->pub_key_hash,
2504 : &tuc->h_issue.hash))
2505 : {
2506 4 : key = ki;
2507 4 : break;
2508 : }
2509 : }
2510 4 : if (NULL == key)
2511 : {
2512 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2513 : "Input token supplied for public key that is not acceptable\n");
2514 0 : GNUNET_break (0);
2515 0 : pay_end (pc,
2516 : TALER_MHD_reply_with_error (
2517 : pc->connection,
2518 : MHD_HTTP_BAD_REQUEST,
2519 : TALER_EC_MERCHANT_GENERIC_TOKEN_KEY_UNKNOWN,
2520 : NULL));
2521 0 : return GNUNET_NO;
2522 : }
2523 4 : if (GNUNET_OK !=
2524 4 : TALER_token_issue_verify (&tuc->pub,
2525 : &key->pub,
2526 4 : &tuc->unblinded_sig))
2527 : {
2528 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2529 : "Input token for public key with valid_after "
2530 : "`%s' has invalid issue signature\n",
2531 : GNUNET_TIME_timestamp2s (key->valid_after));
2532 0 : GNUNET_break (0);
2533 0 : pay_end (pc,
2534 : TALER_MHD_reply_with_error (
2535 : pc->connection,
2536 : MHD_HTTP_BAD_REQUEST,
2537 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_TOKEN_ISSUE_SIG_INVALID,
2538 : NULL));
2539 0 : return GNUNET_NO;
2540 : }
2541 :
2542 4 : if (GNUNET_OK !=
2543 4 : TALER_wallet_token_use_verify (&pc->check_contract.h_contract_terms,
2544 4 : &pc->parse_wallet_data.h_wallet_data,
2545 4 : &tuc->pub,
2546 4 : &tuc->sig))
2547 : {
2548 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2549 : "Input token for public key with valid_before "
2550 : "`%s' has invalid use signature\n",
2551 : GNUNET_TIME_timestamp2s (key->valid_before));
2552 0 : GNUNET_break (0);
2553 0 : pay_end (pc,
2554 : TALER_MHD_reply_with_error (
2555 : pc->connection,
2556 : MHD_HTTP_BAD_REQUEST,
2557 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_TOKEN_USE_SIG_INVALID,
2558 : NULL));
2559 0 : return GNUNET_NO;
2560 : }
2561 :
2562 4 : num_validated++;
2563 : }
2564 :
2565 4 : if (num_validated != expected_num)
2566 : {
2567 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2568 : "Expected %d tokens for family %s, but found %d\n",
2569 : expected_num,
2570 : family->slug,
2571 : num_validated);
2572 0 : GNUNET_break (0);
2573 0 : pay_end (pc,
2574 : TALER_MHD_reply_with_error (
2575 : pc->connection,
2576 : MHD_HTTP_BAD_REQUEST,
2577 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_TOKEN_COUNT_MISMATCH,
2578 : NULL));
2579 0 : return GNUNET_NO;
2580 : }
2581 :
2582 4 : return GNUNET_YES;
2583 : }
2584 :
2585 :
2586 : /**
2587 : * Check if an output token of the given @a tfk is mandatory, or if
2588 : * wallets are allowed to simply not support it and still proceed.
2589 : *
2590 : * @param tfk token family kind to check
2591 : * @return true if such outputs are mandatory and wallets must supply
2592 : * the corresponding blinded input
2593 : */
2594 : static bool
2595 6 : test_tfk_mandatory (enum TALER_MERCHANTDB_TokenFamilyKind tfk)
2596 : {
2597 6 : switch (tfk)
2598 : {
2599 0 : case TALER_MERCHANTDB_TFK_Discount:
2600 0 : return false;
2601 6 : case TALER_MERCHANTDB_TFK_Subscription:
2602 6 : return true;
2603 : }
2604 0 : GNUNET_break (0);
2605 0 : return false;
2606 : }
2607 :
2608 :
2609 : /**
2610 : * Sign the tokens provided by the wallet for a particular @a key.
2611 : *
2612 : * @param[in,out] payment we are processing
2613 : * @param key token family data
2614 : * @param priv private key to use to sign with
2615 : * @param mandatory true if the token must exist, if false
2616 : * and the client did not provide an envelope, that's OK and
2617 : * we just also skimp on the signature
2618 : * @param index offset in the token envelope array (from other families)
2619 : * @param expected_num number of tokens of this type that we should create
2620 : * @return #GNUNET_NO on failure
2621 : * #GNUNET_OK on success
2622 : */
2623 : static enum GNUNET_GenericReturnValue
2624 6 : sign_token_envelopes (struct PayContext *pc,
2625 : struct TALER_MERCHANT_ContractTokenFamilyKey *key,
2626 : struct TALER_TokenIssuePrivateKey *priv,
2627 : bool mandatory,
2628 : unsigned int index,
2629 : unsigned int expected_num)
2630 : {
2631 6 : unsigned int num_signed = 0;
2632 :
2633 12 : for (unsigned int j = 0; j<expected_num; j++)
2634 : {
2635 6 : unsigned int pos = index + j;
2636 6 : const struct TokenEnvelope *env
2637 6 : = &pc->parse_wallet_data.token_envelopes[pos];
2638 6 : struct SignedOutputToken *output
2639 6 : = &pc->validate_tokens.output_tokens[pos];
2640 :
2641 6 : if ( (pos >= pc->parse_wallet_data.token_envelopes_cnt) ||
2642 6 : (pos >= pc->validate_tokens.output_tokens_len) )
2643 : {
2644 0 : GNUNET_assert (0); /* this should not happen */
2645 : return GNUNET_NO;
2646 : }
2647 6 : if (NULL == env->blinded_token.blinded_pub)
2648 : {
2649 0 : if (! mandatory)
2650 0 : continue;
2651 :
2652 : /* mandatory token families require a token envelope. */
2653 0 : GNUNET_break_op (0);
2654 0 : pay_end (pc,
2655 : TALER_MHD_reply_with_error (
2656 : pc->connection,
2657 : MHD_HTTP_BAD_REQUEST,
2658 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
2659 : "Token envelope for mandatory token family missing"));
2660 0 : return GNUNET_NO;
2661 : }
2662 6 : TALER_token_issue_sign (priv,
2663 : &env->blinded_token,
2664 : &output->sig);
2665 6 : output->h_issue.hash = key->pub.public_key->pub_key_hash;
2666 6 : num_signed++;
2667 : }
2668 :
2669 6 : if (num_signed != expected_num)
2670 : {
2671 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2672 : "Expected %d token envelopes for public key with valid_after "
2673 : "'%s', but found %d\n",
2674 : expected_num,
2675 : GNUNET_TIME_timestamp2s (key->valid_after),
2676 : num_signed);
2677 0 : GNUNET_break (0);
2678 0 : pay_end (pc,
2679 : TALER_MHD_reply_with_error (
2680 : pc->connection,
2681 : MHD_HTTP_BAD_REQUEST,
2682 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_TOKEN_ENVELOPE_COUNT_MISMATCH,
2683 : NULL));
2684 0 : return GNUNET_NO;
2685 : }
2686 :
2687 6 : return GNUNET_OK;
2688 : }
2689 :
2690 :
2691 : /**
2692 : * Find the family entry for the family of the given @a slug
2693 : * in @a pc.
2694 : *
2695 : * @param[in] pc payment context to search
2696 : * @param slug slug to search for
2697 : * @return NULL if @a slug was not found
2698 : */
2699 : static const struct TALER_MERCHANT_ContractTokenFamily *
2700 10 : find_family (const struct PayContext *pc,
2701 : const char *slug)
2702 : {
2703 10 : for (unsigned int i = 0;
2704 10 : i < pc->check_contract.contract_terms->details.v1.token_authorities_len;
2705 0 : i++)
2706 : {
2707 10 : const struct TALER_MERCHANT_ContractTokenFamily *tfi
2708 10 : = &pc->check_contract.contract_terms->details.v1.token_authorities[i];
2709 :
2710 10 : if (0 == strcmp (tfi->slug,
2711 : slug))
2712 : {
2713 10 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2714 : "Token family %s found with %u keys\n",
2715 : slug,
2716 : tfi->keys_len);
2717 10 : return tfi;
2718 : }
2719 : }
2720 0 : return NULL;
2721 : }
2722 :
2723 :
2724 : /**
2725 : * Validate tokens and token envelopes. First, we check if all tokens listed
2726 : * in the 'inputs' array of the selected choice are present in the 'tokens'
2727 : * array of the request. Then, we validate the signatures of each provided
2728 : * token.
2729 : *
2730 : * @param[in,out] pc context we use to handle the payment
2731 : */
2732 : static void
2733 38 : phase_validate_tokens (struct PayContext *pc)
2734 : {
2735 38 : switch (pc->check_contract.contract_terms->version)
2736 : {
2737 32 : case TALER_MERCHANT_CONTRACT_VERSION_0:
2738 : /* No tokens to validate */
2739 32 : pc->phase = PP_PAY_TRANSACTION;
2740 : pc->validate_tokens.max_fee
2741 32 : = pc->check_contract.contract_terms->details.v0.max_fee;
2742 : pc->validate_tokens.brutto
2743 32 : = pc->check_contract.contract_terms->details.v0.brutto;
2744 32 : break;
2745 6 : case TALER_MERCHANT_CONTRACT_VERSION_1:
2746 : {
2747 6 : const struct TALER_MERCHANT_ContractChoice *selected
2748 6 : = &pc->check_contract.contract_terms->details.v1.choices[
2749 6 : pc->parse_wallet_data.choice_index];
2750 :
2751 6 : pc->validate_tokens.max_fee = selected->max_fee;
2752 6 : pc->validate_tokens.brutto = selected->amount;
2753 :
2754 10 : for (unsigned int i = 0; i<selected->inputs_len; i++)
2755 : {
2756 4 : const struct TALER_MERCHANT_ContractInput *input
2757 4 : = &selected->inputs[i];
2758 : const struct TALER_MERCHANT_ContractTokenFamily *family;
2759 :
2760 4 : if (input->type != TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN)
2761 : {
2762 : /* only validate inputs of type token (for now) */
2763 0 : continue;
2764 : }
2765 :
2766 4 : family = find_family (pc,
2767 4 : input->details.token.token_family_slug);
2768 4 : if (NULL == family)
2769 : {
2770 : /* this should never happen, since the choices and
2771 : token families are validated on insert. */
2772 0 : GNUNET_break (0);
2773 0 : pay_end (pc,
2774 : TALER_MHD_reply_with_error (
2775 : pc->connection,
2776 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2777 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
2778 : "token family not found in order"));
2779 0 : return;
2780 : }
2781 4 : if (GNUNET_NO ==
2782 4 : find_valid_input_tokens (pc,
2783 : family,
2784 : i,
2785 4 : input->details.token.count))
2786 : {
2787 : /* Error is already scheduled from find_valid_input_token. */
2788 0 : return;
2789 : }
2790 : }
2791 :
2792 6 : GNUNET_array_grow (pc->validate_tokens.output_tokens,
2793 : pc->validate_tokens.output_tokens_len,
2794 : selected->outputs_len);
2795 :
2796 12 : for (unsigned int i = 0; i<selected->outputs_len; i++)
2797 : {
2798 : enum GNUNET_DB_QueryStatus qs;
2799 : struct TALER_MERCHANTDB_TokenFamilyKeyDetails details;
2800 6 : const struct TALER_MERCHANT_ContractOutput *output
2801 6 : = &selected->outputs[i];
2802 : const struct TALER_MERCHANT_ContractTokenFamily *family;
2803 : struct TALER_MERCHANT_ContractTokenFamilyKey *key;
2804 :
2805 : // FIXME: check donau outputs are good choices
2806 : // (allowed donau, total amount below max, correct year, ...)
2807 : // change 'if' to switch...
2808 6 : if (output->type != TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN)
2809 : {
2810 : /* only validate outputs of type tokens (for now) */
2811 0 : continue;
2812 : }
2813 : // FIXME: move this into a function for the switch case on token...
2814 6 : family = find_family (pc,
2815 6 : output->details.token.token_family_slug);
2816 6 : if (NULL == family)
2817 : {
2818 : /* this should never happen, since the choices and
2819 : token families are validated on insert. */
2820 0 : GNUNET_break (0);
2821 0 : pay_end (pc,
2822 : TALER_MHD_reply_with_error (
2823 : pc->connection,
2824 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2825 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
2826 : "token family not found in order"));
2827 0 : return;
2828 : }
2829 6 : if (output->details.token.key_index >= family->keys_len)
2830 : {
2831 : /* this should never happen, since the choices and
2832 : token families are validated on insert. */
2833 0 : GNUNET_break (0);
2834 0 : pay_end (pc,
2835 : TALER_MHD_reply_with_error (
2836 : pc->connection,
2837 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2838 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
2839 : "key index invalid for token family"));
2840 0 : return;
2841 : }
2842 6 : key = &family->keys[output->details.token.key_index];
2843 6 : qs = TMH_db->lookup_token_family_key (
2844 6 : TMH_db->cls,
2845 6 : pc->hc->instance->settings.id,
2846 6 : family->slug,
2847 6 : pc->check_contract.contract_terms->timestamp,
2848 6 : pc->check_contract.contract_terms->pay_deadline,
2849 : &details);
2850 6 : if (qs <= 0)
2851 : {
2852 0 : GNUNET_log (
2853 : GNUNET_ERROR_TYPE_ERROR,
2854 : "Did not find key for %s at [%llu,%llu]\n",
2855 : family->slug,
2856 : (unsigned long long) pc->check_contract.contract_terms->timestamp.
2857 : abs_time.
2858 : abs_value_us,
2859 : (unsigned long long) pc->check_contract.contract_terms->pay_deadline
2860 : .abs_time.
2861 : abs_value_us);
2862 0 : GNUNET_break (0);
2863 0 : pay_end (pc,
2864 : TALER_MHD_reply_with_error (
2865 : pc->connection,
2866 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2867 : TALER_EC_GENERIC_DB_FETCH_FAILED,
2868 : NULL));
2869 0 : return;
2870 : }
2871 :
2872 6 : GNUNET_assert (NULL != details.priv.private_key);
2873 6 : if (GNUNET_OK !=
2874 6 : sign_token_envelopes (
2875 : pc,
2876 : key,
2877 : &details.priv,
2878 6 : test_tfk_mandatory (details.token_family.kind),
2879 : i,
2880 6 : output->details.token.count))
2881 : {
2882 : /* Error is already scheduled from sign_token_envelopes. */
2883 0 : return;
2884 : }
2885 : }
2886 : }
2887 : }
2888 :
2889 80 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
2890 : {
2891 42 : const struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
2892 :
2893 42 : if (GNUNET_OK !=
2894 42 : TALER_amount_cmp_currency (&dc->cdd.amount,
2895 42 : &pc->validate_tokens.brutto))
2896 : {
2897 0 : GNUNET_break_op (0);
2898 0 : fprintf (stderr,
2899 : "HERE (%u): %s != %s\n",
2900 0 : (unsigned int) pc->check_contract.contract_terms->version,
2901 0 : dc->cdd.amount.currency,
2902 0 : TALER_amount2s (&pc->validate_tokens.brutto));
2903 0 : pay_end (pc,
2904 : TALER_MHD_reply_with_error (
2905 : pc->connection,
2906 : MHD_HTTP_CONFLICT,
2907 : TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
2908 0 : pc->validate_tokens.brutto.currency));
2909 0 : return;
2910 : }
2911 : }
2912 :
2913 38 : pc->phase = PP_PAY_TRANSACTION;
2914 : }
2915 :
2916 :
2917 : /**
2918 : * Function called with information about a coin that was deposited.
2919 : * Checks if this coin is in our list of deposits as well.
2920 : *
2921 : * @param cls closure with our `struct PayContext *`
2922 : * @param deposit_serial which deposit operation is this about
2923 : * @param exchange_url URL of the exchange that issued the coin
2924 : * @param h_wire hash of merchant's wire details
2925 : * @param deposit_timestamp when was the deposit made
2926 : * @param amount_with_fee amount the exchange will deposit for this coin
2927 : * @param deposit_fee fee the exchange will charge for this coin
2928 : * @param coin_pub public key of the coin
2929 : */
2930 : static void
2931 6 : deposit_paid_check (
2932 : void *cls,
2933 : uint64_t deposit_serial,
2934 : const char *exchange_url,
2935 : const struct TALER_MerchantWireHashP *h_wire,
2936 : struct GNUNET_TIME_Timestamp deposit_timestamp,
2937 : const struct TALER_Amount *amount_with_fee,
2938 : const struct TALER_Amount *deposit_fee,
2939 : const struct TALER_CoinSpendPublicKeyP *coin_pub)
2940 : {
2941 6 : struct PayContext *pc = cls;
2942 :
2943 14 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
2944 : {
2945 10 : struct DepositConfirmation *dci = &pc->parse_pay.dc[i];
2946 :
2947 10 : if ( (0 ==
2948 10 : GNUNET_memcmp (&dci->cdd.coin_pub,
2949 2 : coin_pub)) &&
2950 : (0 ==
2951 2 : strcmp (dci->exchange_url,
2952 2 : exchange_url)) &&
2953 : (GNUNET_YES ==
2954 2 : TALER_amount_cmp_currency (&dci->cdd.amount,
2955 2 : amount_with_fee)) &&
2956 : (0 ==
2957 2 : TALER_amount_cmp (&dci->cdd.amount,
2958 : amount_with_fee)) )
2959 : {
2960 2 : dci->matched_in_db = true;
2961 2 : break;
2962 : }
2963 : }
2964 6 : }
2965 :
2966 :
2967 : static void
2968 0 : input_tokens_paid_check (
2969 : void *cls,
2970 : uint64_t spent_token_serial,
2971 : const struct TALER_PrivateContractHashP *h_contract_terms,
2972 : const struct TALER_TokenIssuePublicKeyHashP *h_issue_pub,
2973 : const struct TALER_TokenUsePublicKeyP *use_pub,
2974 : const struct TALER_TokenUseSignatureP *use_sig,
2975 : const struct TALER_TokenIssueSignature *issue_sig)
2976 : {
2977 0 : struct PayContext *pc = cls;
2978 :
2979 0 : for (size_t i = 0; i<pc->parse_pay.tokens_cnt; i++)
2980 : {
2981 0 : struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[i];
2982 :
2983 0 : if ( (0 ==
2984 0 : GNUNET_memcmp (&tuc->pub, use_pub)) &&
2985 : (0 ==
2986 0 : GNUNET_memcmp (&tuc->sig, use_sig)) &&
2987 : (0 ==
2988 0 : GNUNET_memcmp (&tuc->unblinded_sig, issue_sig)) )
2989 : {
2990 0 : tuc->found_in_db = true;
2991 0 : break;
2992 : }
2993 : }
2994 0 : }
2995 :
2996 :
2997 : /**
2998 : * Handle case where contract was already paid. Either decides
2999 : * the payment is idempotent, or refunds the excess payment.
3000 : *
3001 : * @param[in,out] pc context we use to handle the payment
3002 : */
3003 : static void
3004 4 : phase_contract_paid (struct PayContext *pc)
3005 : {
3006 : json_t *refunds;
3007 4 : bool unmatched = false;
3008 :
3009 : {
3010 : enum GNUNET_DB_QueryStatus qs;
3011 :
3012 4 : qs = TMH_db->lookup_deposits_by_order (TMH_db->cls,
3013 : pc->check_contract.order_serial,
3014 : &deposit_paid_check,
3015 : pc);
3016 : /* Since orders with choices can have a price of zero,
3017 : 0 is also a valid query state */
3018 4 : if (qs < 0)
3019 : {
3020 0 : GNUNET_break (0);
3021 0 : pay_end (pc,
3022 : TALER_MHD_reply_with_error (
3023 : pc->connection,
3024 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3025 : TALER_EC_GENERIC_DB_FETCH_FAILED,
3026 : "lookup_deposits_by_order"));
3027 2 : return;
3028 : }
3029 : }
3030 8 : for (size_t i = 0; i<pc->parse_pay.coins_cnt && ! unmatched; i++)
3031 : {
3032 4 : struct DepositConfirmation *dci = &pc->parse_pay.dc[i];
3033 :
3034 4 : if (! dci->matched_in_db)
3035 2 : unmatched = true;
3036 : }
3037 : /* Check if provided input tokens match token in the database */
3038 : {
3039 : enum GNUNET_DB_QueryStatus qs;
3040 :
3041 : /* FIXME-Optimization: Maybe use h_contract instead of order_serial here? */
3042 4 : qs = TMH_db->lookup_spent_tokens_by_order (TMH_db->cls,
3043 : pc->check_contract.order_serial,
3044 : &input_tokens_paid_check,
3045 : pc);
3046 :
3047 4 : if (qs < 0)
3048 : {
3049 0 : GNUNET_break (0);
3050 0 : pay_end (pc,
3051 : TALER_MHD_reply_with_error (
3052 : pc->connection,
3053 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3054 : TALER_EC_GENERIC_DB_FETCH_FAILED,
3055 : "lookup_spent_tokens_by_order"));
3056 0 : return;
3057 : }
3058 : }
3059 4 : for (size_t i = 0; i<pc->parse_pay.tokens_cnt && ! unmatched; i++)
3060 : {
3061 0 : struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[i];
3062 :
3063 0 : if (! tuc->found_in_db)
3064 0 : unmatched = true;
3065 : }
3066 4 : if (! unmatched)
3067 : {
3068 : /* Everything fine, idempotent request, generate response immediately */
3069 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3070 : "Idempotent pay request for order `%s', signing again\n",
3071 : pc->order_id);
3072 2 : pc->phase = PP_SUCCESS_RESPONSE;
3073 2 : return;
3074 : }
3075 : /* Conflict, double-payment detected! */
3076 : /* FIXME-#8674: What should we do with input tokens?
3077 : Currently there is no refund for tokens. */
3078 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3079 : "Client attempted to pay extra for already paid order `%s'\n",
3080 : pc->order_id);
3081 2 : refunds = json_array ();
3082 2 : GNUNET_assert (NULL != refunds);
3083 6 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
3084 : {
3085 4 : struct DepositConfirmation *dci = &pc->parse_pay.dc[i];
3086 : struct TALER_MerchantSignatureP merchant_sig;
3087 :
3088 4 : if (dci->matched_in_db)
3089 0 : continue;
3090 4 : TALER_merchant_refund_sign (&dci->cdd.coin_pub,
3091 4 : &pc->check_contract.h_contract_terms,
3092 : 0, /* rtransaction id */
3093 4 : &dci->cdd.amount,
3094 4 : &pc->hc->instance->merchant_priv,
3095 : &merchant_sig);
3096 4 : GNUNET_assert (
3097 : 0 ==
3098 : json_array_append_new (
3099 : refunds,
3100 : GNUNET_JSON_PACK (
3101 : GNUNET_JSON_pack_data_auto (
3102 : "coin_pub",
3103 : &dci->cdd.coin_pub),
3104 : GNUNET_JSON_pack_data_auto (
3105 : "merchant_sig",
3106 : &merchant_sig),
3107 : TALER_JSON_pack_amount ("amount",
3108 : &dci->cdd.amount),
3109 : GNUNET_JSON_pack_uint64 ("rtransaction_id",
3110 : 0))));
3111 : }
3112 2 : pay_end (pc,
3113 2 : TALER_MHD_REPLY_JSON_PACK (
3114 : pc->connection,
3115 : MHD_HTTP_CONFLICT,
3116 : TALER_MHD_PACK_EC (
3117 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_ALREADY_PAID),
3118 : GNUNET_JSON_pack_array_steal ("refunds",
3119 : refunds)));
3120 : }
3121 :
3122 :
3123 : /**
3124 : * Check the database state for the given order.
3125 : * Schedules an error response in the connection on failure.
3126 : *
3127 : * @param[in,out] pc context we use to handle the payment
3128 : */
3129 : static void
3130 42 : phase_check_contract (struct PayContext *pc)
3131 : {
3132 : /* obtain contract terms */
3133 : enum GNUNET_DB_QueryStatus qs;
3134 42 : bool paid = false;
3135 :
3136 42 : if (NULL != pc->check_contract.contract_terms_json)
3137 : {
3138 0 : json_decref (pc->check_contract.contract_terms_json);
3139 0 : pc->check_contract.contract_terms_json = NULL;
3140 : }
3141 42 : if (NULL != pc->check_contract.contract_terms)
3142 : {
3143 0 : TALER_MERCHANT_contract_free (pc->check_contract.contract_terms);
3144 0 : pc->check_contract.contract_terms = NULL;
3145 : }
3146 42 : qs = TMH_db->lookup_contract_terms2 (TMH_db->cls,
3147 42 : pc->hc->instance->settings.id,
3148 : pc->order_id,
3149 : &pc->check_contract.contract_terms_json,
3150 : &pc->check_contract.order_serial,
3151 : &paid,
3152 : NULL,
3153 : &pc->check_contract.pos_key,
3154 : &pc->check_contract.pos_alg);
3155 42 : if (0 > qs)
3156 : {
3157 : /* single, read-only SQL statements should never cause
3158 : serialization problems */
3159 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
3160 : /* Always report on hard error to enable diagnostics */
3161 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
3162 0 : pay_end (pc,
3163 : TALER_MHD_reply_with_error (
3164 : pc->connection,
3165 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3166 : TALER_EC_GENERIC_DB_FETCH_FAILED,
3167 : "contract terms"));
3168 4 : return;
3169 : }
3170 42 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
3171 : {
3172 0 : pay_end (pc,
3173 : TALER_MHD_reply_with_error (
3174 : pc->connection,
3175 : MHD_HTTP_NOT_FOUND,
3176 : TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
3177 : pc->order_id));
3178 0 : return;
3179 : }
3180 : /* hash contract (needed later) */
3181 : #if DEBUG
3182 : json_dumpf (pc->check_contract.contract_terms_json,
3183 : stderr,
3184 : JSON_INDENT (2));
3185 : #endif
3186 42 : if (GNUNET_OK !=
3187 42 : TALER_JSON_contract_hash (pc->check_contract.contract_terms_json,
3188 : &pc->check_contract.h_contract_terms))
3189 : {
3190 0 : GNUNET_break (0);
3191 0 : pay_end (pc,
3192 : TALER_MHD_reply_with_error (
3193 : pc->connection,
3194 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3195 : TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
3196 : NULL));
3197 0 : return;
3198 : }
3199 42 : if (paid)
3200 : {
3201 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3202 : "Order `%s' paid, checking for double-payment\n",
3203 : pc->order_id);
3204 4 : pc->phase = PP_CONTRACT_PAID;
3205 4 : return;
3206 : }
3207 38 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3208 : "Handling payment for order `%s' with contract hash `%s'\n",
3209 : pc->order_id,
3210 : GNUNET_h2s (&pc->check_contract.h_contract_terms.hash));
3211 :
3212 38 : pc->check_contract.contract_terms = TALER_MERCHANT_contract_parse (
3213 : pc->check_contract.contract_terms_json,
3214 : true);
3215 :
3216 38 : if (NULL == pc->check_contract.contract_terms)
3217 : {
3218 : /* invalid contract */
3219 0 : GNUNET_break (0);
3220 0 : pay_end (pc,
3221 : TALER_MHD_reply_with_error (
3222 : pc->connection,
3223 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3224 : TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
3225 : pc->order_id));
3226 0 : return;
3227 : }
3228 :
3229 : /* Get details from contract and check fundamentals */
3230 : {
3231 38 : switch (pc->check_contract.contract_terms->version)
3232 : {
3233 32 : case TALER_MERCHANT_CONTRACT_VERSION_0:
3234 : {
3235 32 : if (pc->parse_wallet_data.choice_index > 0)
3236 : {
3237 0 : GNUNET_break (0);
3238 0 : pay_end (pc,
3239 : TALER_MHD_reply_with_error (
3240 : pc->connection,
3241 : MHD_HTTP_BAD_REQUEST,
3242 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_OUT_OF_BOUNDS,
3243 : "contract terms v0 has no choices"));
3244 0 : return;
3245 : }
3246 : }
3247 32 : break;
3248 6 : case TALER_MERCHANT_CONTRACT_VERSION_1:
3249 : {
3250 6 : if (pc->parse_wallet_data.choice_index < 0)
3251 : {
3252 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3253 : "Order `%s' has non-empty choices array but"
3254 : "request is missing 'choice_index' field\n",
3255 : pc->order_id);
3256 0 : GNUNET_break (0);
3257 0 : pay_end (pc,
3258 : TALER_MHD_reply_with_error (
3259 : pc->connection,
3260 : MHD_HTTP_BAD_REQUEST,
3261 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_MISSING,
3262 : NULL));
3263 0 : return;
3264 : }
3265 6 : if (pc->parse_wallet_data.choice_index >=
3266 6 : pc->check_contract.contract_terms->details.v1.choices_len)
3267 : {
3268 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3269 : "Order `%s' has choices array with %u elements but "
3270 : "request has 'choice_index' field with value %d\n",
3271 : pc->order_id,
3272 : pc->check_contract.contract_terms->details.v1.choices_len,
3273 : pc->parse_wallet_data.choice_index);
3274 0 : GNUNET_break (0);
3275 0 : pay_end (pc,
3276 : TALER_MHD_reply_with_error (
3277 : pc->connection,
3278 : MHD_HTTP_BAD_REQUEST,
3279 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_OUT_OF_BOUNDS,
3280 : NULL));
3281 0 : return;
3282 : }
3283 : }
3284 6 : break;
3285 0 : default:
3286 0 : GNUNET_break (0);
3287 0 : pay_end (pc,
3288 : TALER_MHD_reply_with_error (
3289 : pc->connection,
3290 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3291 : TALER_EC_GENERIC_DB_FETCH_FAILED,
3292 : "contract 'version' in database not supported by this backend")
3293 : );
3294 0 : return;
3295 : }
3296 : }
3297 :
3298 38 : if (GNUNET_TIME_timestamp_cmp (pc->check_contract.contract_terms->
3299 : wire_deadline,
3300 : <,
3301 : pc->check_contract.contract_terms->
3302 : refund_deadline))
3303 : {
3304 : /* This should already have been checked when creating the order! */
3305 0 : GNUNET_break (0);
3306 0 : pay_end (pc,
3307 : TALER_MHD_reply_with_error (
3308 : pc->connection,
3309 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3310 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUND_DEADLINE_PAST_WIRE_TRANSFER_DEADLINE,
3311 : NULL));
3312 0 : return;
3313 : }
3314 38 : if (GNUNET_TIME_absolute_is_past (pc->check_contract.contract_terms->
3315 : pay_deadline.abs_time))
3316 : {
3317 : /* too late */
3318 0 : pay_end (pc,
3319 : TALER_MHD_reply_with_error (
3320 : pc->connection,
3321 : MHD_HTTP_GONE,
3322 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_OFFER_EXPIRED,
3323 : NULL));
3324 0 : return;
3325 : }
3326 :
3327 : /* Make sure wire method (still) exists for this instance */
3328 : {
3329 : struct TMH_WireMethod *wm;
3330 :
3331 38 : wm = pc->hc->instance->wm_head;
3332 39 : while (0 != GNUNET_memcmp (&pc->check_contract.contract_terms->h_wire,
3333 : &wm->h_wire))
3334 1 : wm = wm->next;
3335 38 : if (NULL == wm)
3336 : {
3337 0 : GNUNET_break (0);
3338 0 : pay_end (pc,
3339 : TALER_MHD_reply_with_error (
3340 : pc->connection,
3341 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3342 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_HASH_UNKNOWN,
3343 : NULL));
3344 0 : return;
3345 : }
3346 38 : pc->check_contract.wm = wm;
3347 : }
3348 38 : pc->phase = PP_VALIDATE_TOKENS;
3349 : }
3350 :
3351 :
3352 : /**
3353 : * Try to parse the wallet_data object of the pay request into
3354 : * the given context. Schedules an error response in the connection
3355 : * on failure.
3356 : *
3357 : * @param[in,out] pc context we use to handle the payment
3358 : */
3359 : static void
3360 42 : phase_parse_wallet_data (struct PayContext *pc)
3361 : {
3362 : const json_t *tokens_evs;
3363 : struct GNUNET_JSON_Specification spec[] = {
3364 42 : GNUNET_JSON_spec_mark_optional (
3365 : GNUNET_JSON_spec_int16 ("choice_index",
3366 : &pc->parse_wallet_data.choice_index),
3367 : NULL),
3368 42 : GNUNET_JSON_spec_mark_optional (
3369 : GNUNET_JSON_spec_array_const ("tokens_evs",
3370 : &tokens_evs),
3371 : NULL),
3372 : // FIXME: extend spec for wallet to submit
3373 : // - URL of selected donau
3374 : // - year
3375 : // - BUDIs with blinded donation receipts (donau-key-hash, blinded value)
3376 : // + check in later phase (once we have the contract)
3377 : // that the selected donau was offered and the BUDIs are below the allowed amount
3378 42 : GNUNET_JSON_spec_end ()
3379 : };
3380 :
3381 42 : pc->parse_wallet_data.choice_index = -1;
3382 42 : if (NULL == pc->parse_pay.wallet_data)
3383 : {
3384 36 : pc->phase = PP_CHECK_CONTRACT;
3385 36 : return;
3386 : }
3387 : {
3388 : enum GNUNET_GenericReturnValue res;
3389 :
3390 6 : res = TALER_MHD_parse_json_data (pc->connection,
3391 : pc->parse_pay.wallet_data,
3392 : spec);
3393 6 : if (GNUNET_YES != res)
3394 : {
3395 0 : GNUNET_break_op (0);
3396 0 : pay_end (pc,
3397 : (GNUNET_NO == res)
3398 : ? MHD_YES
3399 : : MHD_NO);
3400 0 : return;
3401 : }
3402 : }
3403 :
3404 : pc->parse_wallet_data.token_envelopes_cnt
3405 6 : = json_array_size (tokens_evs);
3406 6 : if (pc->parse_wallet_data.token_envelopes_cnt >
3407 : MAX_TOKEN_ALLOWED_OUTPUTs)
3408 : {
3409 0 : GNUNET_break_op (0);
3410 0 : pay_end (pc,
3411 : TALER_MHD_reply_with_error (
3412 : pc->connection,
3413 : MHD_HTTP_BAD_REQUEST,
3414 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3415 : "'tokens_evs' array too long"));
3416 0 : return;
3417 : }
3418 : pc->parse_wallet_data.token_envelopes
3419 6 : = GNUNET_new_array (pc->parse_wallet_data.token_envelopes_cnt,
3420 : struct TokenEnvelope);
3421 :
3422 : {
3423 : unsigned int tokens_ev_index;
3424 : json_t *token_ev;
3425 :
3426 12 : json_array_foreach (tokens_evs,
3427 : tokens_ev_index,
3428 : token_ev)
3429 : {
3430 6 : struct TokenEnvelope *ev
3431 6 : = &pc->parse_wallet_data.token_envelopes[tokens_ev_index];
3432 : struct GNUNET_JSON_Specification ispec[] = {
3433 6 : TALER_JSON_spec_token_envelope (NULL,
3434 : &ev->blinded_token),
3435 6 : GNUNET_JSON_spec_end ()
3436 : };
3437 : enum GNUNET_GenericReturnValue res;
3438 :
3439 6 : if (json_is_null (token_ev))
3440 0 : continue;
3441 6 : res = TALER_MHD_parse_json_data (pc->connection,
3442 : token_ev,
3443 : ispec);
3444 6 : if (GNUNET_YES != res)
3445 : {
3446 0 : GNUNET_break_op (0);
3447 0 : pay_end (pc,
3448 : (GNUNET_NO == res)
3449 : ? MHD_YES
3450 : : MHD_NO);
3451 0 : return;
3452 : }
3453 :
3454 6 : for (unsigned int j = 0; j<tokens_ev_index; j++)
3455 : {
3456 0 : if (0 ==
3457 0 : GNUNET_memcmp (ev->blinded_token.blinded_pub,
3458 : pc->parse_wallet_data.token_envelopes[j].
3459 : blinded_token.blinded_pub))
3460 : {
3461 0 : GNUNET_break_op (0);
3462 0 : pay_end (pc,
3463 : TALER_MHD_reply_with_error (
3464 : pc->connection,
3465 : MHD_HTTP_BAD_REQUEST,
3466 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3467 : "duplicate token envelope in list"));
3468 0 : return;
3469 : }
3470 : }
3471 : }
3472 : }
3473 :
3474 6 : TALER_json_hash (pc->parse_pay.wallet_data,
3475 : &pc->parse_wallet_data.h_wallet_data);
3476 :
3477 6 : pc->phase = PP_CHECK_CONTRACT;
3478 : }
3479 :
3480 :
3481 : /**
3482 : * Try to parse the pay request into the given pay context.
3483 : * Schedules an error response in the connection on failure.
3484 : *
3485 : * @param[in,out] pc context we use to handle the payment
3486 : */
3487 : static void
3488 42 : phase_parse_pay (struct PayContext *pc)
3489 : {
3490 42 : const char *session_id = NULL;
3491 : const json_t *coins;
3492 : const json_t *tokens;
3493 : struct GNUNET_JSON_Specification spec[] = {
3494 42 : GNUNET_JSON_spec_array_const ("coins",
3495 : &coins),
3496 42 : GNUNET_JSON_spec_mark_optional (
3497 : GNUNET_JSON_spec_string ("session_id",
3498 : &session_id),
3499 : NULL),
3500 42 : GNUNET_JSON_spec_mark_optional (
3501 : GNUNET_JSON_spec_object_const ("wallet_data",
3502 : &pc->parse_pay.wallet_data),
3503 : NULL),
3504 42 : GNUNET_JSON_spec_mark_optional (
3505 : GNUNET_JSON_spec_array_const ("tokens",
3506 : &tokens),
3507 : NULL),
3508 42 : GNUNET_JSON_spec_end ()
3509 : };
3510 :
3511 42 : GNUNET_assert (PP_PARSE_PAY == pc->phase);
3512 : {
3513 : enum GNUNET_GenericReturnValue res;
3514 :
3515 42 : res = TALER_MHD_parse_json_data (pc->connection,
3516 42 : pc->hc->request_body,
3517 : spec);
3518 42 : if (GNUNET_YES != res)
3519 : {
3520 0 : GNUNET_break_op (0);
3521 0 : pay_end (pc,
3522 : (GNUNET_NO == res)
3523 : ? MHD_YES
3524 : : MHD_NO);
3525 0 : return;
3526 : }
3527 : }
3528 :
3529 : /* copy session ID (if set) */
3530 42 : if (NULL != session_id)
3531 : {
3532 16 : pc->parse_pay.session_id = GNUNET_strdup (session_id);
3533 : }
3534 : else
3535 : {
3536 : /* use empty string as default if client didn't specify it */
3537 26 : pc->parse_pay.session_id = GNUNET_strdup ("");
3538 : }
3539 :
3540 42 : pc->parse_pay.coins_cnt = json_array_size (coins);
3541 42 : if (pc->parse_pay.coins_cnt > MAX_COIN_ALLOWED_COINS)
3542 : {
3543 0 : GNUNET_break_op (0);
3544 0 : pay_end (pc,
3545 : TALER_MHD_reply_with_error (
3546 : pc->connection,
3547 : MHD_HTTP_BAD_REQUEST,
3548 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3549 : "'coins' array too long"));
3550 0 : return;
3551 : }
3552 : /* note: 1 coin = 1 deposit confirmation expected */
3553 42 : pc->parse_pay.dc = GNUNET_new_array (pc->parse_pay.coins_cnt,
3554 : struct DepositConfirmation);
3555 :
3556 : /* This loop populates the array 'dc' in 'pc' */
3557 : {
3558 : unsigned int coins_index;
3559 : json_t *coin;
3560 :
3561 90 : json_array_foreach (coins, coins_index, coin)
3562 : {
3563 48 : struct DepositConfirmation *dc = &pc->parse_pay.dc[coins_index];
3564 : const char *exchange_url;
3565 : struct GNUNET_JSON_Specification ispec[] = {
3566 48 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
3567 : &dc->cdd.coin_sig),
3568 48 : GNUNET_JSON_spec_fixed_auto ("coin_pub",
3569 : &dc->cdd.coin_pub),
3570 48 : TALER_JSON_spec_denom_sig ("ub_sig",
3571 : &dc->cdd.denom_sig),
3572 48 : GNUNET_JSON_spec_fixed_auto ("h_denom",
3573 : &dc->cdd.h_denom_pub),
3574 48 : TALER_JSON_spec_amount_any ("contribution",
3575 : &dc->cdd.amount),
3576 48 : TALER_JSON_spec_web_url ("exchange_url",
3577 : &exchange_url),
3578 : /* if a minimum age was required, the minimum_age_sig and
3579 : * age_commitment must be provided */
3580 48 : GNUNET_JSON_spec_mark_optional (
3581 48 : GNUNET_JSON_spec_fixed_auto ("minimum_age_sig",
3582 : &dc->minimum_age_sig),
3583 : &dc->no_minimum_age_sig),
3584 48 : GNUNET_JSON_spec_mark_optional (
3585 : TALER_JSON_spec_age_commitment ("age_commitment",
3586 : &dc->age_commitment),
3587 : &dc->no_age_commitment),
3588 : /* if minimum age was not required, but coin with age restriction set
3589 : * was used, h_age_commitment must be provided. */
3590 48 : GNUNET_JSON_spec_mark_optional (
3591 48 : GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
3592 : &dc->cdd.h_age_commitment),
3593 : &dc->no_h_age_commitment),
3594 48 : GNUNET_JSON_spec_end ()
3595 : };
3596 : enum GNUNET_GenericReturnValue res;
3597 48 : struct ExchangeGroup *eg = NULL;
3598 :
3599 48 : res = TALER_MHD_parse_json_data (pc->connection,
3600 : coin,
3601 : ispec);
3602 48 : if (GNUNET_YES != res)
3603 : {
3604 0 : GNUNET_break_op (0);
3605 0 : pay_end (pc,
3606 : (GNUNET_NO == res)
3607 : ? MHD_YES
3608 : : MHD_NO);
3609 0 : return;
3610 : }
3611 58 : for (unsigned int j = 0; j<coins_index; j++)
3612 : {
3613 10 : if (0 ==
3614 10 : GNUNET_memcmp (&dc->cdd.coin_pub,
3615 : &pc->parse_pay.dc[j].cdd.coin_pub))
3616 : {
3617 0 : GNUNET_break_op (0);
3618 0 : pay_end (pc,
3619 : TALER_MHD_reply_with_error (pc->connection,
3620 : MHD_HTTP_BAD_REQUEST,
3621 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3622 : "duplicate coin in list"));
3623 0 : return;
3624 : }
3625 : }
3626 :
3627 48 : dc->exchange_url = GNUNET_strdup (exchange_url);
3628 48 : dc->index = coins_index;
3629 48 : dc->pc = pc;
3630 :
3631 : /* Check the consistency of the (potential) age restriction
3632 : * information. */
3633 48 : if (dc->no_age_commitment != dc->no_minimum_age_sig)
3634 : {
3635 0 : GNUNET_break_op (0);
3636 0 : pay_end (pc,
3637 : TALER_MHD_reply_with_error (
3638 : pc->connection,
3639 : MHD_HTTP_BAD_REQUEST,
3640 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3641 : "inconsistent: 'age_commitment' vs. 'minimum_age_sig'"
3642 : ));
3643 0 : return;
3644 : }
3645 :
3646 : /* Setup exchange group */
3647 48 : for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
3648 : {
3649 10 : if (0 ==
3650 10 : strcmp (pc->parse_pay.egs[i]->exchange_url,
3651 : exchange_url))
3652 : {
3653 10 : eg = pc->parse_pay.egs[i];
3654 10 : break;
3655 : }
3656 : }
3657 48 : if (NULL == eg)
3658 : {
3659 38 : eg = GNUNET_new (struct ExchangeGroup);
3660 38 : eg->pc = pc;
3661 38 : eg->exchange_url = dc->exchange_url;
3662 38 : eg->total = dc->cdd.amount;
3663 38 : GNUNET_array_append (pc->parse_pay.egs,
3664 : pc->parse_pay.num_exchanges,
3665 : eg);
3666 : }
3667 : else
3668 : {
3669 10 : if (0 >
3670 10 : TALER_amount_add (&eg->total,
3671 10 : &eg->total,
3672 10 : &dc->cdd.amount))
3673 : {
3674 0 : GNUNET_break_op (0);
3675 0 : pay_end (pc,
3676 : TALER_MHD_reply_with_error (
3677 : pc->connection,
3678 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3679 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
3680 : "Overflow adding up amounts"));
3681 0 : return;
3682 : }
3683 : }
3684 : }
3685 : }
3686 :
3687 42 : pc->parse_pay.tokens_cnt = json_array_size (tokens);
3688 42 : if (pc->parse_pay.tokens_cnt > MAX_TOKEN_ALLOWED_INPUTs)
3689 : {
3690 0 : GNUNET_break_op (0);
3691 0 : pay_end (pc,
3692 : TALER_MHD_reply_with_error (
3693 : pc->connection,
3694 : MHD_HTTP_BAD_REQUEST,
3695 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3696 : "'tokens' array too long"));
3697 0 : return;
3698 : }
3699 :
3700 42 : pc->parse_pay.tokens = GNUNET_new_array (pc->parse_pay.tokens_cnt,
3701 : struct TokenUseConfirmation);
3702 :
3703 : /* This look populates the array 'tokens' in 'pc' */
3704 : {
3705 : unsigned int tokens_index;
3706 : json_t *token;
3707 :
3708 46 : json_array_foreach (tokens, tokens_index, token)
3709 : {
3710 4 : struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[tokens_index];
3711 : struct GNUNET_JSON_Specification ispec[] = {
3712 4 : GNUNET_JSON_spec_fixed_auto ("token_sig",
3713 : &tuc->sig),
3714 4 : GNUNET_JSON_spec_fixed_auto ("token_pub",
3715 : &tuc->pub),
3716 4 : GNUNET_JSON_spec_fixed_auto ("h_issue",
3717 : &tuc->h_issue),
3718 4 : TALER_JSON_spec_token_issue_sig ("ub_sig",
3719 : &tuc->unblinded_sig),
3720 4 : GNUNET_JSON_spec_end ()
3721 : };
3722 : enum GNUNET_GenericReturnValue res;
3723 :
3724 4 : res = TALER_MHD_parse_json_data (pc->connection,
3725 : token,
3726 : ispec);
3727 4 : if (GNUNET_YES != res)
3728 : {
3729 0 : GNUNET_break_op (0);
3730 0 : pay_end (pc,
3731 : (GNUNET_NO == res)
3732 : ? MHD_YES
3733 : : MHD_NO);
3734 0 : return;
3735 : }
3736 :
3737 4 : for (unsigned int j = 0; j<tokens_index; j++)
3738 : {
3739 0 : if (0 ==
3740 0 : GNUNET_memcmp (&tuc->pub,
3741 : &pc->parse_pay.tokens[j].pub))
3742 : {
3743 0 : GNUNET_break_op (0);
3744 0 : pay_end (pc,
3745 : TALER_MHD_reply_with_error (pc->connection,
3746 : MHD_HTTP_BAD_REQUEST,
3747 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3748 : "duplicate token in list"));
3749 0 : return;
3750 : }
3751 : }
3752 : }
3753 : }
3754 :
3755 42 : pc->phase = PP_PARSE_WALLET_DATA;
3756 : }
3757 :
3758 :
3759 : /**
3760 : * Custom cleanup routine for a `struct PayContext`.
3761 : *
3762 : * @param cls the `struct PayContext` to clean up.
3763 : */
3764 : static void
3765 42 : pay_context_cleanup (void *cls)
3766 : {
3767 42 : struct PayContext *pc = cls;
3768 :
3769 42 : if (NULL != pc->batch_deposits.timeout_task)
3770 : {
3771 28 : GNUNET_SCHEDULER_cancel (pc->batch_deposits.timeout_task);
3772 28 : pc->batch_deposits.timeout_task = NULL;
3773 : }
3774 42 : if (NULL != pc->check_contract.contract_terms_json)
3775 : {
3776 42 : json_decref (pc->check_contract.contract_terms_json);
3777 42 : pc->check_contract.contract_terms_json = NULL;
3778 : }
3779 90 : for (unsigned int i = 0; i<pc->parse_pay.coins_cnt; i++)
3780 : {
3781 48 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
3782 :
3783 48 : TALER_denom_sig_free (&dc->cdd.denom_sig);
3784 48 : GNUNET_free (dc->exchange_url);
3785 : }
3786 42 : GNUNET_free (pc->parse_pay.dc);
3787 46 : for (unsigned int i = 0; i<pc->parse_pay.tokens_cnt; i++)
3788 : {
3789 4 : struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[i];
3790 :
3791 4 : TALER_token_issue_sig_free (&tuc->unblinded_sig);
3792 : }
3793 42 : GNUNET_free (pc->parse_pay.tokens);
3794 80 : for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
3795 : {
3796 38 : struct ExchangeGroup *eg = pc->parse_pay.egs[i];
3797 :
3798 38 : if (NULL != eg->fo)
3799 0 : TMH_EXCHANGES_keys4exchange_cancel (eg->fo);
3800 38 : GNUNET_free (eg);
3801 : }
3802 42 : GNUNET_free (pc->parse_pay.egs);
3803 42 : if (NULL != pc->check_contract.contract_terms)
3804 : {
3805 38 : TALER_MERCHANT_contract_free (pc->check_contract.contract_terms);
3806 38 : pc->check_contract.contract_terms = NULL;
3807 : }
3808 42 : if (NULL != pc->response)
3809 : {
3810 6 : MHD_destroy_response (pc->response);
3811 6 : pc->response = NULL;
3812 : }
3813 42 : GNUNET_free (pc->parse_pay.session_id);
3814 42 : GNUNET_CONTAINER_DLL_remove (pc_head,
3815 : pc_tail,
3816 : pc);
3817 42 : GNUNET_free (pc->check_contract.pos_key);
3818 42 : GNUNET_free (pc);
3819 42 : }
3820 :
3821 :
3822 : MHD_RESULT
3823 76 : TMH_post_orders_ID_pay (const struct TMH_RequestHandler *rh,
3824 : struct MHD_Connection *connection,
3825 : struct TMH_HandlerContext *hc)
3826 : {
3827 76 : struct PayContext *pc = hc->ctx;
3828 :
3829 76 : GNUNET_assert (NULL != hc->infix);
3830 76 : if (NULL == pc)
3831 : {
3832 42 : pc = GNUNET_new (struct PayContext);
3833 42 : pc->connection = connection;
3834 42 : pc->hc = hc;
3835 42 : pc->order_id = hc->infix;
3836 42 : hc->ctx = pc;
3837 42 : hc->cc = &pay_context_cleanup;
3838 42 : GNUNET_CONTAINER_DLL_insert (pc_head,
3839 : pc_tail,
3840 : pc);
3841 : }
3842 : while (1)
3843 : {
3844 672 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3845 : "Processing /pay in phase %d\n",
3846 : (int) pc->phase);
3847 374 : switch (pc->phase)
3848 : {
3849 42 : case PP_PARSE_PAY:
3850 42 : phase_parse_pay (pc);
3851 42 : break;
3852 42 : case PP_PARSE_WALLET_DATA:
3853 42 : phase_parse_wallet_data (pc);
3854 42 : break;
3855 42 : case PP_CHECK_CONTRACT:
3856 42 : phase_check_contract (pc);
3857 42 : break;
3858 38 : case PP_VALIDATE_TOKENS:
3859 38 : phase_validate_tokens (pc);
3860 38 : break;
3861 4 : case PP_CONTRACT_PAID:
3862 4 : phase_contract_paid (pc);
3863 4 : break;
3864 66 : case PP_PAY_TRANSACTION:
3865 66 : phase_execute_pay_transaction (pc);
3866 66 : break;
3867 28 : case PP_PAYMENT_NOTIFICATION:
3868 28 : phase_payment_notification (pc);
3869 28 : break;
3870 : // FIXME: donau phase
3871 30 : case PP_SUCCESS_RESPONSE:
3872 30 : phase_success_response (pc);
3873 30 : break;
3874 34 : case PP_BATCH_DEPOSITS:
3875 34 : phase_batch_deposits (pc);
3876 34 : break;
3877 6 : case PP_RETURN_RESPONSE:
3878 6 : phase_return_response (pc);
3879 6 : break;
3880 0 : case PP_FAIL_LEGAL_REASONS:
3881 0 : phase_fail_for_legal_reasons (pc);
3882 0 : break;
3883 42 : case PP_END_YES:
3884 42 : return MHD_YES;
3885 0 : case PP_END_NO:
3886 0 : return MHD_NO;
3887 : }
3888 332 : switch (pc->suspended)
3889 : {
3890 0 : case GNUNET_SYSERR:
3891 : /* during shutdown, we don't generate any more replies */
3892 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3893 : "Processing /pay ends due to shutdown in phase %d\n",
3894 : (int) pc->phase);
3895 0 : return MHD_NO;
3896 298 : case GNUNET_NO:
3897 : /* continue to next phase */
3898 298 : break;
3899 34 : case GNUNET_YES:
3900 34 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3901 : "Processing /pay suspended in phase %d\n",
3902 : (int) pc->phase);
3903 34 : return MHD_YES;
3904 : }
3905 : }
3906 : /* impossible to get here */
3907 : GNUNET_assert (0);
3908 : return MHD_YES;
3909 : }
3910 :
3911 :
3912 : /* end of taler-merchant-httpd_post-orders-ID-pay.c */
|