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 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
51 : #include <donau/donau_service.h>
52 : #include <donau/donau_util.h>
53 : #include <donau/donau_json_lib.h>
54 : #endif
55 :
56 : /**
57 : * How often do we retry the (complex!) database transaction?
58 : */
59 : #define MAX_RETRIES 5
60 :
61 : /**
62 : * Maximum number of coins that we allow per transaction.
63 : * Note that the limit for each batch deposit request to
64 : * the exchange is lower, so we may break a very large
65 : * number of coins up into multiple smaller requests to
66 : * the exchange.
67 : */
68 : #define MAX_COIN_ALLOWED_COINS 1024
69 :
70 : /**
71 : * Maximum number of tokens that we allow as inputs per transaction
72 : */
73 : #define MAX_TOKEN_ALLOWED_INPUTs 64
74 :
75 : /**
76 : * Maximum number of tokens that we allow as outputs per transaction
77 : */
78 : #define MAX_TOKEN_ALLOWED_OUTPUTs 64
79 :
80 : /**
81 : * How often do we ask the exchange again about our
82 : * KYC status? Very rarely, as if the user actively
83 : * changes it, we should usually notice anyway.
84 : */
85 : #define KYC_RETRY_FREQUENCY GNUNET_TIME_UNIT_WEEKS
86 :
87 : /**
88 : * Information we keep for an individual call to the pay handler.
89 : */
90 : struct PayContext;
91 :
92 :
93 : /**
94 : * Different phases of processing the /pay request.
95 : */
96 : enum PayPhase
97 : {
98 : /**
99 : * Initial phase where the request is parsed.
100 : */
101 : PP_PARSE_PAY = 0,
102 :
103 : /**
104 : * Parse wallet data object from the pay request.
105 : */
106 : PP_PARSE_WALLET_DATA,
107 :
108 : /**
109 : * Check database state for the given order.
110 : */
111 : PP_CHECK_CONTRACT,
112 :
113 : /**
114 : * Validate provided tokens and token envelopes.
115 : */
116 : PP_VALIDATE_TOKENS,
117 :
118 : /**
119 : * Check if contract has been paid.
120 : */
121 : PP_CONTRACT_PAID,
122 :
123 : /**
124 : * Compute money pot changes.
125 : */
126 : PP_COMPUTE_MONEY_POTS,
127 :
128 : /**
129 : * Execute payment transaction.
130 : */
131 : PP_PAY_TRANSACTION,
132 :
133 : /**
134 : * Communicate with DONAU to generate a donation receipt from the donor BUDIs.
135 : */
136 : PP_REQUEST_DONATION_RECEIPT,
137 :
138 : /**
139 : * Process the donation receipt response from DONAU (save the donau_sigs to the db).
140 : */
141 : PP_FINAL_OUTPUT_TOKEN_PROCESSING,
142 :
143 : /**
144 : * Notify other processes about successful payment.
145 : */
146 : PP_PAYMENT_NOTIFICATION,
147 :
148 : /**
149 : * Create final success response.
150 : */
151 : PP_SUCCESS_RESPONSE,
152 :
153 : /**
154 : * Perform batch deposits with exchange(s).
155 : */
156 : PP_BATCH_DEPOSITS,
157 :
158 : /**
159 : * Return response in payment context.
160 : */
161 : PP_RETURN_RESPONSE,
162 :
163 : /**
164 : * An exchange denied a deposit, fail for
165 : * legal reasons.
166 : */
167 : PP_FAIL_LEGAL_REASONS,
168 :
169 : /**
170 : * Return #MHD_YES to end processing.
171 : */
172 : PP_END_YES,
173 :
174 : /**
175 : * Return #MHD_NO to end processing.
176 : */
177 : PP_END_NO
178 : };
179 :
180 :
181 : /**
182 : * Information kept during a pay request for each coin.
183 : */
184 : struct DepositConfirmation
185 : {
186 :
187 : /**
188 : * Reference to the main PayContext
189 : */
190 : struct PayContext *pc;
191 :
192 : /**
193 : * URL of the exchange that issued this coin.
194 : */
195 : char *exchange_url;
196 :
197 : /**
198 : * Details about the coin being deposited.
199 : */
200 : struct TALER_EXCHANGE_CoinDepositDetail cdd;
201 :
202 : /**
203 : * Fee charged by the exchange for the deposit operation of this coin.
204 : */
205 : struct TALER_Amount deposit_fee;
206 :
207 : /**
208 : * Fee charged by the exchange for the refund operation of this coin.
209 : */
210 : struct TALER_Amount refund_fee;
211 :
212 : /**
213 : * If a minimum age was required (i. e. pc->minimum_age is large enough),
214 : * this is the signature of the minimum age (as a single uint8_t), using the
215 : * private key to the corresponding age group. Might be all zeroes for no
216 : * age attestation.
217 : */
218 : struct TALER_AgeAttestationP minimum_age_sig;
219 :
220 : /**
221 : * If a minimum age was required (i. e. pc->minimum_age is large enough),
222 : * this is the age commitment (i. e. age mask and vector of EdDSA public
223 : * keys, one per age group) that went into the mining of the coin. The
224 : * SHA256 hash of the mask and the vector of public keys was bound to the
225 : * key.
226 : */
227 : struct TALER_AgeCommitment age_commitment;
228 :
229 : /**
230 : * Age mask in the denomination that defines the age groups. Only
231 : * applicable, if minimum age was required.
232 : */
233 : struct TALER_AgeMask age_mask;
234 :
235 : /**
236 : * Offset of this coin into the `dc` array of all coins in the
237 : * @e pc.
238 : */
239 : unsigned int index;
240 :
241 : /**
242 : * true, if no field "age_commitment" was found in the JSON blob
243 : */
244 : bool no_age_commitment;
245 :
246 : /**
247 : * True, if no field "minimum_age_sig" was found in the JSON blob
248 : */
249 : bool no_minimum_age_sig;
250 :
251 : /**
252 : * true, if no field "h_age_commitment" was found in the JSON blob
253 : */
254 : bool no_h_age_commitment;
255 :
256 : /**
257 : * true if we found this coin in the database.
258 : */
259 : bool found_in_db;
260 :
261 : /**
262 : * true if we #deposit_paid_check() matched this coin in the database.
263 : */
264 : bool matched_in_db;
265 :
266 : };
267 :
268 : struct TokenUseConfirmation
269 : {
270 :
271 : /**
272 : * Signature on the deposit request made using the token use private key.
273 : */
274 : struct TALER_TokenUseSignatureP sig;
275 :
276 : /**
277 : * Token use public key. This key was blindly signed by the merchant during
278 : * the token issuance process.
279 : */
280 : struct TALER_TokenUsePublicKeyP pub;
281 :
282 : /**
283 : * Unblinded signature on the token use public key done by the merchant.
284 : */
285 : struct TALER_TokenIssueSignature unblinded_sig;
286 :
287 : /**
288 : * Hash of the token issue public key associated with this token.
289 : * Note this is set in the validate_tokens phase.
290 : */
291 : struct TALER_TokenIssuePublicKeyHashP h_issue;
292 :
293 : /**
294 : * true if we found this token in the database.
295 : */
296 : bool found_in_db;
297 :
298 : };
299 :
300 :
301 : /**
302 : * Information about a token envelope.
303 : */
304 : struct TokenEnvelope
305 : {
306 :
307 : /**
308 : * Blinded token use public keys waiting to be signed.
309 : */
310 : struct TALER_TokenEnvelope blinded_token;
311 :
312 : };
313 :
314 :
315 : /**
316 : * (Blindly) signed token to be returned to the wallet.
317 : */
318 : struct SignedOutputToken
319 : {
320 :
321 : /**
322 : * Index of the output token that produced
323 : * this blindly signed token.
324 : */
325 : unsigned int output_index;
326 :
327 : /**
328 : * Blinded token use public keys waiting to be signed.
329 : */
330 : struct TALER_BlindedTokenIssueSignature sig;
331 :
332 : /**
333 : * Hash of token issue public key.
334 : */
335 : struct TALER_TokenIssuePublicKeyHashP h_issue;
336 :
337 : };
338 :
339 :
340 : /**
341 : * Information kept during a pay request for each exchange.
342 : */
343 : struct ExchangeGroup
344 : {
345 :
346 : /**
347 : * Payment context this group is part of.
348 : */
349 : struct PayContext *pc;
350 :
351 : /**
352 : * Handle to the batch deposit operation we are performing for this
353 : * exchange, NULL after the operation is done.
354 : */
355 : struct TALER_EXCHANGE_BatchDepositHandle *bdh;
356 :
357 : /**
358 : * Handle for operation to lookup /keys (and auditors) from
359 : * the exchange used for this transaction; NULL if no operation is
360 : * pending.
361 : */
362 : struct TMH_EXCHANGES_KeysOperation *fo;
363 :
364 : /**
365 : * URL of the exchange that issued this coin. Aliases
366 : * the exchange URL of one of the coins, do not free!
367 : */
368 : const char *exchange_url;
369 :
370 : /**
371 : * Total deposit amount in this exchange group.
372 : */
373 : struct TALER_Amount total;
374 :
375 : /**
376 : * Wire fee that applies to this exchange for the
377 : * given payment context's wire method.
378 : */
379 : struct TALER_Amount wire_fee;
380 :
381 : /**
382 : * true if we already tried a forced /keys download.
383 : */
384 : bool tried_force_keys;
385 :
386 : /**
387 : * Did this exchange deny the transaction for legal reasons?
388 : */
389 : bool got_451;
390 : };
391 :
392 :
393 : /**
394 : * Information about donau, that can be fetched even
395 : * if the merhchant doesn't support donau
396 : */
397 : struct DonauData
398 : {
399 : /**
400 : * The user-selected Donau URL.
401 : */
402 : char *donau_url;
403 :
404 : /**
405 : * The donation year, as parsed from "year".
406 : */
407 : uint64_t donation_year;
408 :
409 : /**
410 : * The original BUDI key-pairs array from the donor
411 : * to be used for the receipt creation.
412 : */
413 : const json_t *budikeypairs;
414 : };
415 :
416 : /**
417 : * Information we keep for an individual call to the /pay handler.
418 : */
419 : struct PayContext
420 : {
421 :
422 : /**
423 : * Stored in a DLL.
424 : */
425 : struct PayContext *next;
426 :
427 : /**
428 : * Stored in a DLL.
429 : */
430 : struct PayContext *prev;
431 :
432 : /**
433 : * MHD connection to return to
434 : */
435 : struct MHD_Connection *connection;
436 :
437 : /**
438 : * Details about the client's request.
439 : */
440 : struct TMH_HandlerContext *hc;
441 :
442 : /**
443 : * Transaction ID given in @e root.
444 : */
445 : const char *order_id;
446 :
447 : /**
448 : * Response to return, NULL if we don't have one yet.
449 : */
450 : struct MHD_Response *response;
451 :
452 : /**
453 : * Array with @e output_tokens_len signed tokens returned in
454 : * the response to the wallet.
455 : */
456 : struct SignedOutputToken *output_tokens;
457 :
458 : /**
459 : * Number of output tokens to return in the response.
460 : * Length of the @e output_tokens array.
461 : */
462 : unsigned int output_tokens_len;
463 :
464 : /**
465 : * Counter used to generate the output index in append_output_token_sig().
466 : */
467 : unsigned int output_index_gen;
468 :
469 : /**
470 : * Counter used to generate the output index in append_output_token_sig().
471 : *
472 : * Counts the generated tokens _within_ the current output_index_gen.
473 : */
474 : unsigned int output_token_cnt;
475 :
476 : /**
477 : * HTTP status code to use for the reply, i.e 200 for "OK".
478 : * Special value UINT_MAX is used to indicate hard errors
479 : * (no reply, return #MHD_NO).
480 : */
481 : unsigned int response_code;
482 :
483 : /**
484 : * Payment processing phase we are in.
485 : */
486 : enum PayPhase phase;
487 :
488 : /**
489 : * #GNUNET_NO if the @e connection was not suspended,
490 : * #GNUNET_YES if the @e connection was suspended,
491 : * #GNUNET_SYSERR if @e connection was resumed to as
492 : * part of #MH_force_pc_resume during shutdown.
493 : */
494 : enum GNUNET_GenericReturnValue suspended;
495 :
496 : /**
497 : * Results from the phase_parse_pay()
498 : */
499 : struct
500 : {
501 :
502 : /**
503 : * Array with @e num_exchanges exchanges we are depositing
504 : * coins into.
505 : */
506 : struct ExchangeGroup **egs;
507 :
508 : /**
509 : * Array with @e coins_cnt coins we are despositing.
510 : */
511 : struct DepositConfirmation *dc;
512 :
513 : /**
514 : * Array with @e tokens_cnt input tokens passed to this request.
515 : */
516 : struct TokenUseConfirmation *tokens;
517 :
518 : /**
519 : * Optional session id given in @e root.
520 : * NULL if not given.
521 : */
522 : char *session_id;
523 :
524 : /**
525 : * Wallet data json object from the request. Containing additional
526 : * wallet data such as the selected choice_index.
527 : */
528 : const json_t *wallet_data;
529 :
530 : /**
531 : * Number of coins this payment is made of. Length
532 : * of the @e dc array.
533 : */
534 : size_t coins_cnt;
535 :
536 : /**
537 : * Number of input tokens passed to this request. Length
538 : * of the @e tokens array.
539 : */
540 : size_t tokens_cnt;
541 :
542 : /**
543 : * Number of exchanges involved in the payment. Length
544 : * of the @e eg array.
545 : */
546 : unsigned int num_exchanges;
547 :
548 : } parse_pay;
549 :
550 : /**
551 : * Results from the phase_wallet_data()
552 : */
553 : struct
554 : {
555 :
556 : /**
557 : * Array with @e token_envelopes_cnt (blinded) token envelopes.
558 : */
559 : struct TokenEnvelope *token_envelopes;
560 :
561 : /**
562 : * Index of selected choice in the @e contract_terms choices array.
563 : */
564 : int16_t choice_index;
565 :
566 : /**
567 : * Number of token envelopes passed to this request.
568 : * Length of the @e token_envelopes array.
569 : */
570 : size_t token_envelopes_cnt;
571 :
572 : /**
573 : * Hash of the canonicalized wallet data json object.
574 : */
575 : struct GNUNET_HashCode h_wallet_data;
576 :
577 : /**
578 : * Donau related information
579 : */
580 : struct DonauData donau;
581 :
582 : /**
583 : * Serial from the DB of the donau instance that we are using
584 : */
585 : uint64_t donau_instance_serial;
586 :
587 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
588 : /**
589 : * Number of the blinded key pairs @e bkps
590 : */
591 : unsigned int num_bkps;
592 :
593 : /**
594 : * Blinded key pairs received from the wallet
595 : */
596 : struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps;
597 :
598 : /**
599 : * The id of the charity as saved on the donau.
600 : */
601 : uint64_t charity_id;
602 :
603 : /**
604 : * Private key of the charity(related to the private key of the merchant).
605 : */
606 : struct DONAU_CharityPrivateKeyP charity_priv;
607 :
608 : /**
609 : * Maximum amount of donations that the charity can receive per year.
610 : */
611 : struct TALER_Amount charity_max_per_year;
612 :
613 : /**
614 : * Amount of donations that the charity has received so far this year.
615 : */
616 : struct TALER_Amount charity_receipts_to_date;
617 :
618 : /**
619 : * Donau keys, that we are using to get the information about the bkps.
620 : */
621 : struct DONAU_Keys *donau_keys;
622 :
623 : /**
624 : * Amount from BKPS
625 : */
626 : struct TALER_Amount donation_amount;
627 : #endif
628 :
629 : } parse_wallet_data;
630 :
631 : /**
632 : * Results from the phase_check_contract()
633 : */
634 : struct
635 : {
636 :
637 : /**
638 : * Hashed @e contract_terms.
639 : */
640 : struct TALER_PrivateContractHashP h_contract_terms;
641 :
642 : /**
643 : * Our contract (or NULL if not available).
644 : */
645 : json_t *contract_terms_json;
646 :
647 : /**
648 : * Parsed contract terms, NULL when parsing failed.
649 : */
650 : struct TALER_MERCHANT_Contract *contract_terms;
651 :
652 : /**
653 : * What wire method (of the @e mi) was selected by the wallet?
654 : * Set in #phase_parse_pay().
655 : */
656 : struct TMH_WireMethod *wm;
657 :
658 : /**
659 : * Set to the POS key, if applicable for this order.
660 : */
661 : char *pos_key;
662 :
663 : /**
664 : * Serial number of this order in the database (set once we did the lookup).
665 : */
666 : uint64_t order_serial;
667 :
668 : /**
669 : * Algorithm chosen for generating the confirmation code.
670 : */
671 : enum TALER_MerchantConfirmationAlgorithm pos_alg;
672 :
673 : } check_contract;
674 :
675 : /**
676 : * Results from the phase_validate_tokens()
677 : */
678 : struct
679 : {
680 :
681 : /**
682 : * Maximum fee the merchant is willing to pay, from @e root.
683 : * Note that IF the total fee of the exchange is higher, that is
684 : * acceptable to the merchant if the customer is willing to
685 : * pay the difference
686 : * (i.e. amount - max_fee <= actual_amount - actual_fee).
687 : */
688 : struct TALER_Amount max_fee;
689 :
690 : /**
691 : * Amount from @e root. This is the amount the merchant expects
692 : * to make, minus @e max_fee.
693 : */
694 : struct TALER_Amount brutto;
695 :
696 : /**
697 : * Index of the donau output in the list of tokens.
698 : * Set to -1 if no donau output exists.
699 : */
700 : int donau_output_index;
701 :
702 : } validate_tokens;
703 :
704 :
705 : struct
706 : {
707 : /**
708 : * Length of the @a pots and @a increments arrays.
709 : */
710 : unsigned int num_pots;
711 :
712 : /**
713 : * Serial IDs of money pots to increment.
714 : */
715 : uint64_t *pots;
716 :
717 : /**
718 : * Increment for the respective money pot.
719 : */
720 : struct TALER_Amount *increments;
721 :
722 : } compute_money_pots;
723 :
724 : /**
725 : * Results from the phase_execute_pay_transaction()
726 : */
727 : struct
728 : {
729 :
730 : /**
731 : * Considering all the coins with the "found_in_db" flag
732 : * set, what is the total amount we were so far paid on
733 : * this contract?
734 : */
735 : struct TALER_Amount total_paid;
736 :
737 : /**
738 : * Considering all the coins with the "found_in_db" flag
739 : * set, what is the total amount we had to pay in deposit
740 : * fees so far on this contract?
741 : */
742 : struct TALER_Amount total_fees_paid;
743 :
744 : /**
745 : * Considering all the coins with the "found_in_db" flag
746 : * set, what is the total amount we already refunded?
747 : */
748 : struct TALER_Amount total_refunded;
749 :
750 : /**
751 : * Number of coin deposits pending.
752 : */
753 : unsigned int pending;
754 :
755 : /**
756 : * How often have we retried the 'main' transaction?
757 : */
758 : unsigned int retry_counter;
759 :
760 : /**
761 : * Set to true if the deposit currency of a coin
762 : * does not match the contract currency.
763 : */
764 : bool deposit_currency_mismatch;
765 :
766 : /**
767 : * Set to true if the database contains a (bogus)
768 : * refund for a different currency.
769 : */
770 : bool refund_currency_mismatch;
771 :
772 : } pay_transaction;
773 :
774 : /**
775 : * Results from the phase_batch_deposits()
776 : */
777 : struct
778 : {
779 :
780 : /**
781 : * Task called when the (suspended) processing for
782 : * the /pay request times out.
783 : * Happens when we don't get a response from the exchange.
784 : */
785 : struct GNUNET_SCHEDULER_Task *timeout_task;
786 :
787 : /**
788 : * Number of batch transactions pending.
789 : */
790 : unsigned int pending_at_eg;
791 :
792 : /**
793 : * Did any exchange deny a deposit for legal reasons?
794 : */
795 : bool got_451;
796 :
797 : } batch_deposits;
798 :
799 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
800 : /**
801 : * Struct for #phase_request_donation_receipt()
802 : */
803 : struct
804 : {
805 : /**
806 : * Handler of the donau request
807 : */
808 : struct DONAU_BatchIssueReceiptHandle *birh;
809 :
810 : } donau_receipt;
811 : #endif
812 : };
813 :
814 :
815 : /**
816 : * Head of active pay context DLL.
817 : */
818 : static struct PayContext *pc_head;
819 :
820 : /**
821 : * Tail of active pay context DLL.
822 : */
823 : static struct PayContext *pc_tail;
824 :
825 :
826 : void
827 15 : TMH_force_pc_resume ()
828 : {
829 15 : for (struct PayContext *pc = pc_head;
830 15 : NULL != pc;
831 0 : pc = pc->next)
832 : {
833 0 : if (NULL != pc->batch_deposits.timeout_task)
834 : {
835 0 : GNUNET_SCHEDULER_cancel (pc->batch_deposits.timeout_task);
836 0 : pc->batch_deposits.timeout_task = NULL;
837 : }
838 0 : if (GNUNET_YES == pc->suspended)
839 : {
840 0 : pc->suspended = GNUNET_SYSERR;
841 0 : MHD_resume_connection (pc->connection);
842 : }
843 : }
844 15 : }
845 :
846 :
847 : /**
848 : * Resume payment processing.
849 : *
850 : * @param[in,out] pc payment process to resume
851 : */
852 : static void
853 37 : pay_resume (struct PayContext *pc)
854 : {
855 37 : GNUNET_assert (GNUNET_YES == pc->suspended);
856 37 : pc->suspended = GNUNET_NO;
857 37 : MHD_resume_connection (pc->connection);
858 37 : TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
859 37 : }
860 :
861 :
862 : /**
863 : * Resume the given pay context and send the given response.
864 : * Stores the response in the @a pc and signals MHD to resume
865 : * the connection. Also ensures MHD runs immediately.
866 : *
867 : * @param pc payment context
868 : * @param response_code response code to use
869 : * @param response response data to send back
870 : */
871 : static void
872 6 : resume_pay_with_response (struct PayContext *pc,
873 : unsigned int response_code,
874 : struct MHD_Response *response)
875 : {
876 6 : pc->response_code = response_code;
877 6 : pc->response = response;
878 6 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
879 : "Resuming /pay handling. HTTP status for our reply is %u.\n",
880 : response_code);
881 12 : for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
882 : {
883 6 : struct ExchangeGroup *eg = pc->parse_pay.egs[i];
884 :
885 6 : if (NULL != eg->fo)
886 : {
887 0 : TMH_EXCHANGES_keys4exchange_cancel (eg->fo);
888 0 : eg->fo = NULL;
889 0 : pc->batch_deposits.pending_at_eg--;
890 : }
891 6 : if (NULL != eg->bdh)
892 : {
893 0 : TALER_EXCHANGE_batch_deposit_cancel (eg->bdh);
894 0 : eg->bdh = NULL;
895 0 : pc->batch_deposits.pending_at_eg--;
896 : }
897 : }
898 6 : GNUNET_assert (0 == pc->batch_deposits.pending_at_eg);
899 6 : if (NULL != pc->batch_deposits.timeout_task)
900 : {
901 6 : GNUNET_SCHEDULER_cancel (pc->batch_deposits.timeout_task);
902 6 : pc->batch_deposits.timeout_task = NULL;
903 : }
904 6 : pc->phase = PP_RETURN_RESPONSE;
905 6 : pay_resume (pc);
906 6 : }
907 :
908 :
909 : /**
910 : * Resume payment processing with an error.
911 : *
912 : * @param pc operation to resume
913 : * @param ec taler error code to return
914 : * @param msg human readable error message
915 : */
916 : static void
917 0 : resume_pay_with_error (struct PayContext *pc,
918 : enum TALER_ErrorCode ec,
919 : const char *msg)
920 : {
921 0 : resume_pay_with_response (
922 : pc,
923 : TALER_ErrorCode_get_http_status_safe (ec),
924 : TALER_MHD_make_error (ec,
925 : msg));
926 0 : }
927 :
928 :
929 : /**
930 : * Conclude payment processing for @a pc with the
931 : * given @a res MHD status code.
932 : *
933 : * @param[in,out] pc payment context for final state transition
934 : * @param res MHD return code to end with
935 : */
936 : static void
937 45 : pay_end (struct PayContext *pc,
938 : MHD_RESULT res)
939 : {
940 45 : pc->phase = (MHD_YES == res)
941 : ? PP_END_YES
942 45 : : PP_END_NO;
943 45 : }
944 :
945 :
946 : /**
947 : * Return response stored in @a pc.
948 : *
949 : * @param[in,out] pc payment context we are processing
950 : */
951 : static void
952 6 : phase_return_response (struct PayContext *pc)
953 : {
954 6 : GNUNET_assert (0 != pc->response_code);
955 : /* We are *done* processing the request, just queue the response (!) */
956 6 : if (UINT_MAX == pc->response_code)
957 : {
958 0 : GNUNET_break (0);
959 0 : pay_end (pc,
960 : MHD_NO); /* hard error */
961 0 : return;
962 : }
963 6 : pay_end (pc,
964 : MHD_queue_response (pc->connection,
965 : pc->response_code,
966 : pc->response));
967 : }
968 :
969 :
970 : /**
971 : * Return a response indicating failure for legal reasons.
972 : *
973 : * @param[in,out] pc payment context we are processing
974 : */
975 : static void
976 0 : phase_fail_for_legal_reasons (struct PayContext *pc)
977 : {
978 : json_t *exchanges;
979 :
980 0 : GNUNET_assert (0 == pc->pay_transaction.pending);
981 0 : GNUNET_assert (pc->batch_deposits.got_451);
982 0 : exchanges = json_array ();
983 0 : GNUNET_assert (NULL != exchanges);
984 0 : for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
985 : {
986 0 : struct ExchangeGroup *eg = pc->parse_pay.egs[i];
987 :
988 0 : GNUNET_assert (NULL == eg->fo);
989 0 : GNUNET_assert (NULL == eg->bdh);
990 0 : if (! eg->got_451)
991 0 : continue;
992 0 : GNUNET_assert (
993 : 0 ==
994 : json_array_append_new (
995 : exchanges,
996 : json_string (eg->exchange_url)));
997 : }
998 0 : pay_end (pc,
999 0 : TALER_MHD_REPLY_JSON_PACK (
1000 : pc->connection,
1001 : MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
1002 : TALER_JSON_pack_ec (
1003 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LEGALLY_REFUSED),
1004 : GNUNET_JSON_pack_array_steal ("exchange_base_urls",
1005 : exchanges)));
1006 0 : }
1007 :
1008 :
1009 : /**
1010 : * Do database transaction for a completed batch deposit.
1011 : *
1012 : * @param eg group that completed
1013 : * @param dr response from the server
1014 : * @return transaction status
1015 : */
1016 : static enum GNUNET_DB_QueryStatus
1017 31 : batch_deposit_transaction (const struct ExchangeGroup *eg,
1018 : const struct TALER_EXCHANGE_BatchDepositResult *dr)
1019 : {
1020 31 : const struct PayContext *pc = eg->pc;
1021 : enum GNUNET_DB_QueryStatus qs;
1022 : struct TALER_Amount total_without_fees;
1023 : uint64_t b_dep_serial;
1024 31 : uint32_t off = 0;
1025 :
1026 31 : GNUNET_assert (GNUNET_OK ==
1027 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
1028 : &total_without_fees));
1029 68 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1030 : {
1031 37 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1032 : struct TALER_Amount amount_without_fees;
1033 :
1034 : /* might want to group deposits by batch more explicitly ... */
1035 37 : if (0 != strcmp (eg->exchange_url,
1036 37 : dc->exchange_url))
1037 0 : continue;
1038 37 : if (dc->found_in_db)
1039 0 : continue;
1040 37 : GNUNET_assert (0 <=
1041 : TALER_amount_subtract (&amount_without_fees,
1042 : &dc->cdd.amount,
1043 : &dc->deposit_fee));
1044 37 : GNUNET_assert (0 <=
1045 : TALER_amount_add (&total_without_fees,
1046 : &total_without_fees,
1047 : &amount_without_fees));
1048 : }
1049 31 : qs = TMH_db->insert_deposit_confirmation (
1050 31 : TMH_db->cls,
1051 31 : pc->hc->instance->settings.id,
1052 : dr->details.ok.deposit_timestamp,
1053 : &pc->check_contract.h_contract_terms,
1054 31 : eg->exchange_url,
1055 31 : pc->check_contract.contract_terms->wire_deadline,
1056 : &total_without_fees,
1057 : &eg->wire_fee,
1058 31 : &pc->check_contract.wm->h_wire,
1059 31 : dr->details.ok.exchange_sig,
1060 31 : dr->details.ok.exchange_pub,
1061 : &b_dep_serial);
1062 31 : if (qs <= 0)
1063 0 : return qs; /* Entire batch already known or failure, we're done */
1064 :
1065 68 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1066 : {
1067 37 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1068 :
1069 : /* might want to group deposits by batch more explicitly ... */
1070 37 : if (0 != strcmp (eg->exchange_url,
1071 37 : dc->exchange_url))
1072 0 : continue;
1073 37 : if (dc->found_in_db)
1074 0 : continue;
1075 : /* FIXME-#9457: We might want to check if the order was fully paid concurrently
1076 : by some other wallet here, and if so, issue an auto-refund. Right now,
1077 : it is possible to over-pay if two wallets literally make a concurrent
1078 : payment, as the earlier check for 'paid' is not in the same transaction
1079 : scope as this 'insert' operation. */
1080 37 : qs = TMH_db->insert_deposit (
1081 37 : TMH_db->cls,
1082 : off++, /* might want to group deposits by batch more explicitly ... */
1083 : b_dep_serial,
1084 37 : &dc->cdd.coin_pub,
1085 37 : &dc->cdd.coin_sig,
1086 37 : &dc->cdd.amount,
1087 37 : &dc->deposit_fee,
1088 37 : &dc->refund_fee,
1089 : GNUNET_TIME_absolute_add (
1090 37 : pc->check_contract.contract_terms->wire_deadline.abs_time,
1091 : GNUNET_TIME_randomize (GNUNET_TIME_UNIT_MINUTES)));
1092 37 : if (qs < 0)
1093 0 : return qs;
1094 37 : GNUNET_break (qs > 0);
1095 : }
1096 31 : return qs;
1097 : }
1098 :
1099 :
1100 : /**
1101 : * Handle case where the batch deposit completed
1102 : * with a status of #MHD_HTTP_OK.
1103 : *
1104 : * @param eg group that completed
1105 : * @param dr response from the server
1106 : */
1107 : static void
1108 31 : handle_batch_deposit_ok (struct ExchangeGroup *eg,
1109 : const struct TALER_EXCHANGE_BatchDepositResult *dr)
1110 : {
1111 31 : struct PayContext *pc = eg->pc;
1112 31 : enum GNUNET_DB_QueryStatus qs
1113 : = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
1114 :
1115 : /* store result to DB */
1116 31 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1117 : "Storing successful payment %s (%s) at instance `%s'\n",
1118 : pc->hc->infix,
1119 : GNUNET_h2s (&pc->check_contract.h_contract_terms.hash),
1120 : pc->hc->instance->settings.id);
1121 31 : for (unsigned int r = 0; r<MAX_RETRIES; r++)
1122 : {
1123 31 : TMH_db->preflight (TMH_db->cls);
1124 31 : if (GNUNET_OK !=
1125 31 : TMH_db->start (TMH_db->cls,
1126 : "batch-deposit-insert-confirmation"))
1127 : {
1128 0 : resume_pay_with_response (
1129 : pc,
1130 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1131 0 : TALER_MHD_MAKE_JSON_PACK (
1132 : TALER_JSON_pack_ec (
1133 : TALER_EC_GENERIC_DB_START_FAILED),
1134 : TMH_pack_exchange_reply (&dr->hr)));
1135 0 : return;
1136 : }
1137 31 : qs = batch_deposit_transaction (eg,
1138 : dr);
1139 31 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
1140 : {
1141 0 : TMH_db->rollback (TMH_db->cls);
1142 0 : continue;
1143 : }
1144 31 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
1145 : {
1146 0 : GNUNET_break (0);
1147 0 : resume_pay_with_error (pc,
1148 : TALER_EC_GENERIC_DB_COMMIT_FAILED,
1149 : "batch_deposit_transaction");
1150 : }
1151 31 : qs = TMH_db->commit (TMH_db->cls);
1152 31 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
1153 : {
1154 0 : TMH_db->rollback (TMH_db->cls);
1155 0 : continue;
1156 : }
1157 31 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
1158 : {
1159 0 : GNUNET_break (0);
1160 0 : resume_pay_with_error (pc,
1161 : TALER_EC_GENERIC_DB_COMMIT_FAILED,
1162 : "insert_deposit");
1163 : }
1164 31 : break; /* DB transaction succeeded */
1165 : }
1166 31 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
1167 : {
1168 0 : resume_pay_with_error (pc,
1169 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
1170 : "insert_deposit");
1171 0 : return;
1172 : }
1173 :
1174 : /* Transaction is done, mark affected coins as complete as well. */
1175 68 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1176 : {
1177 37 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1178 :
1179 37 : if (0 != strcmp (eg->exchange_url,
1180 37 : pc->parse_pay.dc[i].exchange_url))
1181 0 : continue;
1182 37 : if (dc->found_in_db)
1183 0 : continue;
1184 37 : dc->found_in_db = true; /* well, at least NOW it'd be true ;-) */
1185 37 : pc->pay_transaction.pending--;
1186 : }
1187 : }
1188 :
1189 :
1190 : /**
1191 : * Notify taler-merchant-kyccheck that we got a KYC
1192 : * rule violation notification and should start to
1193 : * check our KYC status.
1194 : *
1195 : * @param eg exchange group we were notified for
1196 : */
1197 : static void
1198 0 : notify_kyc_required (const struct ExchangeGroup *eg)
1199 : {
1200 0 : struct GNUNET_DB_EventHeaderP es = {
1201 0 : .size = htons (sizeof (es)),
1202 0 : .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_RULE_TRIGGERED)
1203 : };
1204 : char *hws;
1205 : char *extra;
1206 :
1207 0 : hws = GNUNET_STRINGS_data_to_string_alloc (
1208 0 : &eg->pc->check_contract.contract_terms->h_wire,
1209 : sizeof (eg->pc->check_contract.contract_terms->h_wire));
1210 0 : GNUNET_asprintf (&extra,
1211 : "%s %s",
1212 : hws,
1213 0 : eg->exchange_url);
1214 0 : GNUNET_free (hws);
1215 0 : TMH_db->event_notify (TMH_db->cls,
1216 : &es,
1217 : extra,
1218 0 : strlen (extra) + 1);
1219 0 : GNUNET_free (extra);
1220 0 : }
1221 :
1222 :
1223 : /**
1224 : * Callback to handle a batch deposit permission's response.
1225 : *
1226 : * @param cls a `struct ExchangeGroup`
1227 : * @param dr HTTP response code details
1228 : */
1229 : static void
1230 37 : batch_deposit_cb (
1231 : void *cls,
1232 : const struct TALER_EXCHANGE_BatchDepositResult *dr)
1233 : {
1234 37 : struct ExchangeGroup *eg = cls;
1235 37 : struct PayContext *pc = eg->pc;
1236 :
1237 37 : eg->bdh = NULL;
1238 37 : pc->batch_deposits.pending_at_eg--;
1239 37 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1240 : "Batch deposit completed with status %u\n",
1241 : dr->hr.http_status);
1242 37 : GNUNET_assert (GNUNET_YES == pc->suspended);
1243 37 : switch (dr->hr.http_status)
1244 : {
1245 31 : case MHD_HTTP_OK:
1246 31 : handle_batch_deposit_ok (eg,
1247 : dr);
1248 31 : if (0 == pc->batch_deposits.pending_at_eg)
1249 : {
1250 31 : pc->phase = PP_COMPUTE_MONEY_POTS;
1251 31 : pay_resume (pc);
1252 : }
1253 31 : return;
1254 0 : case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
1255 0 : notify_kyc_required (eg);
1256 0 : eg->got_451 = true;
1257 0 : pc->batch_deposits.got_451 = true;
1258 : /* update pc->pay_transaction.pending */
1259 0 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1260 : {
1261 0 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1262 :
1263 0 : if (0 != strcmp (eg->exchange_url,
1264 0 : pc->parse_pay.dc[i].exchange_url))
1265 0 : continue;
1266 0 : if (dc->found_in_db)
1267 0 : continue;
1268 0 : pc->pay_transaction.pending--;
1269 : }
1270 0 : if (0 == pc->batch_deposits.pending_at_eg)
1271 : {
1272 0 : pc->phase = PP_COMPUTE_MONEY_POTS;
1273 0 : pay_resume (pc);
1274 : }
1275 0 : return;
1276 6 : default:
1277 6 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1278 : "Deposit operation failed with HTTP code %u/%d\n",
1279 : dr->hr.http_status,
1280 : (int) dr->hr.ec);
1281 : /* Transaction failed */
1282 6 : if (5 == dr->hr.http_status / 100)
1283 : {
1284 : /* internal server error at exchange */
1285 0 : resume_pay_with_response (pc,
1286 : MHD_HTTP_BAD_GATEWAY,
1287 0 : TALER_MHD_MAKE_JSON_PACK (
1288 : TALER_JSON_pack_ec (
1289 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS),
1290 : TMH_pack_exchange_reply (&dr->hr)));
1291 0 : return;
1292 : }
1293 6 : if (NULL == dr->hr.reply)
1294 : {
1295 : /* We can't do anything meaningful here, the exchange did something wrong */
1296 0 : resume_pay_with_response (
1297 : pc,
1298 : MHD_HTTP_BAD_GATEWAY,
1299 0 : TALER_MHD_MAKE_JSON_PACK (
1300 : TALER_JSON_pack_ec (
1301 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_REPLY_MALFORMED),
1302 : TMH_pack_exchange_reply (&dr->hr)));
1303 0 : return;
1304 : }
1305 :
1306 : /* Forward error, adding the "exchange_url" for which the
1307 : error was being generated */
1308 6 : if (TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS == dr->hr.ec)
1309 : {
1310 6 : resume_pay_with_response (
1311 : pc,
1312 : MHD_HTTP_CONFLICT,
1313 6 : TALER_MHD_MAKE_JSON_PACK (
1314 : TALER_JSON_pack_ec (
1315 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS),
1316 : TMH_pack_exchange_reply (&dr->hr),
1317 : GNUNET_JSON_pack_string ("exchange_url",
1318 : eg->exchange_url)));
1319 6 : return;
1320 : }
1321 0 : resume_pay_with_response (
1322 : pc,
1323 : MHD_HTTP_BAD_GATEWAY,
1324 0 : TALER_MHD_MAKE_JSON_PACK (
1325 : TALER_JSON_pack_ec (
1326 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS),
1327 : TMH_pack_exchange_reply (&dr->hr),
1328 : GNUNET_JSON_pack_string ("exchange_url",
1329 : eg->exchange_url)));
1330 0 : return;
1331 : } /* end switch */
1332 : }
1333 :
1334 :
1335 : /**
1336 : * Force re-downloading keys for @a eg.
1337 : *
1338 : * @param[in,out] eg group to re-download keys for
1339 : */
1340 : static void
1341 : force_keys (struct ExchangeGroup *eg);
1342 :
1343 :
1344 : /**
1345 : * Function called with the result of our exchange keys lookup.
1346 : *
1347 : * @param cls the `struct ExchangeGroup`
1348 : * @param keys the keys of the exchange
1349 : * @param exchange representation of the exchange
1350 : */
1351 : static void
1352 37 : process_pay_with_keys (
1353 : void *cls,
1354 : struct TALER_EXCHANGE_Keys *keys,
1355 : struct TMH_Exchange *exchange)
1356 : {
1357 37 : struct ExchangeGroup *eg = cls;
1358 37 : struct PayContext *pc = eg->pc;
1359 37 : struct TMH_HandlerContext *hc = pc->hc;
1360 : unsigned int group_size;
1361 : struct TALER_Amount max_amount;
1362 : enum TMH_ExchangeStatus es;
1363 :
1364 37 : eg->fo = NULL;
1365 37 : pc->batch_deposits.pending_at_eg--;
1366 37 : GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
1367 37 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1368 : "Processing payment with keys from exchange %s\n",
1369 : eg->exchange_url);
1370 37 : GNUNET_assert (GNUNET_YES == pc->suspended);
1371 37 : if (NULL == keys)
1372 : {
1373 0 : GNUNET_break_op (0);
1374 0 : resume_pay_with_error (
1375 : pc,
1376 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT,
1377 : NULL);
1378 0 : return;
1379 : }
1380 37 : if (! TMH_EXCHANGES_is_below_limit (keys,
1381 : TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION,
1382 37 : &eg->total))
1383 : {
1384 0 : GNUNET_break_op (0);
1385 0 : resume_pay_with_error (
1386 : pc,
1387 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_TRANSACTION_LIMIT_VIOLATION,
1388 : eg->exchange_url);
1389 0 : return;
1390 : }
1391 :
1392 37 : max_amount = eg->total;
1393 37 : es = TMH_exchange_check_debit (
1394 37 : pc->hc->instance->settings.id,
1395 : exchange,
1396 37 : pc->check_contract.wm,
1397 : &max_amount);
1398 37 : if ( (TMH_ES_OK != es) &&
1399 : (TMH_ES_RETRY_OK != es) )
1400 : {
1401 0 : if (eg->tried_force_keys ||
1402 0 : (0 == (TMH_ES_RETRY_OK & es)) )
1403 : {
1404 0 : GNUNET_break_op (0);
1405 0 : resume_pay_with_error (
1406 : pc,
1407 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED,
1408 : NULL);
1409 0 : return;
1410 : }
1411 0 : force_keys (eg);
1412 0 : return;
1413 : }
1414 37 : if (-1 ==
1415 37 : TALER_amount_cmp (&max_amount,
1416 37 : &eg->total))
1417 : {
1418 : /* max_amount < eg->total */
1419 0 : GNUNET_break_op (0);
1420 0 : resume_pay_with_error (
1421 : pc,
1422 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_TRANSACTION_LIMIT_VIOLATION,
1423 : eg->exchange_url);
1424 0 : return;
1425 : }
1426 :
1427 37 : if (GNUNET_OK !=
1428 37 : TMH_EXCHANGES_lookup_wire_fee (exchange,
1429 37 : pc->check_contract.wm->wire_method,
1430 : &eg->wire_fee))
1431 : {
1432 0 : if (eg->tried_force_keys)
1433 : {
1434 0 : GNUNET_break_op (0);
1435 0 : resume_pay_with_error (
1436 : pc,
1437 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED,
1438 0 : pc->check_contract.wm->wire_method);
1439 0 : return;
1440 : }
1441 0 : force_keys (eg);
1442 0 : return;
1443 : }
1444 37 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1445 : "Got wire data for %s\n",
1446 : eg->exchange_url);
1447 :
1448 : /* Initiate /batch-deposit operation for all coins of
1449 : the current exchange (!) */
1450 37 : group_size = 0;
1451 82 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1452 : {
1453 45 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1454 : const struct TALER_EXCHANGE_DenomPublicKey *denom_details;
1455 45 : bool is_age_restricted_denom = false;
1456 :
1457 45 : if (0 != strcmp (eg->exchange_url,
1458 45 : pc->parse_pay.dc[i].exchange_url))
1459 0 : continue;
1460 45 : if (dc->found_in_db)
1461 0 : continue;
1462 :
1463 : denom_details
1464 45 : = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
1465 45 : &dc->cdd.h_denom_pub);
1466 45 : if (NULL == denom_details)
1467 : {
1468 0 : if (eg->tried_force_keys)
1469 : {
1470 0 : GNUNET_break_op (0);
1471 0 : resume_pay_with_response (
1472 : pc,
1473 : MHD_HTTP_BAD_REQUEST,
1474 0 : TALER_MHD_MAKE_JSON_PACK (
1475 : TALER_JSON_pack_ec (
1476 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND),
1477 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1478 : &dc->cdd.h_denom_pub),
1479 : GNUNET_JSON_pack_allow_null (
1480 : GNUNET_JSON_pack_object_steal (
1481 : "exchange_keys",
1482 : TALER_EXCHANGE_keys_to_json (keys)))));
1483 0 : return;
1484 : }
1485 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1486 : "Missing denomination %s from exchange %s, updating keys\n",
1487 : GNUNET_h2s (&dc->cdd.h_denom_pub.hash),
1488 : eg->exchange_url);
1489 0 : force_keys (eg);
1490 0 : return;
1491 : }
1492 45 : dc->deposit_fee = denom_details->fees.deposit;
1493 45 : dc->refund_fee = denom_details->fees.refund;
1494 :
1495 45 : if (GNUNET_TIME_absolute_is_past (
1496 : denom_details->expire_deposit.abs_time))
1497 : {
1498 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1499 : "Denomination key offered by client has expired for deposits\n");
1500 0 : resume_pay_with_response (
1501 : pc,
1502 : MHD_HTTP_GONE,
1503 0 : TALER_MHD_MAKE_JSON_PACK (
1504 : TALER_JSON_pack_ec (
1505 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_DEPOSIT_EXPIRED),
1506 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1507 : &denom_details->h_key)));
1508 0 : return;
1509 : }
1510 :
1511 : /* Now that we have the details about the denomination, we can verify age
1512 : * restriction requirements, if applicable. Note that denominations with an
1513 : * age_mask equal to zero always pass the age verification. */
1514 45 : is_age_restricted_denom = (0 != denom_details->key.age_mask.bits);
1515 :
1516 45 : if (is_age_restricted_denom &&
1517 0 : (0 < pc->check_contract.contract_terms->minimum_age))
1518 0 : {
1519 : /* Minimum age given and restricted coin provided: We need to verify the
1520 : * minimum age */
1521 0 : unsigned int code = 0;
1522 :
1523 0 : if (dc->no_age_commitment)
1524 : {
1525 0 : GNUNET_break_op (0);
1526 0 : code = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_MISSING;
1527 0 : goto AGE_FAIL;
1528 : }
1529 0 : dc->age_commitment.mask = denom_details->key.age_mask;
1530 0 : if (((int) (dc->age_commitment.num + 1)) !=
1531 0 : __builtin_popcount (dc->age_commitment.mask.bits))
1532 : {
1533 0 : GNUNET_break_op (0);
1534 0 : code =
1535 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_SIZE_MISMATCH;
1536 0 : goto AGE_FAIL;
1537 : }
1538 0 : if (GNUNET_OK !=
1539 0 : TALER_age_commitment_verify (
1540 0 : &dc->age_commitment,
1541 0 : pc->check_contract.contract_terms->minimum_age,
1542 0 : &dc->minimum_age_sig))
1543 0 : code = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_VERIFICATION_FAILED;
1544 0 : AGE_FAIL:
1545 0 : if (0 < code)
1546 : {
1547 0 : GNUNET_break_op (0);
1548 0 : TALER_age_commitment_free (&dc->age_commitment);
1549 0 : resume_pay_with_response (
1550 : pc,
1551 : MHD_HTTP_BAD_REQUEST,
1552 0 : TALER_MHD_MAKE_JSON_PACK (
1553 : TALER_JSON_pack_ec (code),
1554 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1555 : &denom_details->h_key)));
1556 0 : return;
1557 : }
1558 :
1559 : /* Age restriction successfully verified!
1560 : * Calculate the hash of the age commitment. */
1561 0 : TALER_age_commitment_hash (&dc->age_commitment,
1562 : &dc->cdd.h_age_commitment);
1563 0 : TALER_age_commitment_free (&dc->age_commitment);
1564 : }
1565 45 : else if (is_age_restricted_denom &&
1566 0 : dc->no_h_age_commitment)
1567 : {
1568 : /* The contract did not ask for a minimum_age but the client paid
1569 : * with a coin that has age restriction enabled. We lack the hash
1570 : * of the age commitment in this case in order to verify the coin
1571 : * and to deposit it with the exchange. */
1572 0 : GNUNET_break_op (0);
1573 0 : resume_pay_with_response (
1574 : pc,
1575 : MHD_HTTP_BAD_REQUEST,
1576 0 : TALER_MHD_MAKE_JSON_PACK (
1577 : TALER_JSON_pack_ec (
1578 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_HASH_MISSING),
1579 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1580 : &denom_details->h_key)));
1581 0 : return;
1582 : }
1583 45 : group_size++;
1584 : }
1585 :
1586 37 : if (0 == group_size)
1587 : {
1588 0 : GNUNET_break (0);
1589 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1590 : "Group size zero, %u batch transactions remain pending\n",
1591 : pc->batch_deposits.pending_at_eg);
1592 0 : if (0 == pc->batch_deposits.pending_at_eg)
1593 : {
1594 0 : pc->phase = PP_COMPUTE_MONEY_POTS;
1595 0 : pay_resume (pc);
1596 0 : return;
1597 : }
1598 0 : return;
1599 : }
1600 37 : if (group_size > TALER_MAX_COINS)
1601 0 : group_size = TALER_MAX_COINS;
1602 37 : {
1603 37 : struct TALER_EXCHANGE_CoinDepositDetail cdds[group_size];
1604 37 : struct TALER_EXCHANGE_DepositContractDetail dcd = {
1605 37 : .wire_deadline = pc->check_contract.contract_terms->wire_deadline,
1606 37 : .merchant_payto_uri = pc->check_contract.wm->payto_uri,
1607 37 : .wire_salt = pc->check_contract.wm->wire_salt,
1608 : .h_contract_terms = pc->check_contract.h_contract_terms,
1609 : .wallet_data_hash = pc->parse_wallet_data.h_wallet_data,
1610 37 : .wallet_timestamp = pc->check_contract.contract_terms->timestamp,
1611 37 : .merchant_pub = hc->instance->merchant_pub,
1612 37 : .refund_deadline = pc->check_contract.contract_terms->refund_deadline
1613 : };
1614 : enum TALER_ErrorCode ec;
1615 37 : size_t off = 0;
1616 :
1617 37 : TALER_merchant_contract_sign (&pc->check_contract.h_contract_terms,
1618 37 : &pc->hc->instance->merchant_priv,
1619 : &dcd.merchant_sig);
1620 45 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1621 : {
1622 45 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1623 :
1624 45 : if (dc->found_in_db)
1625 0 : continue;
1626 45 : if (0 != strcmp (dc->exchange_url,
1627 : eg->exchange_url))
1628 0 : continue;
1629 45 : cdds[off++] = dc->cdd;
1630 45 : if (off >= group_size)
1631 37 : break;
1632 : }
1633 37 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1634 : "Initiating batch deposit with %u coins\n",
1635 : group_size);
1636 : /* Note: the coin signatures over the wallet_data_hash are
1637 : checked inside of this call */
1638 37 : eg->bdh = TALER_EXCHANGE_batch_deposit (
1639 : TMH_curl_ctx,
1640 : eg->exchange_url,
1641 : keys,
1642 : &dcd,
1643 : group_size,
1644 : cdds,
1645 : &batch_deposit_cb,
1646 : eg,
1647 : &ec);
1648 37 : if (NULL == eg->bdh)
1649 : {
1650 : /* Signature was invalid or some other constraint was not satisfied. If
1651 : the exchange was unavailable, we'd get that information in the
1652 : callback. */
1653 0 : GNUNET_break_op (0);
1654 0 : resume_pay_with_response (
1655 : pc,
1656 : TALER_ErrorCode_get_http_status_safe (ec),
1657 0 : TALER_MHD_MAKE_JSON_PACK (
1658 : TALER_JSON_pack_ec (ec),
1659 : GNUNET_JSON_pack_string ("exchange_url",
1660 : eg->exchange_url)));
1661 0 : return;
1662 : }
1663 37 : pc->batch_deposits.pending_at_eg++;
1664 37 : if (TMH_force_audit)
1665 8 : TALER_EXCHANGE_batch_deposit_force_dc (eg->bdh);
1666 : }
1667 : }
1668 :
1669 :
1670 : static void
1671 0 : force_keys (struct ExchangeGroup *eg)
1672 : {
1673 0 : struct PayContext *pc = eg->pc;
1674 :
1675 0 : eg->tried_force_keys = true;
1676 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1677 : "Forcing /keys download (once)\n");
1678 0 : eg->fo = TMH_EXCHANGES_keys4exchange (
1679 : eg->exchange_url,
1680 : true,
1681 : &process_pay_with_keys,
1682 : eg);
1683 0 : if (NULL == eg->fo)
1684 : {
1685 0 : GNUNET_break_op (0);
1686 0 : resume_pay_with_error (pc,
1687 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNTRUSTED,
1688 : eg->exchange_url);
1689 0 : return;
1690 : }
1691 0 : pc->batch_deposits.pending_at_eg++;
1692 : }
1693 :
1694 :
1695 : /**
1696 : * Handle a timeout for the processing of the pay request.
1697 : *
1698 : * @param cls our `struct PayContext`
1699 : */
1700 : static void
1701 0 : handle_pay_timeout (void *cls)
1702 : {
1703 0 : struct PayContext *pc = cls;
1704 :
1705 0 : pc->batch_deposits.timeout_task = NULL;
1706 0 : GNUNET_assert (GNUNET_YES == pc->suspended);
1707 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1708 : "Resuming pay with error after timeout\n");
1709 0 : resume_pay_with_error (pc,
1710 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT,
1711 : NULL);
1712 0 : }
1713 :
1714 :
1715 : /**
1716 : * Compute the timeout for a /pay request based on the number of coins
1717 : * involved.
1718 : *
1719 : * @param num_coins number of coins
1720 : * @returns timeout for the /pay request
1721 : */
1722 : static struct GNUNET_TIME_Relative
1723 37 : get_pay_timeout (unsigned int num_coins)
1724 : {
1725 : struct GNUNET_TIME_Relative t;
1726 :
1727 : /* FIXME-Performance-Optimization: Do some benchmarking to come up with a
1728 : * better timeout. We've increased this value so the wallet integration
1729 : * test passes again on my (Florian) machine.
1730 : */
1731 37 : t = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
1732 37 : 15 * (1 + (num_coins / 5)));
1733 :
1734 37 : return t;
1735 : }
1736 :
1737 :
1738 : /**
1739 : * Start batch deposits for all exchanges involved
1740 : * in this payment.
1741 : *
1742 : * @param[in,out] pc payment context we are processing
1743 : */
1744 : static void
1745 37 : phase_batch_deposits (struct PayContext *pc)
1746 : {
1747 74 : for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
1748 : {
1749 37 : struct ExchangeGroup *eg = pc->parse_pay.egs[i];
1750 37 : bool have_coins = false;
1751 :
1752 37 : for (size_t j = 0; j<pc->parse_pay.coins_cnt; j++)
1753 : {
1754 37 : struct DepositConfirmation *dc = &pc->parse_pay.dc[j];
1755 :
1756 37 : if (0 != strcmp (eg->exchange_url,
1757 37 : dc->exchange_url))
1758 0 : continue;
1759 37 : if (dc->found_in_db)
1760 0 : continue;
1761 37 : have_coins = true;
1762 37 : break;
1763 : }
1764 37 : if (! have_coins)
1765 0 : continue; /* no coins left to deposit at this exchange */
1766 37 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1767 : "Getting /keys for %s\n",
1768 : eg->exchange_url);
1769 37 : eg->fo = TMH_EXCHANGES_keys4exchange (
1770 : eg->exchange_url,
1771 : false,
1772 : &process_pay_with_keys,
1773 : eg);
1774 37 : if (NULL == eg->fo)
1775 : {
1776 0 : GNUNET_break_op (0);
1777 0 : pay_end (pc,
1778 : TALER_MHD_reply_with_error (
1779 : pc->connection,
1780 : MHD_HTTP_BAD_REQUEST,
1781 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNTRUSTED,
1782 : eg->exchange_url));
1783 0 : return;
1784 : }
1785 37 : pc->batch_deposits.pending_at_eg++;
1786 : }
1787 37 : if (0 == pc->batch_deposits.pending_at_eg)
1788 : {
1789 0 : pc->phase = PP_COMPUTE_MONEY_POTS;
1790 0 : pay_resume (pc);
1791 0 : return;
1792 : }
1793 : /* Suspend while we interact with the exchange */
1794 37 : MHD_suspend_connection (pc->connection);
1795 37 : pc->suspended = GNUNET_YES;
1796 37 : GNUNET_assert (NULL == pc->batch_deposits.timeout_task);
1797 : pc->batch_deposits.timeout_task
1798 37 : = GNUNET_SCHEDULER_add_delayed (get_pay_timeout (pc->parse_pay.coins_cnt),
1799 : &handle_pay_timeout,
1800 : pc);
1801 : }
1802 :
1803 :
1804 : /**
1805 : * Build JSON array of blindly signed token envelopes,
1806 : * to be used in the response to the wallet.
1807 : *
1808 : * @param[in,out] pc payment context to use
1809 : */
1810 : static json_t *
1811 33 : build_token_sigs (struct PayContext *pc)
1812 : {
1813 : json_t *token_sigs;
1814 :
1815 33 : if (0 == pc->output_tokens_len)
1816 29 : return NULL;
1817 4 : token_sigs = json_array ();
1818 4 : GNUNET_assert (NULL != token_sigs);
1819 8 : for (unsigned int i = 0; i < pc->output_tokens_len; i++)
1820 : {
1821 4 : GNUNET_assert (0 ==
1822 : json_array_append_new (
1823 : token_sigs,
1824 : GNUNET_JSON_PACK (
1825 : GNUNET_JSON_pack_blinded_sig (
1826 : "blind_sig",
1827 : pc->output_tokens[i].sig.signature)
1828 : )));
1829 : }
1830 4 : return token_sigs;
1831 : }
1832 :
1833 :
1834 : /**
1835 : * Generate response (payment successful)
1836 : *
1837 : * @param[in,out] pc payment context where the payment was successful
1838 : */
1839 : static void
1840 33 : phase_success_response (struct PayContext *pc)
1841 : {
1842 : struct TALER_MerchantSignatureP sig;
1843 : char *pos_confirmation;
1844 :
1845 : /* Sign on our end (as the payment did go through, even if it may
1846 : have been refunded already) */
1847 33 : TALER_merchant_pay_sign (&pc->check_contract.h_contract_terms,
1848 33 : &pc->hc->instance->merchant_priv,
1849 : &sig);
1850 : /* Build the response */
1851 66 : pos_confirmation = (NULL == pc->check_contract.pos_key)
1852 : ? NULL
1853 33 : : TALER_build_pos_confirmation (pc->check_contract.pos_key,
1854 : pc->check_contract.pos_alg,
1855 2 : &pc->validate_tokens.brutto,
1856 2 : pc->check_contract.contract_terms->timestamp
1857 : );
1858 33 : pay_end (pc,
1859 33 : TALER_MHD_REPLY_JSON_PACK (
1860 : pc->connection,
1861 : MHD_HTTP_OK,
1862 : GNUNET_JSON_pack_allow_null (
1863 : GNUNET_JSON_pack_string ("pos_confirmation",
1864 : pos_confirmation)),
1865 : GNUNET_JSON_pack_allow_null (
1866 : GNUNET_JSON_pack_array_steal ("token_sigs",
1867 : build_token_sigs (pc))),
1868 : GNUNET_JSON_pack_data_auto ("sig",
1869 : &sig)));
1870 33 : GNUNET_free (pos_confirmation);
1871 33 : }
1872 :
1873 :
1874 : /**
1875 : * Use database to notify other clients about the
1876 : * payment being completed.
1877 : *
1878 : * @param[in,out] pc context to trigger notification for
1879 : */
1880 : static void
1881 31 : phase_payment_notification (struct PayContext *pc)
1882 : {
1883 : {
1884 31 : struct TMH_OrderPayEventP pay_eh = {
1885 31 : .header.size = htons (sizeof (pay_eh)),
1886 31 : .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_PAID),
1887 31 : .merchant_pub = pc->hc->instance->merchant_pub
1888 : };
1889 :
1890 31 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1891 : "Notifying clients about payment of order %s\n",
1892 : pc->order_id);
1893 31 : GNUNET_CRYPTO_hash (pc->order_id,
1894 : strlen (pc->order_id),
1895 : &pay_eh.h_order_id);
1896 31 : TMH_db->event_notify (TMH_db->cls,
1897 : &pay_eh.header,
1898 : NULL,
1899 : 0);
1900 : }
1901 31 : if ( (NULL != pc->parse_pay.session_id) &&
1902 31 : (NULL != pc->check_contract.contract_terms->fulfillment_url) )
1903 : {
1904 19 : struct TMH_SessionEventP session_eh = {
1905 19 : .header.size = htons (sizeof (session_eh)),
1906 19 : .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED),
1907 19 : .merchant_pub = pc->hc->instance->merchant_pub
1908 : };
1909 :
1910 19 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1911 : "Notifying clients about session change to %s for %s\n",
1912 : pc->parse_pay.session_id,
1913 : pc->check_contract.contract_terms->fulfillment_url);
1914 19 : GNUNET_CRYPTO_hash (pc->parse_pay.session_id,
1915 19 : strlen (pc->parse_pay.session_id),
1916 : &session_eh.h_session_id);
1917 19 : GNUNET_CRYPTO_hash (pc->check_contract.contract_terms->fulfillment_url,
1918 19 : strlen (pc->check_contract.contract_terms->
1919 : fulfillment_url),
1920 : &session_eh.h_fulfillment_url);
1921 19 : TMH_db->event_notify (TMH_db->cls,
1922 : &session_eh.header,
1923 : NULL,
1924 : 0);
1925 : }
1926 31 : pc->phase = PP_SUCCESS_RESPONSE;
1927 31 : }
1928 :
1929 :
1930 : /**
1931 : * Phase to write all outputs to our database so we do
1932 : * not re-request them in case the client re-plays the
1933 : * request.
1934 : *
1935 : * @param[in,out] pc payment context
1936 : */
1937 : static void
1938 31 : phase_final_output_token_processing (struct PayContext *pc)
1939 : {
1940 31 : if (0 == pc->output_tokens_len)
1941 : {
1942 27 : pc->phase++;
1943 27 : return;
1944 : }
1945 4 : for (unsigned int retry = 0; retry < MAX_RETRIES; retry++)
1946 : {
1947 : enum GNUNET_DB_QueryStatus qs;
1948 :
1949 4 : TMH_db->preflight (TMH_db->cls);
1950 4 : if (GNUNET_OK !=
1951 4 : TMH_db->start (TMH_db->cls,
1952 : "insert_order_blinded_sigs"))
1953 : {
1954 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1955 : "start insert_order_blinded_sigs_failed");
1956 0 : pc->phase++;
1957 0 : return;
1958 : }
1959 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
1960 : if (pc->parse_wallet_data.num_bkps > 0)
1961 : {
1962 : qs = TMH_db->update_donau_instance_receipts_amount (
1963 : TMH_db->cls,
1964 : &pc->parse_wallet_data.donau_instance_serial,
1965 : &pc->parse_wallet_data.charity_receipts_to_date);
1966 : switch (qs)
1967 : {
1968 : case GNUNET_DB_STATUS_HARD_ERROR:
1969 : TMH_db->rollback (TMH_db->cls);
1970 : GNUNET_break (0);
1971 : return;
1972 : case GNUNET_DB_STATUS_SOFT_ERROR:
1973 : TMH_db->rollback (TMH_db->cls);
1974 : continue;
1975 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
1976 : /* weird for an update */
1977 : GNUNET_break (0);
1978 : break;
1979 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
1980 : break;
1981 : }
1982 : }
1983 : #endif
1984 4 : for (unsigned int i = 0;
1985 8 : i < pc->output_tokens_len;
1986 4 : i++)
1987 : {
1988 4 : qs = TMH_db->insert_order_blinded_sigs (
1989 4 : TMH_db->cls,
1990 : pc->order_id,
1991 : i,
1992 4 : &pc->output_tokens[i].h_issue.hash,
1993 4 : pc->output_tokens[i].sig.signature);
1994 :
1995 4 : switch (qs)
1996 : {
1997 0 : case GNUNET_DB_STATUS_HARD_ERROR:
1998 0 : TMH_db->rollback (TMH_db->cls);
1999 0 : pc->phase++;
2000 0 : return;
2001 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
2002 0 : TMH_db->rollback (TMH_db->cls);
2003 0 : goto OUTER;
2004 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
2005 : /* weird for an update */
2006 0 : GNUNET_break (0);
2007 0 : break;
2008 4 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
2009 4 : break;
2010 : }
2011 : } /* for i */
2012 4 : qs = TMH_db->commit (TMH_db->cls);
2013 4 : switch (qs)
2014 : {
2015 0 : case GNUNET_DB_STATUS_HARD_ERROR:
2016 0 : TMH_db->rollback (TMH_db->cls);
2017 0 : pc->phase++;
2018 0 : return;
2019 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
2020 0 : TMH_db->rollback (TMH_db->cls);
2021 0 : continue;
2022 4 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
2023 4 : pc->phase++;
2024 4 : return; /* success */
2025 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
2026 0 : pc->phase++;
2027 0 : return; /* success */
2028 : }
2029 0 : GNUNET_break (0);
2030 0 : pc->phase++;
2031 0 : return; /* strange */
2032 0 : OUTER:
2033 : } /* for retry */
2034 0 : TMH_db->rollback (TMH_db->cls);
2035 0 : pc->phase++;
2036 : /* We continue anyway, as there is not much we can
2037 : do here: the Donau *did* issue us the receipts;
2038 : also, we'll eventually ask the Donau for the
2039 : balance and get the correct one. Plus, we were
2040 : paid by the client, so it's technically all still
2041 : OK. If the request fails anyway, the wallet will
2042 : most likely replay the request and then hopefully
2043 : we will succeed the next time */
2044 : }
2045 :
2046 :
2047 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
2048 :
2049 : /**
2050 : * Add donation receipt outputs to the output_tokens.
2051 : *
2052 : * Note that under the current (odd, bad) libdonau
2053 : * API *we* are responsible for freeing blinded_sigs,
2054 : * so we truly own that array!
2055 : *
2056 : * @param[in,out] pc payment context
2057 : * @param num_blinded_sigs number of signatures received
2058 : * @param blinded_sigs blinded signatures from Donau
2059 : * @return #GNUNET_OK on success,
2060 : * #GNUNET_SYSERR on failure (state machine was
2061 : * in that case already advanced)
2062 : */
2063 : static enum GNUNET_GenericReturnValue
2064 : add_donation_receipt_outputs (
2065 : struct PayContext *pc,
2066 : size_t num_blinded_sigs,
2067 : struct DONAU_BlindedDonationUnitSignature *blinded_sigs)
2068 : {
2069 : int donau_output_index = pc->validate_tokens.donau_output_index;
2070 :
2071 : GNUNET_assert (pc->parse_wallet_data.num_bkps ==
2072 : num_blinded_sigs);
2073 :
2074 : GNUNET_assert (donau_output_index >= 0);
2075 :
2076 : for (unsigned int i = 0; i<pc->output_tokens_len; i++)
2077 : {
2078 : struct SignedOutputToken *sot
2079 : = &pc->output_tokens[i];
2080 :
2081 : /* Only look at actual donau tokens. */
2082 : if (sot->output_index != donau_output_index)
2083 : continue;
2084 :
2085 : sot->sig.signature = GNUNET_CRYPTO_blind_sig_incref (blinded_sigs[i].
2086 : blinded_sig);
2087 : sot->h_issue.hash = pc->parse_wallet_data.bkps[i].h_donation_unit_pub.hash;
2088 : }
2089 : return GNUNET_OK;
2090 : }
2091 :
2092 :
2093 : /**
2094 : * Callback to handle the result of a batch issue request.
2095 : *
2096 : * @param cls our `struct PayContext`
2097 : * @param resp the response from Donau
2098 : */
2099 : static void
2100 : merchant_donau_issue_receipt_cb (
2101 : void *cls,
2102 : const struct DONAU_BatchIssueResponse *resp)
2103 : {
2104 : struct PayContext *pc = cls;
2105 : /* Donau replies asynchronously, so we expect the PayContext
2106 : * to be suspended. */
2107 : GNUNET_assert (GNUNET_YES == pc->suspended);
2108 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2109 : "Donau responded with status=%u, ec=%u",
2110 : resp->hr.http_status,
2111 : resp->hr.ec);
2112 : switch (resp->hr.http_status)
2113 : {
2114 : case 0:
2115 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2116 : "Donau batch issue request from merchant-httpd failed (http_status==0)");
2117 : resume_pay_with_error (pc,
2118 : TALER_EC_MERCHANT_GENERIC_DONAU_INVALID_RESPONSE,
2119 : resp->hr.hint);
2120 : return;
2121 : case MHD_HTTP_OK:
2122 : case MHD_HTTP_CREATED:
2123 : if (TALER_EC_NONE != resp->hr.ec)
2124 : {
2125 : /* Most probably, it is just some small flaw from
2126 : * donau so no point in failing, yet we have to display it */
2127 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2128 : "Donau signalled error %u despite HTTP %u",
2129 : resp->hr.ec,
2130 : resp->hr.http_status);
2131 : }
2132 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2133 : "Donau accepted donation receipts with total_issued=%s",
2134 : TALER_amount2s (&resp->details.ok.issued_amount));
2135 : if (GNUNET_OK !=
2136 : add_donation_receipt_outputs (pc,
2137 : resp->details.ok.num_blinded_sigs,
2138 : resp->details.ok.blinded_sigs))
2139 : return; /* state machine was already advanced */
2140 : pc->phase = PP_FINAL_OUTPUT_TOKEN_PROCESSING;
2141 : pay_resume (pc);
2142 : return;
2143 :
2144 : case MHD_HTTP_BAD_REQUEST:
2145 : case MHD_HTTP_FORBIDDEN:
2146 : case MHD_HTTP_NOT_FOUND:
2147 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
2148 : default: /* make sure that everything except 200/201 will end up here*/
2149 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2150 : "Donau replied with HTTP %u (ec=%u)",
2151 : resp->hr.http_status,
2152 : resp->hr.ec);
2153 : resume_pay_with_error (pc,
2154 : TALER_EC_MERCHANT_GENERIC_DONAU_INVALID_RESPONSE,
2155 : resp->hr.hint);
2156 : return;
2157 : }
2158 : }
2159 :
2160 :
2161 : /**
2162 : * Parse a bkp encoded in JSON.
2163 : *
2164 : * @param[out] bkp where to return the result
2165 : * @param bkp_key_obj json to parse
2166 : * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if @a bkp_key_obj
2167 : * is malformed.
2168 : */
2169 : static enum GNUNET_GenericReturnValue
2170 : merchant_parse_json_bkp (struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkp,
2171 : const json_t *bkp_key_obj)
2172 : {
2173 : struct GNUNET_JSON_Specification spec[] = {
2174 : GNUNET_JSON_spec_fixed_auto ("h_donation_unit_pub",
2175 : &bkp->h_donation_unit_pub),
2176 : DONAU_JSON_spec_blinded_donation_identifier ("blinded_udi",
2177 : &bkp->blinded_udi),
2178 : GNUNET_JSON_spec_end ()
2179 : };
2180 :
2181 : if (GNUNET_OK !=
2182 : GNUNET_JSON_parse (bkp_key_obj,
2183 : spec,
2184 : NULL,
2185 : NULL))
2186 : {
2187 : GNUNET_break_op (0);
2188 : return GNUNET_SYSERR;
2189 : }
2190 : return GNUNET_OK;
2191 : }
2192 :
2193 :
2194 : /**
2195 : * Generate a donation signature for the bkp and charity.
2196 : *
2197 : * @param[in,out] pc payment context containing the charity and bkps
2198 : */
2199 : static void
2200 : phase_request_donation_receipt (struct PayContext *pc)
2201 : {
2202 : if ( (NULL == pc->parse_wallet_data.donau.donau_url) ||
2203 : (0 == pc->parse_wallet_data.num_bkps) )
2204 : {
2205 : pc->phase++;
2206 : return;
2207 : }
2208 : pc->donau_receipt.birh =
2209 : DONAU_charity_issue_receipt (
2210 : TMH_curl_ctx,
2211 : pc->parse_wallet_data.donau.donau_url,
2212 : &pc->parse_wallet_data.charity_priv,
2213 : pc->parse_wallet_data.charity_id,
2214 : pc->parse_wallet_data.donau.donation_year,
2215 : pc->parse_wallet_data.num_bkps,
2216 : pc->parse_wallet_data.bkps,
2217 : &merchant_donau_issue_receipt_cb,
2218 : pc);
2219 : if (NULL == pc->donau_receipt.birh)
2220 : {
2221 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2222 : "Failed to create Donau receipt request");
2223 : pay_end (pc,
2224 : TALER_MHD_reply_with_error (pc->connection,
2225 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2226 : TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
2227 : "Donau request creation error"));
2228 : return;
2229 : }
2230 : MHD_suspend_connection (pc->connection);
2231 : pc->suspended = GNUNET_YES;
2232 : }
2233 :
2234 :
2235 : #endif
2236 :
2237 :
2238 : /**
2239 : * Increment the money pot @a pot_id in @a pc by @a increment.
2240 : *
2241 : * @param[in,out] pc context to update
2242 : * @param pot_id money pot to increment
2243 : * @param increment amount to add
2244 : */
2245 : static void
2246 0 : increment_pot (struct PayContext *pc,
2247 : uint64_t pot_id,
2248 : const struct TALER_Amount *increment)
2249 : {
2250 0 : for (unsigned int i = 0; i<pc->compute_money_pots.num_pots; i++)
2251 : {
2252 0 : if (pot_id == pc->compute_money_pots.pots[i])
2253 : {
2254 : struct TALER_Amount *p;
2255 :
2256 0 : p = &pc->compute_money_pots.increments[i];
2257 0 : GNUNET_assert (0 <=
2258 : TALER_amount_add (p,
2259 : p,
2260 : increment));
2261 0 : return;
2262 : }
2263 : }
2264 0 : GNUNET_array_append (pc->compute_money_pots.pots,
2265 : pc->compute_money_pots.num_pots,
2266 : pot_id);
2267 0 : pc->compute_money_pots.num_pots--; /* do not increment twice... */
2268 0 : GNUNET_array_append (pc->compute_money_pots.increments,
2269 : pc->compute_money_pots.num_pots,
2270 : *increment);
2271 : }
2272 :
2273 :
2274 : /**
2275 : * Compute the total changes to money pots in preparation
2276 : * for the #PP_PAY_TRANSACTION phase.
2277 : *
2278 : * @param[in,out] pc payment context to transact
2279 : */
2280 : static void
2281 72 : phase_compute_money_pots (struct PayContext *pc)
2282 : {
2283 72 : const struct TALER_MERCHANT_Contract *contract
2284 : = pc->check_contract.contract_terms;
2285 : struct TALER_Amount assigned;
2286 :
2287 72 : if (0 == pc->parse_pay.coins_cnt)
2288 : {
2289 : /* Did not pay with any coins, so no currency/amount involved,
2290 : hence no money pot update possible. */
2291 4 : pc->phase++;
2292 4 : return;
2293 : }
2294 :
2295 68 : TALER_amount_set_zero (pc->parse_pay.dc[0].cdd.amount.currency,
2296 : &assigned);
2297 68 : GNUNET_assert (NULL != contract);
2298 76 : for (size_t i = 0; i<contract->products_len; i++)
2299 : {
2300 8 : const struct TALER_MERCHANT_ProductSold *product
2301 8 : = &contract->products[i];
2302 8 : const struct TALER_Amount *price = NULL;
2303 :
2304 : /* find price in the right currency */
2305 8 : for (unsigned int j = 0; j<product->prices_length; j++)
2306 : {
2307 0 : if (GNUNET_OK ==
2308 0 : TALER_amount_cmp_currency (&assigned,
2309 0 : &product->prices[j]))
2310 : {
2311 0 : price = &product->prices[j];
2312 0 : break;
2313 : }
2314 : }
2315 8 : if (NULL == price)
2316 : {
2317 8 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2318 : "Product `%s' has no price given in `%s'.\n",
2319 : product->product_id,
2320 : assigned.currency);
2321 8 : continue;
2322 : }
2323 0 : if (0 != product->product_money_pot)
2324 : {
2325 0 : GNUNET_assert (0 <=
2326 : TALER_amount_add (&assigned,
2327 : &assigned,
2328 : price));
2329 0 : increment_pot (pc,
2330 0 : product->product_money_pot,
2331 : price);
2332 : }
2333 : }
2334 :
2335 : {
2336 : /* Compute what is left from the order total and account for that.
2337 : Also sanity-check and handle the case where the overall order
2338 : is below that of the sum of the products. */
2339 : struct TALER_Amount left;
2340 :
2341 68 : if (0 >
2342 68 : TALER_amount_subtract (&left,
2343 68 : &pc->validate_tokens.brutto,
2344 : &assigned))
2345 : {
2346 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2347 : "Total order brutto amount below sum from products, skipping per-product money pots\n");
2348 0 : GNUNET_free (pc->compute_money_pots.pots);
2349 0 : GNUNET_free (pc->compute_money_pots.increments);
2350 0 : pc->compute_money_pots.num_pots = 0;
2351 0 : left = pc->validate_tokens.brutto;
2352 : }
2353 :
2354 68 : if ( (! TALER_amount_is_zero (&left)) &&
2355 68 : (0 != contract->default_money_pot) )
2356 0 : increment_pot (pc,
2357 0 : contract->default_money_pot,
2358 : &left);
2359 : }
2360 68 : pc->phase++;
2361 : }
2362 :
2363 :
2364 : /**
2365 : * Function called with information about a coin that was deposited.
2366 : *
2367 : * @param cls closure
2368 : * @param exchange_url exchange where @a coin_pub was deposited
2369 : * @param coin_pub public key of the coin
2370 : * @param amount_with_fee amount the exchange will deposit for this coin
2371 : * @param deposit_fee fee the exchange will charge for this coin
2372 : * @param refund_fee fee the exchange will charge for refunding this coin
2373 : */
2374 : static void
2375 39 : check_coin_paid (void *cls,
2376 : const char *exchange_url,
2377 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
2378 : const struct TALER_Amount *amount_with_fee,
2379 : const struct TALER_Amount *deposit_fee,
2380 : const struct TALER_Amount *refund_fee)
2381 : {
2382 39 : struct PayContext *pc = cls;
2383 :
2384 90 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
2385 : {
2386 51 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
2387 :
2388 51 : if (dc->found_in_db)
2389 6 : continue; /* processed earlier, skip "expensive" memcmp() */
2390 : /* Get matching coin from results*/
2391 45 : if ( (0 != GNUNET_memcmp (coin_pub,
2392 37 : &dc->cdd.coin_pub)) ||
2393 : (0 !=
2394 37 : strcmp (exchange_url,
2395 74 : dc->exchange_url)) ||
2396 : (GNUNET_OK !=
2397 37 : TALER_amount_cmp_currency (amount_with_fee,
2398 74 : &dc->cdd.amount)) ||
2399 37 : (0 != TALER_amount_cmp (amount_with_fee,
2400 37 : &dc->cdd.amount)) )
2401 8 : continue; /* does not match, skip */
2402 37 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2403 : "Deposit of coin `%s' already in our DB.\n",
2404 : TALER_B2S (coin_pub));
2405 37 : if ( (GNUNET_OK !=
2406 37 : TALER_amount_cmp_currency (&pc->pay_transaction.total_paid,
2407 37 : amount_with_fee)) ||
2408 : (GNUNET_OK !=
2409 37 : TALER_amount_cmp_currency (&pc->pay_transaction.total_fees_paid,
2410 : deposit_fee)) )
2411 : {
2412 0 : GNUNET_break_op (0);
2413 0 : pc->pay_transaction.deposit_currency_mismatch = true;
2414 0 : break;
2415 : }
2416 37 : GNUNET_assert (0 <=
2417 : TALER_amount_add (&pc->pay_transaction.total_paid,
2418 : &pc->pay_transaction.total_paid,
2419 : amount_with_fee));
2420 37 : GNUNET_assert (0 <=
2421 : TALER_amount_add (&pc->pay_transaction.total_fees_paid,
2422 : &pc->pay_transaction.total_fees_paid,
2423 : deposit_fee));
2424 37 : dc->deposit_fee = *deposit_fee;
2425 37 : dc->refund_fee = *refund_fee;
2426 37 : dc->cdd.amount = *amount_with_fee;
2427 37 : dc->found_in_db = true;
2428 37 : pc->pay_transaction.pending--;
2429 : }
2430 39 : }
2431 :
2432 :
2433 : /**
2434 : * Function called with information about a refund. Check if this coin was
2435 : * claimed by the wallet for the transaction, and if so add the refunded
2436 : * amount to the pc's "total_refunded" amount.
2437 : *
2438 : * @param cls closure with a `struct PayContext`
2439 : * @param coin_pub public coin from which the refund comes from
2440 : * @param refund_amount refund amount which is being taken from @a coin_pub
2441 : */
2442 : static void
2443 0 : check_coin_refunded (void *cls,
2444 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
2445 : const struct TALER_Amount *refund_amount)
2446 : {
2447 0 : struct PayContext *pc = cls;
2448 :
2449 : /* We look at refunds here that apply to the coins
2450 : that the customer is currently trying to pay us with.
2451 :
2452 : Such refunds are not "normal" refunds, but abort-pay refunds, which are
2453 : given in the case that the wallet aborts the payment.
2454 : In the case the wallet then decides to complete the payment *after* doing
2455 : an abort-pay refund (an unusual but possible case), we need
2456 : to make sure that existing refunds are accounted for. */
2457 :
2458 0 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
2459 : {
2460 0 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
2461 :
2462 : /* Get matching coins from results. */
2463 0 : if (0 != GNUNET_memcmp (coin_pub,
2464 : &dc->cdd.coin_pub))
2465 0 : continue;
2466 0 : if (GNUNET_OK !=
2467 0 : TALER_amount_cmp_currency (&pc->pay_transaction.total_refunded,
2468 : refund_amount))
2469 : {
2470 0 : GNUNET_break (0);
2471 0 : pc->pay_transaction.refund_currency_mismatch = true;
2472 0 : break;
2473 : }
2474 0 : GNUNET_assert (0 <=
2475 : TALER_amount_add (&pc->pay_transaction.total_refunded,
2476 : &pc->pay_transaction.total_refunded,
2477 : refund_amount));
2478 0 : break;
2479 : }
2480 0 : }
2481 :
2482 :
2483 : /**
2484 : * Check whether the amount paid is sufficient to cover the price.
2485 : *
2486 : * @param pc payment context to check
2487 : * @return true if the payment is sufficient, false if it is
2488 : * insufficient
2489 : */
2490 : static bool
2491 33 : check_payment_sufficient (struct PayContext *pc)
2492 : {
2493 : struct TALER_Amount acc_fee;
2494 : struct TALER_Amount acc_amount;
2495 : struct TALER_Amount final_amount;
2496 : struct TALER_Amount total_wire_fee;
2497 : struct TALER_Amount total_needed;
2498 :
2499 33 : if (0 == pc->parse_pay.coins_cnt)
2500 2 : return TALER_amount_is_zero (&pc->validate_tokens.brutto);
2501 31 : GNUNET_assert (GNUNET_OK ==
2502 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2503 : &total_wire_fee));
2504 62 : for (unsigned int i = 0; i < pc->parse_pay.num_exchanges; i++)
2505 : {
2506 31 : if (GNUNET_OK !=
2507 31 : TALER_amount_cmp_currency (&total_wire_fee,
2508 31 : &pc->parse_pay.egs[i]->wire_fee))
2509 : {
2510 0 : GNUNET_break_op (0);
2511 0 : pay_end (pc,
2512 : TALER_MHD_reply_with_error (pc->connection,
2513 : MHD_HTTP_BAD_REQUEST,
2514 : TALER_EC_GENERIC_CURRENCY_MISMATCH,
2515 : total_wire_fee.currency));
2516 0 : return false;
2517 : }
2518 31 : if (0 >
2519 31 : TALER_amount_add (&total_wire_fee,
2520 : &total_wire_fee,
2521 31 : &pc->parse_pay.egs[i]->wire_fee))
2522 : {
2523 0 : GNUNET_break (0);
2524 0 : pay_end (pc,
2525 : TALER_MHD_reply_with_error (
2526 : pc->connection,
2527 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2528 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_WIRE_FEE_ADDITION_FAILED,
2529 : "could not add exchange wire fee to total"));
2530 0 : return false;
2531 : }
2532 : }
2533 :
2534 : /**
2535 : * This loops calculates what are the deposit fee / total
2536 : * amount with fee / and wire fee, for all the coins.
2537 : */
2538 31 : GNUNET_assert (GNUNET_OK ==
2539 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2540 : &acc_fee));
2541 31 : GNUNET_assert (GNUNET_OK ==
2542 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2543 : &acc_amount));
2544 68 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
2545 : {
2546 37 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
2547 :
2548 37 : GNUNET_assert (dc->found_in_db);
2549 37 : if ( (GNUNET_OK !=
2550 37 : TALER_amount_cmp_currency (&acc_fee,
2551 74 : &dc->deposit_fee)) ||
2552 : (GNUNET_OK !=
2553 37 : TALER_amount_cmp_currency (&acc_amount,
2554 37 : &dc->cdd.amount)) )
2555 : {
2556 0 : GNUNET_break_op (0);
2557 0 : pay_end (pc,
2558 : TALER_MHD_reply_with_error (
2559 : pc->connection,
2560 : MHD_HTTP_BAD_REQUEST,
2561 : TALER_EC_GENERIC_CURRENCY_MISMATCH,
2562 0 : dc->deposit_fee.currency));
2563 0 : return false;
2564 : }
2565 37 : if ( (0 >
2566 37 : TALER_amount_add (&acc_fee,
2567 37 : &dc->deposit_fee,
2568 37 : &acc_fee)) ||
2569 : (0 >
2570 37 : TALER_amount_add (&acc_amount,
2571 37 : &dc->cdd.amount,
2572 : &acc_amount)) )
2573 : {
2574 0 : GNUNET_break (0);
2575 : /* Overflow in these amounts? Very strange. */
2576 0 : pay_end (pc,
2577 : TALER_MHD_reply_with_error (
2578 : pc->connection,
2579 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2580 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
2581 : "Overflow adding up amounts"));
2582 0 : return false;
2583 : }
2584 37 : if (1 ==
2585 37 : TALER_amount_cmp (&dc->deposit_fee,
2586 37 : &dc->cdd.amount))
2587 : {
2588 0 : GNUNET_break_op (0);
2589 0 : pay_end (pc,
2590 : TALER_MHD_reply_with_error (
2591 : pc->connection,
2592 : MHD_HTTP_BAD_REQUEST,
2593 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_FEES_EXCEED_PAYMENT,
2594 : "Deposit fees exceed coin's contribution"));
2595 0 : return false;
2596 : }
2597 : } /* end deposit loop */
2598 :
2599 31 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2600 : "Amount received from wallet: %s\n",
2601 : TALER_amount2s (&acc_amount));
2602 31 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2603 : "Deposit fee for all coins: %s\n",
2604 : TALER_amount2s (&acc_fee));
2605 31 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2606 : "Total wire fee: %s\n",
2607 : TALER_amount2s (&total_wire_fee));
2608 31 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2609 : "Deposit fee limit for merchant: %s\n",
2610 : TALER_amount2s (&pc->validate_tokens.max_fee));
2611 31 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2612 : "Total refunded amount: %s\n",
2613 : TALER_amount2s (&pc->pay_transaction.total_refunded));
2614 :
2615 : /* Now compare exchange wire fee compared to what we are willing to pay */
2616 31 : if (GNUNET_YES !=
2617 31 : TALER_amount_cmp_currency (&total_wire_fee,
2618 : &acc_fee))
2619 : {
2620 0 : GNUNET_break (0);
2621 0 : pay_end (pc,
2622 : TALER_MHD_reply_with_error (
2623 : pc->connection,
2624 : MHD_HTTP_BAD_REQUEST,
2625 : TALER_EC_GENERIC_CURRENCY_MISMATCH,
2626 : total_wire_fee.currency));
2627 0 : return false;
2628 : }
2629 :
2630 : /* add wire fee to the total fees */
2631 31 : if (0 >
2632 31 : TALER_amount_add (&acc_fee,
2633 : &acc_fee,
2634 : &total_wire_fee))
2635 : {
2636 0 : GNUNET_break (0);
2637 0 : pay_end (pc,
2638 : TALER_MHD_reply_with_error (
2639 : pc->connection,
2640 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2641 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
2642 : "Overflow adding up amounts"));
2643 0 : return false;
2644 : }
2645 31 : if (-1 == TALER_amount_cmp (&pc->validate_tokens.max_fee,
2646 : &acc_fee))
2647 : {
2648 : /**
2649 : * Sum of fees of *all* the different exchanges of all the coins are
2650 : * higher than the fixed limit that the merchant is willing to pay. The
2651 : * difference must be paid by the customer.
2652 : */
2653 : struct TALER_Amount excess_fee;
2654 :
2655 : /* compute fee amount to be covered by customer */
2656 8 : GNUNET_assert (TALER_AAR_RESULT_POSITIVE ==
2657 : TALER_amount_subtract (&excess_fee,
2658 : &acc_fee,
2659 : &pc->validate_tokens.max_fee));
2660 : /* add that to the total */
2661 8 : if (0 >
2662 8 : TALER_amount_add (&total_needed,
2663 : &excess_fee,
2664 8 : &pc->validate_tokens.brutto))
2665 : {
2666 0 : GNUNET_break (0);
2667 0 : pay_end (pc,
2668 : TALER_MHD_reply_with_error (
2669 : pc->connection,
2670 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2671 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
2672 : "Overflow adding up amounts"));
2673 0 : return false;
2674 : }
2675 : }
2676 : else
2677 : {
2678 : /* Fees are fully covered by the merchant, all we require
2679 : is that the total payment is not below the contract's amount */
2680 23 : total_needed = pc->validate_tokens.brutto;
2681 : }
2682 :
2683 : /* Do not count refunds towards the payment */
2684 31 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2685 : "Subtracting total refunds from paid amount: %s\n",
2686 : TALER_amount2s (&pc->pay_transaction.total_refunded));
2687 31 : if (0 >
2688 31 : TALER_amount_subtract (&final_amount,
2689 : &acc_amount,
2690 31 : &pc->pay_transaction.total_refunded))
2691 : {
2692 0 : GNUNET_break (0);
2693 0 : pay_end (pc,
2694 : TALER_MHD_reply_with_error (
2695 : pc->connection,
2696 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2697 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUNDS_EXCEED_PAYMENTS,
2698 : "refunded amount exceeds total payments"));
2699 0 : return false;
2700 : }
2701 :
2702 31 : if (-1 == TALER_amount_cmp (&final_amount,
2703 : &total_needed))
2704 : {
2705 : /* acc_amount < total_needed */
2706 2 : if (-1 < TALER_amount_cmp (&acc_amount,
2707 : &total_needed))
2708 : {
2709 0 : GNUNET_break_op (0);
2710 0 : pay_end (pc,
2711 : TALER_MHD_reply_with_error (
2712 : pc->connection,
2713 : MHD_HTTP_PAYMENT_REQUIRED,
2714 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUNDED,
2715 : "contract not paid up due to refunds"));
2716 0 : return false;
2717 : }
2718 2 : if (-1 < TALER_amount_cmp (&acc_amount,
2719 2 : &pc->validate_tokens.brutto))
2720 : {
2721 0 : GNUNET_break_op (0);
2722 0 : pay_end (pc,
2723 : TALER_MHD_reply_with_error (
2724 : pc->connection,
2725 : MHD_HTTP_BAD_REQUEST,
2726 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_DUE_TO_FEES,
2727 : "contract not paid up due to fees (client may have calculated them badly)"));
2728 0 : return false;
2729 : }
2730 2 : GNUNET_break_op (0);
2731 2 : pay_end (pc,
2732 : TALER_MHD_reply_with_error (
2733 : pc->connection,
2734 : MHD_HTTP_BAD_REQUEST,
2735 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_PAYMENT_INSUFFICIENT,
2736 : "payment insufficient"));
2737 2 : return false;
2738 : }
2739 29 : return true;
2740 : }
2741 :
2742 :
2743 : /**
2744 : * Execute the DB transaction. If required (from
2745 : * soft/serialization errors), the transaction can be
2746 : * restarted here.
2747 : *
2748 : * @param[in,out] pc payment context to transact
2749 : */
2750 : static void
2751 72 : phase_execute_pay_transaction (struct PayContext *pc)
2752 : {
2753 72 : struct TMH_HandlerContext *hc = pc->hc;
2754 72 : const char *instance_id = hc->instance->settings.id;
2755 :
2756 72 : if (pc->batch_deposits.got_451)
2757 : {
2758 0 : pc->phase = PP_FAIL_LEGAL_REASONS;
2759 0 : return;
2760 : }
2761 : /* Avoid re-trying transactions on soft errors forever! */
2762 72 : if (pc->pay_transaction.retry_counter++ > MAX_RETRIES)
2763 : {
2764 0 : GNUNET_break (0);
2765 0 : pay_end (pc,
2766 : TALER_MHD_reply_with_error (pc->connection,
2767 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2768 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
2769 : NULL));
2770 0 : return;
2771 : }
2772 :
2773 : /* Initialize some amount accumulators
2774 : (used in check_coin_paid(), check_coin_refunded()
2775 : and check_payment_sufficient()). */
2776 72 : GNUNET_break (GNUNET_OK ==
2777 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2778 : &pc->pay_transaction.total_paid));
2779 72 : GNUNET_break (GNUNET_OK ==
2780 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2781 : &pc->pay_transaction.total_fees_paid));
2782 72 : GNUNET_break (GNUNET_OK ==
2783 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2784 : &pc->pay_transaction.total_refunded));
2785 154 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
2786 82 : pc->parse_pay.dc[i].found_in_db = false;
2787 72 : pc->pay_transaction.pending = pc->parse_pay.coins_cnt;
2788 :
2789 : /* First, try to see if we have all we need already done */
2790 72 : TMH_db->preflight (TMH_db->cls);
2791 72 : if (GNUNET_OK !=
2792 72 : TMH_db->start (TMH_db->cls,
2793 : "run pay"))
2794 : {
2795 0 : GNUNET_break (0);
2796 0 : pay_end (pc,
2797 : TALER_MHD_reply_with_error (pc->connection,
2798 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2799 : TALER_EC_GENERIC_DB_START_FAILED,
2800 : NULL));
2801 0 : return;
2802 : }
2803 :
2804 72 : if (0 < pc->compute_money_pots.num_pots)
2805 : {
2806 : enum GNUNET_DB_QueryStatus qs;
2807 :
2808 0 : qs = TMH_db->increment_money_pots (TMH_db->cls,
2809 : instance_id,
2810 0 : pc->compute_money_pots.num_pots,
2811 0 : pc->compute_money_pots.pots,
2812 0 : pc->compute_money_pots.increments);
2813 0 : switch (qs)
2814 : {
2815 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
2816 0 : TMH_db->rollback (TMH_db->cls);
2817 0 : return; /* do it again */
2818 0 : case GNUNET_DB_STATUS_HARD_ERROR:
2819 : /* Always report on hard error as well to enable diagnostics */
2820 0 : TMH_db->rollback (TMH_db->cls);
2821 0 : pay_end (pc,
2822 : TALER_MHD_reply_with_error (pc->connection,
2823 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2824 : TALER_EC_GENERIC_DB_STORE_FAILED,
2825 : "increment_money_pots"));
2826 0 : return;
2827 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
2828 : /* strange */
2829 0 : GNUNET_break (0);
2830 0 : break;
2831 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
2832 : /* Good, proceed! */
2833 0 : break;
2834 : }
2835 :
2836 : }
2837 :
2838 74 : for (size_t i = 0; i<pc->parse_pay.tokens_cnt; i++)
2839 : {
2840 4 : struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[i];
2841 : enum GNUNET_DB_QueryStatus qs;
2842 :
2843 : /* Insert used token into database, the unique constraint will
2844 : case an error if this token was used before. */
2845 4 : qs = TMH_db->insert_spent_token (TMH_db->cls,
2846 4 : &pc->check_contract.h_contract_terms,
2847 4 : &tuc->h_issue,
2848 4 : &tuc->pub,
2849 4 : &tuc->sig,
2850 4 : &tuc->unblinded_sig);
2851 :
2852 4 : switch (qs)
2853 : {
2854 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
2855 0 : TMH_db->rollback (TMH_db->cls);
2856 0 : return; /* do it again */
2857 0 : case GNUNET_DB_STATUS_HARD_ERROR:
2858 : /* Always report on hard error as well to enable diagnostics */
2859 0 : TMH_db->rollback (TMH_db->cls);
2860 0 : pay_end (pc,
2861 : TALER_MHD_reply_with_error (pc->connection,
2862 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2863 : TALER_EC_GENERIC_DB_STORE_FAILED,
2864 : "insert used token"));
2865 0 : return;
2866 2 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
2867 : /* UNIQUE constraint violation, meaning this token was already used. */
2868 2 : TMH_db->rollback (TMH_db->cls);
2869 2 : pay_end (pc,
2870 : TALER_MHD_reply_with_error (pc->connection,
2871 : MHD_HTTP_CONFLICT,
2872 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_TOKEN_INVALID,
2873 : NULL));
2874 2 : return;
2875 2 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
2876 : /* Good, proceed! */
2877 2 : break;
2878 : }
2879 : } /* for all tokens */
2880 :
2881 : {
2882 : enum GNUNET_DB_QueryStatus qs;
2883 :
2884 : /* Check if some of these coins already succeeded for _this_ contract. */
2885 70 : qs = TMH_db->lookup_deposits (TMH_db->cls,
2886 : instance_id,
2887 70 : &pc->check_contract.h_contract_terms,
2888 : &check_coin_paid,
2889 : pc);
2890 70 : if (0 > qs)
2891 : {
2892 0 : TMH_db->rollback (TMH_db->cls);
2893 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
2894 0 : return; /* do it again */
2895 : /* Always report on hard error as well to enable diagnostics */
2896 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
2897 0 : pay_end (pc,
2898 : TALER_MHD_reply_with_error (pc->connection,
2899 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2900 : TALER_EC_GENERIC_DB_FETCH_FAILED,
2901 : "lookup deposits"));
2902 0 : return;
2903 : }
2904 70 : if (pc->pay_transaction.deposit_currency_mismatch)
2905 : {
2906 0 : TMH_db->rollback (TMH_db->cls);
2907 0 : GNUNET_break_op (0);
2908 0 : pay_end (pc,
2909 : TALER_MHD_reply_with_error (pc->connection,
2910 : MHD_HTTP_BAD_REQUEST,
2911 : TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
2912 0 : pc->validate_tokens.brutto.currency))
2913 : ;
2914 0 : return;
2915 : }
2916 : }
2917 :
2918 : {
2919 : enum GNUNET_DB_QueryStatus qs;
2920 :
2921 : /* Check if we refunded some of the coins */
2922 70 : qs = TMH_db->lookup_refunds (TMH_db->cls,
2923 : instance_id,
2924 70 : &pc->check_contract.h_contract_terms,
2925 : &check_coin_refunded,
2926 : pc);
2927 70 : if (0 > qs)
2928 : {
2929 0 : TMH_db->rollback (TMH_db->cls);
2930 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
2931 0 : return; /* do it again */
2932 : /* Always report on hard error as well to enable diagnostics */
2933 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
2934 0 : pay_end (pc,
2935 : TALER_MHD_reply_with_error (pc->connection,
2936 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2937 : TALER_EC_GENERIC_DB_FETCH_FAILED,
2938 : "lookup refunds"));
2939 0 : return;
2940 : }
2941 70 : if (pc->pay_transaction.refund_currency_mismatch)
2942 : {
2943 0 : TMH_db->rollback (TMH_db->cls);
2944 0 : pay_end (pc,
2945 : TALER_MHD_reply_with_error (pc->connection,
2946 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2947 : TALER_EC_GENERIC_DB_FETCH_FAILED,
2948 : "refund currency in database does not match order currency"));
2949 0 : return;
2950 : }
2951 : }
2952 :
2953 : /* Check if there are coins that still need to be processed */
2954 70 : if (0 != pc->pay_transaction.pending)
2955 : {
2956 : /* we made no DB changes, so we can just rollback */
2957 37 : TMH_db->rollback (TMH_db->cls);
2958 : /* Ok, we need to first go to the network to process more coins.
2959 : We that interaction in *tiny* transactions (hence the rollback
2960 : above). */
2961 37 : pc->phase = PP_BATCH_DEPOSITS;
2962 37 : return;
2963 : }
2964 :
2965 : /* 0 == pc->pay_transaction.pending: all coins processed, let's see if that was enough */
2966 33 : if (! check_payment_sufficient (pc))
2967 : {
2968 : /* check_payment_sufficient() will have queued an error already.
2969 : We need to still abort the transaction. */
2970 2 : TMH_db->rollback (TMH_db->cls);
2971 2 : return;
2972 : }
2973 : /* Payment succeeded, save in database */
2974 31 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2975 : "Order `%s' (%s) was fully paid\n",
2976 : pc->order_id,
2977 : GNUNET_h2s (&pc->check_contract.h_contract_terms.hash));
2978 : {
2979 : enum GNUNET_DB_QueryStatus qs;
2980 :
2981 31 : qs = TMH_db->mark_contract_paid (TMH_db->cls,
2982 : instance_id,
2983 31 : &pc->check_contract.h_contract_terms,
2984 31 : pc->parse_pay.session_id,
2985 31 : pc->parse_wallet_data.choice_index);
2986 31 : if (qs < 0)
2987 : {
2988 0 : TMH_db->rollback (TMH_db->cls);
2989 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
2990 0 : return; /* do it again */
2991 0 : GNUNET_break (0);
2992 0 : pay_end (pc,
2993 : TALER_MHD_reply_with_error (pc->connection,
2994 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2995 : TALER_EC_GENERIC_DB_STORE_FAILED,
2996 : "mark contract paid"));
2997 0 : return;
2998 : }
2999 : }
3000 :
3001 :
3002 : {
3003 31 : const struct TALER_MERCHANT_ContractChoice *choice =
3004 31 : &pc->check_contract.contract_terms->details.v1
3005 31 : .choices[pc->parse_wallet_data.choice_index];
3006 :
3007 35 : for (size_t i = 0; i<pc->output_tokens_len; i++)
3008 : {
3009 : unsigned int output_index;
3010 : enum TALER_MERCHANT_ContractOutputType type;
3011 :
3012 4 : output_index = pc->output_tokens[i].output_index;
3013 4 : GNUNET_assert (output_index < choice->outputs_len);
3014 4 : type = choice->outputs[output_index].type;
3015 :
3016 4 : switch (type)
3017 : {
3018 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
3019 : /* Well, good luck getting here */
3020 0 : GNUNET_break (0);
3021 0 : pay_end (pc,
3022 : TALER_MHD_reply_with_error (pc->connection,
3023 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3024 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
3025 : "invalid output type"));
3026 0 : break;
3027 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
3028 : /* We skip output tokens of donation receipts here, as they are handled in the
3029 : * phase_final_output_token_processing() callback from donau */
3030 0 : break;
3031 4 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
3032 4 : struct SignedOutputToken *output =
3033 4 : &pc->output_tokens[i];
3034 : enum GNUNET_DB_QueryStatus qs;
3035 :
3036 4 : qs = TMH_db->insert_issued_token (TMH_db->cls,
3037 4 : &pc->check_contract.h_contract_terms,
3038 4 : &output->h_issue,
3039 4 : &output->sig);
3040 4 : switch (qs)
3041 : {
3042 0 : case GNUNET_DB_STATUS_HARD_ERROR:
3043 0 : TMH_db->rollback (TMH_db->cls);
3044 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
3045 0 : pay_end (pc,
3046 : TALER_MHD_reply_with_error (pc->connection,
3047 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3048 : TALER_EC_GENERIC_DB_STORE_FAILED,
3049 : "insert output token"));
3050 0 : return;
3051 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
3052 : /* Serialization failure, retry */
3053 0 : TMH_db->rollback (TMH_db->cls);
3054 0 : return;
3055 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
3056 : /* UNIQUE constraint violation, meaning this token was already used. */
3057 0 : TMH_db->rollback (TMH_db->cls);
3058 0 : pay_end (pc,
3059 : TALER_MHD_reply_with_error (pc->connection,
3060 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3061 : TALER_EC_GENERIC_DB_STORE_FAILED,
3062 : "duplicate output token"));
3063 0 : return;
3064 4 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
3065 4 : break;
3066 : }
3067 4 : break;
3068 : }
3069 : }
3070 : }
3071 :
3072 31 : TMH_notify_order_change (hc->instance,
3073 : TMH_OSF_CLAIMED | TMH_OSF_PAID,
3074 31 : pc->check_contract.contract_terms->timestamp,
3075 : pc->check_contract.order_serial);
3076 : {
3077 : enum GNUNET_DB_QueryStatus qs;
3078 : json_t *jhook;
3079 :
3080 31 : jhook = GNUNET_JSON_PACK (
3081 : GNUNET_JSON_pack_object_incref ("contract_terms",
3082 : pc->check_contract.contract_terms_json),
3083 : GNUNET_JSON_pack_string ("order_id",
3084 : pc->order_id)
3085 : );
3086 31 : GNUNET_assert (NULL != jhook);
3087 31 : qs = TMH_trigger_webhook (pc->hc->instance->settings.id,
3088 : "pay",
3089 : jhook);
3090 31 : json_decref (jhook);
3091 31 : if (qs < 0)
3092 : {
3093 0 : TMH_db->rollback (TMH_db->cls);
3094 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
3095 0 : return; /* do it again */
3096 0 : GNUNET_break (0);
3097 0 : pay_end (pc,
3098 : TALER_MHD_reply_with_error (pc->connection,
3099 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3100 : TALER_EC_GENERIC_DB_STORE_FAILED,
3101 : "failed to trigger webhooks"));
3102 0 : return;
3103 : }
3104 : }
3105 : {
3106 : enum GNUNET_DB_QueryStatus qs;
3107 :
3108 : /* Now commit! */
3109 31 : qs = TMH_db->commit (TMH_db->cls);
3110 31 : if (0 > qs)
3111 : {
3112 : /* commit failed */
3113 0 : TMH_db->rollback (TMH_db->cls);
3114 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
3115 0 : return; /* do it again */
3116 0 : GNUNET_break (0);
3117 0 : pay_end (pc,
3118 : TALER_MHD_reply_with_error (pc->connection,
3119 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3120 : TALER_EC_GENERIC_DB_COMMIT_FAILED,
3121 : NULL));
3122 0 : return;
3123 : }
3124 : }
3125 31 : pc->phase++;
3126 : }
3127 :
3128 :
3129 : /**
3130 : * Ensures that the expected number of tokens for a @e key
3131 : * are provided as inputs and have valid signatures.
3132 : *
3133 : * @param[in,out] pc payment context we are processing
3134 : * @param family family the tokens should be from
3135 : * @param index number of the input we are handling
3136 : * @param expected_num number of tokens expected
3137 : * @return #GNUNET_YES on success
3138 : */
3139 : static enum GNUNET_GenericReturnValue
3140 4 : find_valid_input_tokens (
3141 : struct PayContext *pc,
3142 : const struct TALER_MERCHANT_ContractTokenFamily *family,
3143 : unsigned int index,
3144 : unsigned int expected_num)
3145 : {
3146 4 : unsigned int num_validated = 0;
3147 : struct GNUNET_TIME_Timestamp now
3148 4 : = GNUNET_TIME_timestamp_get ();
3149 4 : const struct TALER_MERCHANT_ContractTokenFamilyKey *kig = NULL;
3150 :
3151 8 : for (unsigned int j = 0; j < expected_num; j++)
3152 : {
3153 4 : struct TokenUseConfirmation *tuc
3154 4 : = &pc->parse_pay.tokens[index + j];
3155 4 : const struct TALER_MERCHANT_ContractTokenFamilyKey *key = NULL;
3156 :
3157 4 : for (unsigned int i = 0; i<family->keys_len; i++)
3158 : {
3159 4 : const struct TALER_MERCHANT_ContractTokenFamilyKey *ki
3160 4 : = &family->keys[i];
3161 :
3162 4 : if (0 ==
3163 4 : GNUNET_memcmp (&ki->pub.public_key->pub_key_hash,
3164 : &tuc->h_issue.hash))
3165 : {
3166 4 : if (GNUNET_TIME_timestamp_cmp (ki->valid_after,
3167 : >,
3168 4 : now) ||
3169 4 : GNUNET_TIME_timestamp_cmp (ki->valid_before,
3170 : <=,
3171 : now))
3172 : {
3173 : /* We have a match, but not in the current validity period */
3174 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3175 : "Public key %s currently not valid\n",
3176 : GNUNET_h2s (&ki->pub.public_key->pub_key_hash));
3177 0 : kig = ki;
3178 0 : continue;
3179 : }
3180 4 : key = ki;
3181 4 : break;
3182 : }
3183 : }
3184 4 : if (NULL == key)
3185 : {
3186 0 : if (NULL != kig)
3187 : {
3188 : char start_str[128];
3189 : char end_str[128];
3190 : char emsg[350];
3191 :
3192 0 : GNUNET_snprintf (start_str,
3193 : sizeof (start_str),
3194 : "%s",
3195 : GNUNET_STRINGS_timestamp_to_string (kig->valid_after));
3196 0 : GNUNET_snprintf (end_str,
3197 : sizeof (end_str),
3198 : "%s",
3199 : GNUNET_STRINGS_timestamp_to_string (kig->valid_before))
3200 : ;
3201 : /* FIXME: use more specific EC */
3202 0 : GNUNET_snprintf (emsg,
3203 : sizeof (emsg),
3204 : "Token is only valid from %s to %s",
3205 : start_str,
3206 : end_str);
3207 0 : pay_end (pc,
3208 : TALER_MHD_reply_with_error (
3209 : pc->connection,
3210 : MHD_HTTP_GONE,
3211 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_OFFER_EXPIRED,
3212 : emsg));
3213 0 : return GNUNET_NO;
3214 : }
3215 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3216 : "Input token supplied for public key %s that is not acceptable\n",
3217 : GNUNET_h2s (&tuc->h_issue.hash));
3218 0 : GNUNET_break_op (0);
3219 0 : pay_end (pc,
3220 : TALER_MHD_reply_with_error (
3221 : pc->connection,
3222 : MHD_HTTP_BAD_REQUEST,
3223 : TALER_EC_MERCHANT_GENERIC_TOKEN_KEY_UNKNOWN,
3224 : NULL));
3225 0 : return GNUNET_NO;
3226 : }
3227 4 : if (GNUNET_OK !=
3228 4 : TALER_token_issue_verify (&tuc->pub,
3229 : &key->pub,
3230 4 : &tuc->unblinded_sig))
3231 : {
3232 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3233 : "Input token for public key with valid_after "
3234 : "`%s' has invalid issue signature\n",
3235 : GNUNET_TIME_timestamp2s (key->valid_after));
3236 0 : GNUNET_break (0);
3237 0 : pay_end (pc,
3238 : TALER_MHD_reply_with_error (
3239 : pc->connection,
3240 : MHD_HTTP_BAD_REQUEST,
3241 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_TOKEN_ISSUE_SIG_INVALID,
3242 : NULL));
3243 0 : return GNUNET_NO;
3244 : }
3245 :
3246 4 : if (GNUNET_OK !=
3247 4 : TALER_wallet_token_use_verify (&pc->check_contract.h_contract_terms,
3248 4 : &pc->parse_wallet_data.h_wallet_data,
3249 4 : &tuc->pub,
3250 4 : &tuc->sig))
3251 : {
3252 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3253 : "Input token for public key with valid_before "
3254 : "`%s' has invalid use signature\n",
3255 : GNUNET_TIME_timestamp2s (key->valid_before));
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_TOKEN_USE_SIG_INVALID,
3262 : NULL));
3263 0 : return GNUNET_NO;
3264 : }
3265 :
3266 4 : num_validated++;
3267 : }
3268 :
3269 4 : if (num_validated != expected_num)
3270 : {
3271 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3272 : "Expected %d tokens for family %s, but found %d\n",
3273 : expected_num,
3274 : family->slug,
3275 : num_validated);
3276 0 : GNUNET_break (0);
3277 0 : pay_end (pc,
3278 : TALER_MHD_reply_with_error (
3279 : pc->connection,
3280 : MHD_HTTP_BAD_REQUEST,
3281 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_TOKEN_COUNT_MISMATCH,
3282 : NULL));
3283 0 : return GNUNET_NO;
3284 : }
3285 4 : return GNUNET_YES;
3286 : }
3287 :
3288 :
3289 : /**
3290 : * Check if an output token of the given @a tfk is mandatory, or if
3291 : * wallets are allowed to simply not support it and still proceed.
3292 : *
3293 : * @param tfk token family kind to check
3294 : * @return true if such outputs are mandatory and wallets must supply
3295 : * the corresponding blinded input
3296 : */
3297 : /* FIXME: this function belongs into a lower-level lib! */
3298 : static bool
3299 6 : test_tfk_mandatory (enum TALER_MERCHANTDB_TokenFamilyKind tfk)
3300 : {
3301 6 : switch (tfk)
3302 : {
3303 0 : case TALER_MERCHANTDB_TFK_Discount:
3304 0 : return false;
3305 6 : case TALER_MERCHANTDB_TFK_Subscription:
3306 6 : return true;
3307 : }
3308 0 : GNUNET_break (0);
3309 0 : return false;
3310 : }
3311 :
3312 :
3313 : /**
3314 : * Sign the tokens provided by the wallet for a particular @a key.
3315 : *
3316 : * @param[in,out] pc reference for payment we are processing
3317 : * @param key token family data
3318 : * @param priv private key to use to sign with
3319 : * @param mandatory true if the token must exist, if false
3320 : * and the client did not provide an envelope, that's OK and
3321 : * we just also skimp on the signature
3322 : * @param index offset in the token envelope array (from other families)
3323 : * @param expected_num number of tokens of this type that we should create
3324 : * @return #GNUNET_NO on failure
3325 : * #GNUNET_OK on success
3326 : */
3327 : static enum GNUNET_GenericReturnValue
3328 6 : sign_token_envelopes (
3329 : struct PayContext *pc,
3330 : const struct TALER_MERCHANT_ContractTokenFamilyKey *key,
3331 : const struct TALER_TokenIssuePrivateKey *priv,
3332 : bool mandatory,
3333 : unsigned int index,
3334 : unsigned int expected_num)
3335 : {
3336 6 : unsigned int num_signed = 0;
3337 :
3338 12 : for (unsigned int j = 0; j<expected_num; j++)
3339 : {
3340 6 : unsigned int pos = index + j;
3341 6 : const struct TokenEnvelope *env
3342 6 : = &pc->parse_wallet_data.token_envelopes[pos];
3343 6 : struct SignedOutputToken *output
3344 6 : = &pc->output_tokens[pos];
3345 :
3346 6 : if ( (pos >= pc->parse_wallet_data.token_envelopes_cnt) ||
3347 6 : (pos >= pc->output_tokens_len) )
3348 : {
3349 0 : GNUNET_assert (0); /* this should not happen */
3350 : return GNUNET_NO;
3351 : }
3352 6 : if (NULL == env->blinded_token.blinded_pub)
3353 : {
3354 0 : if (! mandatory)
3355 0 : continue;
3356 :
3357 : /* mandatory token families require a token envelope. */
3358 0 : GNUNET_break_op (0);
3359 0 : pay_end (pc,
3360 : TALER_MHD_reply_with_error (
3361 : pc->connection,
3362 : MHD_HTTP_BAD_REQUEST,
3363 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3364 : "Token envelope for mandatory token family missing"));
3365 0 : return GNUNET_NO;
3366 : }
3367 6 : TALER_token_issue_sign (priv,
3368 : &env->blinded_token,
3369 : &output->sig);
3370 : output->h_issue.hash
3371 6 : = key->pub.public_key->pub_key_hash;
3372 6 : num_signed++;
3373 : }
3374 :
3375 6 : if (mandatory &&
3376 : (num_signed != expected_num) )
3377 : {
3378 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3379 : "Expected %d token envelopes for public key with valid_after "
3380 : "'%s', but found %d\n",
3381 : expected_num,
3382 : GNUNET_TIME_timestamp2s (key->valid_after),
3383 : num_signed);
3384 0 : GNUNET_break (0);
3385 0 : pay_end (pc,
3386 : TALER_MHD_reply_with_error (
3387 : pc->connection,
3388 : MHD_HTTP_BAD_REQUEST,
3389 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_TOKEN_ENVELOPE_COUNT_MISMATCH,
3390 : NULL));
3391 0 : return GNUNET_NO;
3392 : }
3393 :
3394 6 : return GNUNET_OK;
3395 : }
3396 :
3397 :
3398 : /**
3399 : * Find the family entry for the family of the given @a slug
3400 : * in @a pc.
3401 : *
3402 : * @param[in] pc payment context to search
3403 : * @param slug slug to search for
3404 : * @return NULL if @a slug was not found
3405 : */
3406 : static const struct TALER_MERCHANT_ContractTokenFamily *
3407 10 : find_family (const struct PayContext *pc,
3408 : const char *slug)
3409 : {
3410 10 : for (unsigned int i = 0;
3411 10 : i < pc->check_contract.contract_terms->details.v1.token_authorities_len;
3412 0 : i++)
3413 : {
3414 10 : const struct TALER_MERCHANT_ContractTokenFamily *tfi
3415 10 : = &pc->check_contract.contract_terms->details.v1.token_authorities[i];
3416 :
3417 10 : if (0 == strcmp (tfi->slug,
3418 : slug))
3419 : {
3420 10 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3421 : "Token family %s found with %u keys\n",
3422 : slug,
3423 : tfi->keys_len);
3424 10 : return tfi;
3425 : }
3426 : }
3427 0 : return NULL;
3428 : }
3429 :
3430 :
3431 : /**
3432 : * Handle contract output of type TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN.
3433 : * Looks up the token family, loads the matching private key,
3434 : * and signs the corresponding token envelopes from the wallet.
3435 : *
3436 : * @param pc context for the pay request
3437 : * @param output contract output we need to process
3438 : * @param output_index index of this output in the contract's outputs array
3439 : * @return #GNUNET_OK on success, #GNUNET_NO if an error was encountered
3440 : */
3441 : static enum GNUNET_GenericReturnValue
3442 6 : handle_output_token (struct PayContext *pc,
3443 : const struct TALER_MERCHANT_ContractOutput *output,
3444 : unsigned int output_index)
3445 : {
3446 : const struct TALER_MERCHANT_ContractTokenFamily *family;
3447 : struct TALER_MERCHANT_ContractTokenFamilyKey *key;
3448 : struct TALER_MERCHANTDB_TokenFamilyKeyDetails details;
3449 : enum GNUNET_DB_QueryStatus qs;
3450 : bool mandatory;
3451 :
3452 : /* Locate token family in the contract.
3453 : This should ever fail as this invariant should
3454 : have been checked when the contract was created. */
3455 6 : family = find_family (pc,
3456 6 : output->details.token.token_family_slug);
3457 6 : if (NULL == family)
3458 : {
3459 : /* This "should never happen", so treat it as an internal error */
3460 0 : GNUNET_break (0);
3461 0 : pay_end (pc,
3462 : TALER_MHD_reply_with_error (
3463 : pc->connection,
3464 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3465 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
3466 : "token family not found in order"));
3467 0 : return GNUNET_SYSERR;
3468 : }
3469 :
3470 : /* Check the key_index field from the output. */
3471 6 : if (output->details.token.key_index >= family->keys_len)
3472 : {
3473 : /* Also "should never happen", contract was presumably validated on insert */
3474 0 : GNUNET_break (0);
3475 0 : pay_end (pc,
3476 : TALER_MHD_reply_with_error (
3477 : pc->connection,
3478 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3479 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
3480 : "key index invalid for token family"));
3481 0 : return GNUNET_SYSERR;
3482 : }
3483 :
3484 : /* Pick the correct key inside that family. */
3485 6 : key = &family->keys[output->details.token.key_index];
3486 :
3487 : /* Fetch the private key from the DB for the merchant instance and
3488 : * this particular family/time interval. */
3489 6 : qs = TMH_db->lookup_token_family_key (
3490 6 : TMH_db->cls,
3491 6 : pc->hc->instance->settings.id,
3492 6 : family->slug,
3493 6 : pc->check_contract.contract_terms->timestamp,
3494 6 : pc->check_contract.contract_terms->pay_deadline,
3495 : &details);
3496 6 : switch (qs)
3497 : {
3498 0 : case GNUNET_DB_STATUS_HARD_ERROR:
3499 : case GNUNET_DB_STATUS_SOFT_ERROR:
3500 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3501 : "Database error looking up token-family key for %s\n",
3502 : family->slug);
3503 0 : GNUNET_break (0);
3504 0 : pay_end (pc,
3505 : TALER_MHD_reply_with_error (
3506 : pc->connection,
3507 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3508 : TALER_EC_GENERIC_DB_FETCH_FAILED,
3509 : NULL));
3510 0 : return GNUNET_NO;
3511 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
3512 0 : GNUNET_log (
3513 : GNUNET_ERROR_TYPE_ERROR,
3514 : "Token-family key for %s not found at [%llu,%llu]\n",
3515 : family->slug,
3516 : (unsigned long long)
3517 : pc->check_contract.contract_terms->timestamp.abs_time.abs_value_us,
3518 : (unsigned long long)
3519 : pc->check_contract.contract_terms->pay_deadline.abs_time.abs_value_us
3520 : );
3521 0 : GNUNET_break (0);
3522 0 : pay_end (pc,
3523 : TALER_MHD_reply_with_error (
3524 : pc->connection,
3525 : MHD_HTTP_NOT_FOUND,
3526 : TALER_EC_GENERIC_DB_FETCH_FAILED,
3527 : NULL));
3528 0 : return GNUNET_NO;
3529 :
3530 6 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
3531 6 : break;
3532 : }
3533 6 : GNUNET_assert (NULL != details.priv.private_key);
3534 6 : GNUNET_free (details.token_family.slug);
3535 6 : GNUNET_free (details.token_family.name);
3536 6 : GNUNET_free (details.token_family.description);
3537 6 : json_decref (details.token_family.description_i18n);
3538 6 : GNUNET_CRYPTO_blind_sign_pub_decref (details.pub.public_key);
3539 6 : GNUNET_free (details.token_family.cipher_spec);
3540 :
3541 : /* Depending on the token family, decide if the token envelope
3542 : * is mandatory or optional. (Simplified logic here: adapt as needed.) */
3543 6 : mandatory = test_tfk_mandatory (details.token_family.kind);
3544 : /* Actually sign the number of token envelopes specified in 'count'.
3545 : * 'output_index' is the offset into the parse_wallet_data arrays. */
3546 6 : if (GNUNET_OK !=
3547 6 : sign_token_envelopes (pc,
3548 : key,
3549 : &details.priv,
3550 : mandatory,
3551 : output_index,
3552 6 : output->details.token.count))
3553 : {
3554 : /* sign_token_envelopes() already queued up an error via pay_end() */
3555 0 : GNUNET_break_op (0);
3556 0 : return GNUNET_NO;
3557 : }
3558 6 : GNUNET_CRYPTO_blind_sign_priv_decref (details.priv.private_key);
3559 6 : return GNUNET_OK;
3560 : }
3561 :
3562 :
3563 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
3564 : /**
3565 : * Handle checks for contract output of type TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT.
3566 : * For now, this does nothing and simply returns #GNUNET_OK.
3567 : *
3568 : * @param pc context for the pay request
3569 : * @param output the contract output describing the donation receipt requirement
3570 : * @return #GNUNET_OK on success,
3571 : * #GNUNET_NO if an error was already queued
3572 : */
3573 : static enum GNUNET_GenericReturnValue
3574 : handle_output_donation_receipt (
3575 : struct PayContext *pc,
3576 : const struct TALER_MERCHANT_ContractOutput *output)
3577 : {
3578 : enum GNUNET_GenericReturnValue ret;
3579 :
3580 : ret = DONAU_get_donation_amount_from_bkps (
3581 : pc->parse_wallet_data.donau_keys,
3582 : pc->parse_wallet_data.bkps,
3583 : pc->parse_wallet_data.num_bkps,
3584 : pc->parse_wallet_data.donau.donation_year,
3585 : &pc->parse_wallet_data.donation_amount);
3586 : switch (ret)
3587 : {
3588 : case GNUNET_SYSERR:
3589 : GNUNET_break (0);
3590 : pay_end (pc,
3591 : TALER_MHD_reply_with_error (
3592 : pc->connection,
3593 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3594 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
3595 : NULL));
3596 : return GNUNET_NO;
3597 : case GNUNET_NO:
3598 : GNUNET_break_op (0);
3599 : pay_end (pc,
3600 : TALER_MHD_reply_with_error (
3601 : pc->connection,
3602 : MHD_HTTP_BAD_REQUEST,
3603 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3604 : "inconsistent bkps / donau keys"));
3605 : return GNUNET_NO;
3606 : case GNUNET_OK:
3607 : break;
3608 : }
3609 :
3610 : if (GNUNET_OK !=
3611 : TALER_amount_cmp_currency (&pc->parse_wallet_data.donation_amount,
3612 : &output->details.donation_receipt.amount))
3613 : {
3614 : GNUNET_break_op (0);
3615 : pay_end (pc,
3616 : TALER_MHD_reply_with_error (
3617 : pc->connection,
3618 : MHD_HTTP_BAD_REQUEST,
3619 : TALER_EC_GENERIC_CURRENCY_MISMATCH,
3620 : output->details.donation_receipt.amount.currency));
3621 : return GNUNET_NO;
3622 : }
3623 :
3624 : if (0 !=
3625 : TALER_amount_cmp (&pc->parse_wallet_data.donation_amount,
3626 : &output->details.donation_receipt.amount))
3627 : {
3628 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3629 : "Wallet amount: %s\n",
3630 : TALER_amount2s (&pc->parse_wallet_data.donation_amount));
3631 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3632 : "Donation receipt amount: %s\n",
3633 : TALER_amount2s (&output->details.donation_receipt.amount));
3634 : GNUNET_break_op (0);
3635 : pay_end (pc,
3636 : TALER_MHD_reply_with_error (
3637 : pc->connection,
3638 : MHD_HTTP_CONFLICT,
3639 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DONATION_AMOUNT_MISMATCH,
3640 : "donation amount mismatch"));
3641 : return GNUNET_NO;
3642 : }
3643 : {
3644 : struct TALER_Amount receipts_to_date;
3645 :
3646 : if (0 >
3647 : TALER_amount_add (&receipts_to_date,
3648 : &pc->parse_wallet_data.charity_receipts_to_date,
3649 : &pc->parse_wallet_data.donation_amount))
3650 : {
3651 : GNUNET_break (0);
3652 : pay_end (pc,
3653 : TALER_MHD_reply_with_error (pc->connection,
3654 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3655 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
3656 : "adding donation amount"));
3657 : return GNUNET_NO;
3658 : }
3659 :
3660 : if (1 ==
3661 : TALER_amount_cmp (&receipts_to_date,
3662 : &pc->parse_wallet_data.charity_max_per_year))
3663 : {
3664 : GNUNET_break_op (0);
3665 : pay_end (pc,
3666 : TALER_MHD_reply_with_error (pc->connection,
3667 : MHD_HTTP_CONFLICT,
3668 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DONATION_AMOUNT_MISMATCH,
3669 : "donation limit exceeded"));
3670 : return GNUNET_NO;
3671 : }
3672 : pc->parse_wallet_data.charity_receipts_to_date = receipts_to_date;
3673 : }
3674 : return GNUNET_OK;
3675 : }
3676 :
3677 :
3678 : #endif /* HAVE_DONAU_DONAU_SERVICE_H */
3679 :
3680 :
3681 : /**
3682 : * Count tokens produced by an output.
3683 : *
3684 : * @param pc pay context
3685 : * @param output output to consider
3686 : * @returns number of output tokens
3687 : */
3688 : static unsigned int
3689 6 : count_output_tokens (const struct PayContext *pc,
3690 : const struct TALER_MERCHANT_ContractOutput *output)
3691 : {
3692 6 : switch (output->type)
3693 : {
3694 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
3695 0 : GNUNET_assert (0);
3696 : break;
3697 6 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
3698 6 : return output->details.token.count;
3699 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
3700 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
3701 : return pc->parse_wallet_data.num_bkps;
3702 : #else
3703 0 : return 0;
3704 : #endif
3705 : }
3706 : /* Not reached. */
3707 0 : GNUNET_assert (0);
3708 : }
3709 :
3710 :
3711 : /**
3712 : * Validate tokens and token envelopes. First, we check if all tokens listed
3713 : * in the 'inputs' array of the selected choice are present in the 'tokens'
3714 : * array of the request. Then, we validate the signatures of each provided
3715 : * token.
3716 : *
3717 : * @param[in,out] pc context we use to handle the payment
3718 : */
3719 : static void
3720 41 : phase_validate_tokens (struct PayContext *pc)
3721 : {
3722 : /* We haven't seen a donau output yet. */
3723 41 : pc->validate_tokens.donau_output_index = -1;
3724 :
3725 41 : switch (pc->check_contract.contract_terms->version)
3726 : {
3727 35 : case TALER_MERCHANT_CONTRACT_VERSION_0:
3728 : /* No tokens to validate */
3729 35 : pc->phase = PP_COMPUTE_MONEY_POTS;
3730 : pc->validate_tokens.max_fee
3731 35 : = pc->check_contract.contract_terms->details.v0.max_fee;
3732 : pc->validate_tokens.brutto
3733 35 : = pc->check_contract.contract_terms->details.v0.brutto;
3734 35 : break;
3735 6 : case TALER_MERCHANT_CONTRACT_VERSION_1:
3736 : {
3737 6 : const struct TALER_MERCHANT_ContractChoice *selected
3738 6 : = &pc->check_contract.contract_terms->details.v1.choices[
3739 6 : pc->parse_wallet_data.choice_index];
3740 : unsigned int output_off;
3741 : unsigned int cnt;
3742 :
3743 6 : pc->validate_tokens.max_fee = selected->max_fee;
3744 6 : pc->validate_tokens.brutto = selected->amount;
3745 :
3746 10 : for (unsigned int i = 0; i<selected->inputs_len; i++)
3747 : {
3748 4 : const struct TALER_MERCHANT_ContractInput *input
3749 4 : = &selected->inputs[i];
3750 : const struct TALER_MERCHANT_ContractTokenFamily *family;
3751 :
3752 4 : switch (input->type)
3753 : {
3754 0 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
3755 0 : GNUNET_break (0);
3756 0 : pay_end (pc,
3757 : TALER_MHD_reply_with_error (
3758 : pc->connection,
3759 : MHD_HTTP_BAD_REQUEST,
3760 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3761 : "input token type not valid"));
3762 0 : return;
3763 : #if FUTURE
3764 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_COIN:
3765 : GNUNET_break (0);
3766 : pay_end (pc,
3767 : TALER_MHD_reply_with_error (
3768 : pc->connection,
3769 : MHD_HTTP_NOT_IMPLEMENTED,
3770 : TALER_EC_MERCHANT_GENERIC_FEATURE_NOT_AVAILABLE,
3771 : "token type not yet supported"));
3772 : return;
3773 : #endif
3774 4 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
3775 4 : family = find_family (pc,
3776 4 : input->details.token.token_family_slug);
3777 4 : if (NULL == family)
3778 : {
3779 : /* this should never happen, since the choices and
3780 : token families are validated on insert. */
3781 0 : GNUNET_break (0);
3782 0 : pay_end (pc,
3783 : TALER_MHD_reply_with_error (
3784 : pc->connection,
3785 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3786 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
3787 : "token family not found in order"));
3788 0 : return;
3789 : }
3790 4 : if (GNUNET_NO ==
3791 4 : find_valid_input_tokens (pc,
3792 : family,
3793 : i,
3794 4 : input->details.token.count))
3795 : {
3796 : /* Error is already scheduled from find_valid_input_token. */
3797 0 : return;
3798 : }
3799 : }
3800 : }
3801 :
3802 : /* calculate pc->output_tokens_len */
3803 6 : output_off = 0;
3804 12 : for (unsigned int i = 0; i<selected->outputs_len; i++)
3805 : {
3806 6 : const struct TALER_MERCHANT_ContractOutput *output
3807 6 : = &selected->outputs[i];
3808 :
3809 6 : switch (output->type)
3810 : {
3811 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
3812 0 : GNUNET_assert (0);
3813 : break;
3814 6 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
3815 6 : cnt = output->details.token.count;
3816 6 : if (output_off + cnt < output_off)
3817 : {
3818 0 : GNUNET_break_op (0);
3819 0 : pay_end (pc,
3820 : TALER_MHD_reply_with_error (
3821 : pc->connection,
3822 : MHD_HTTP_BAD_REQUEST,
3823 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3824 : "output token counter overflow"));
3825 0 : return;
3826 : }
3827 6 : output_off += cnt;
3828 6 : break;
3829 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
3830 : /* check that this output type appears at most once */
3831 0 : if (pc->validate_tokens.donau_output_index >= 0)
3832 : {
3833 : /* This should have been prevented when the
3834 : contract was initially created */
3835 0 : GNUNET_break (0);
3836 0 : pay_end (pc,
3837 : TALER_MHD_reply_with_error (
3838 : pc->connection,
3839 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3840 : TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
3841 : "two donau output sets in same contract"));
3842 0 : return;
3843 : }
3844 0 : pc->validate_tokens.donau_output_index = i;
3845 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
3846 : if (output_off + pc->parse_wallet_data.num_bkps < output_off)
3847 : {
3848 : GNUNET_break_op (0);
3849 : pay_end (pc,
3850 : TALER_MHD_reply_with_error (
3851 : pc->connection,
3852 : MHD_HTTP_BAD_REQUEST,
3853 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3854 : "output token counter overflow"));
3855 : return;
3856 : }
3857 : output_off += pc->parse_wallet_data.num_bkps;
3858 : #endif
3859 0 : break;
3860 : }
3861 : }
3862 :
3863 :
3864 6 : pc->output_tokens_len = output_off;
3865 : pc->output_tokens
3866 6 : = GNUNET_new_array (pc->output_tokens_len,
3867 : struct SignedOutputToken);
3868 :
3869 : /* calculate pc->output_tokens[].output_index */
3870 6 : output_off = 0;
3871 12 : for (unsigned int i = 0; i<selected->outputs_len; i++)
3872 : {
3873 6 : const struct TALER_MERCHANT_ContractOutput *output
3874 6 : = &selected->outputs[i];
3875 6 : cnt = count_output_tokens (pc,
3876 : output);
3877 12 : for (unsigned int j = 0; j<cnt; j++)
3878 6 : pc->output_tokens[output_off + j].output_index = i;
3879 6 : output_off += cnt;
3880 : }
3881 :
3882 : /* compute non-donau outputs */
3883 6 : output_off = 0;
3884 12 : for (unsigned int i = 0; i<selected->outputs_len; i++)
3885 : {
3886 6 : const struct TALER_MERCHANT_ContractOutput *output
3887 6 : = &selected->outputs[i];
3888 :
3889 6 : switch (output->type)
3890 : {
3891 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
3892 0 : GNUNET_assert (0);
3893 : break;
3894 6 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
3895 6 : cnt = output->details.token.count;
3896 6 : GNUNET_assert (output_off + cnt
3897 : <= pc->output_tokens_len);
3898 6 : if (GNUNET_OK !=
3899 6 : handle_output_token (pc,
3900 : output,
3901 : output_off))
3902 : {
3903 : /* Error is already scheduled from handle_output_token. */
3904 0 : return;
3905 : }
3906 6 : output_off += cnt;
3907 6 : break;
3908 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
3909 : #ifndef HAVE_DONAU_DONAU_SERVICE_H
3910 : /* We checked at parse time, and
3911 : wallet didn't want donau, so OK! */
3912 0 : return;
3913 : #else
3914 : if ( (0 != pc->parse_wallet_data.num_bkps) &&
3915 : (GNUNET_OK !=
3916 : handle_output_donation_receipt (pc,
3917 : output)) )
3918 : {
3919 : /* Error is already scheduled from handle_output_donation_receipt. */
3920 : return;
3921 : }
3922 : output_off += pc->parse_wallet_data.num_bkps;
3923 : continue;
3924 : #endif
3925 : } /* switch on output token */
3926 : } /* for all output token types */
3927 : } /* case contract v1 */
3928 6 : break;
3929 : } /* switch on contract type */
3930 :
3931 86 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
3932 : {
3933 45 : const struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
3934 :
3935 45 : if (GNUNET_OK !=
3936 45 : TALER_amount_cmp_currency (&dc->cdd.amount,
3937 45 : &pc->validate_tokens.brutto))
3938 : {
3939 0 : GNUNET_break_op (0);
3940 0 : pay_end (pc,
3941 : TALER_MHD_reply_with_error (
3942 : pc->connection,
3943 : MHD_HTTP_CONFLICT,
3944 : TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
3945 0 : pc->validate_tokens.brutto.currency));
3946 0 : return;
3947 : }
3948 : }
3949 :
3950 41 : pc->phase = PP_COMPUTE_MONEY_POTS;
3951 : }
3952 :
3953 :
3954 : /**
3955 : * Function called with information about a coin that was deposited.
3956 : * Checks if this coin is in our list of deposits as well.
3957 : *
3958 : * @param cls closure with our `struct PayContext *`
3959 : * @param deposit_serial which deposit operation is this about
3960 : * @param exchange_url URL of the exchange that issued the coin
3961 : * @param h_wire hash of merchant's wire details
3962 : * @param deposit_timestamp when was the deposit made
3963 : * @param amount_with_fee amount the exchange will deposit for this coin
3964 : * @param deposit_fee fee the exchange will charge for this coin
3965 : * @param coin_pub public key of the coin
3966 : */
3967 : static void
3968 6 : deposit_paid_check (
3969 : void *cls,
3970 : uint64_t deposit_serial,
3971 : const char *exchange_url,
3972 : const struct TALER_MerchantWireHashP *h_wire,
3973 : struct GNUNET_TIME_Timestamp deposit_timestamp,
3974 : const struct TALER_Amount *amount_with_fee,
3975 : const struct TALER_Amount *deposit_fee,
3976 : const struct TALER_CoinSpendPublicKeyP *coin_pub)
3977 : {
3978 6 : struct PayContext *pc = cls;
3979 :
3980 14 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
3981 : {
3982 10 : struct DepositConfirmation *dci = &pc->parse_pay.dc[i];
3983 :
3984 10 : if ( (0 ==
3985 10 : GNUNET_memcmp (&dci->cdd.coin_pub,
3986 2 : coin_pub)) &&
3987 : (0 ==
3988 2 : strcmp (dci->exchange_url,
3989 2 : exchange_url)) &&
3990 : (GNUNET_YES ==
3991 2 : TALER_amount_cmp_currency (&dci->cdd.amount,
3992 2 : amount_with_fee)) &&
3993 : (0 ==
3994 2 : TALER_amount_cmp (&dci->cdd.amount,
3995 : amount_with_fee)) )
3996 : {
3997 2 : dci->matched_in_db = true;
3998 2 : break;
3999 : }
4000 : }
4001 6 : }
4002 :
4003 :
4004 : /**
4005 : * Function called with information about a token that was spent.
4006 : * FIXME: Replace this with a more specific function for this cb
4007 : *
4008 : * @param cls closure with `struct PayContext *`
4009 : * @param spent_token_serial "serial" of the spent token unused
4010 : * @param h_contract_terms hash of the contract terms unused
4011 : * @param h_issue_pub hash of the token issue public key unused
4012 : * @param use_pub public key of the token
4013 : * @param use_sig signature of the token
4014 : * @param issue_sig signature of the token issue
4015 : */
4016 : static void
4017 0 : input_tokens_paid_check (
4018 : void *cls,
4019 : uint64_t spent_token_serial,
4020 : const struct TALER_PrivateContractHashP *h_contract_terms,
4021 : const struct TALER_TokenIssuePublicKeyHashP *h_issue_pub,
4022 : const struct TALER_TokenUsePublicKeyP *use_pub,
4023 : const struct TALER_TokenUseSignatureP *use_sig,
4024 : const struct TALER_TokenIssueSignature *issue_sig)
4025 : {
4026 0 : struct PayContext *pc = cls;
4027 :
4028 0 : for (size_t i = 0; i<pc->parse_pay.tokens_cnt; i++)
4029 : {
4030 0 : struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[i];
4031 :
4032 0 : if ( (0 ==
4033 0 : GNUNET_memcmp (&tuc->pub,
4034 0 : use_pub)) &&
4035 : (0 ==
4036 0 : GNUNET_memcmp (&tuc->sig,
4037 0 : use_sig)) &&
4038 : (0 ==
4039 0 : GNUNET_memcmp (&tuc->unblinded_sig,
4040 : issue_sig)) )
4041 : {
4042 0 : tuc->found_in_db = true;
4043 0 : break;
4044 : }
4045 : }
4046 0 : }
4047 :
4048 :
4049 : /**
4050 : * Small helper function to append an output token signature from db
4051 : *
4052 : * @param cls closure with `struct PayContext *`
4053 : * @param h_issue hash of the token
4054 : * @param sig signature of the token
4055 : */
4056 : static void
4057 0 : append_output_token_sig (void *cls,
4058 : struct GNUNET_HashCode *h_issue,
4059 : struct GNUNET_CRYPTO_BlindedSignature *sig)
4060 : {
4061 0 : struct PayContext *pc = cls;
4062 : struct TALER_MERCHANT_ContractChoice *choice;
4063 : const struct TALER_MERCHANT_ContractOutput *output;
4064 : struct SignedOutputToken out;
4065 : unsigned int cnt;
4066 :
4067 0 : GNUNET_assert (TALER_MERCHANT_CONTRACT_VERSION_1 ==
4068 : pc->check_contract.contract_terms->version);
4069 0 : choice = &pc->check_contract.contract_terms->details.v1
4070 0 : .choices[pc->parse_wallet_data.choice_index];
4071 0 : output = &choice->outputs[pc->output_index_gen];
4072 0 : cnt = count_output_tokens (pc,
4073 : output);
4074 0 : out.output_index = pc->output_index_gen;
4075 0 : out.h_issue.hash = *h_issue;
4076 0 : out.sig.signature = sig;
4077 0 : GNUNET_CRYPTO_blind_sig_incref (sig);
4078 0 : GNUNET_array_append (pc->output_tokens,
4079 : pc->output_tokens_len,
4080 : out);
4081 : /* Go to next output once we've output all tokens for the current one. */
4082 0 : pc->output_token_cnt++;
4083 0 : if (pc->output_token_cnt >= cnt)
4084 : {
4085 0 : pc->output_token_cnt = 0;
4086 0 : pc->output_index_gen++;
4087 : }
4088 0 : }
4089 :
4090 :
4091 : /**
4092 : * Handle case where contract was already paid. Either decides
4093 : * the payment is idempotent, or refunds the excess payment.
4094 : *
4095 : * @param[in,out] pc context we use to handle the payment
4096 : */
4097 : static void
4098 4 : phase_contract_paid (struct PayContext *pc)
4099 : {
4100 : json_t *refunds;
4101 4 : bool unmatched = false;
4102 :
4103 : {
4104 : enum GNUNET_DB_QueryStatus qs;
4105 :
4106 4 : qs = TMH_db->lookup_deposits_by_order (TMH_db->cls,
4107 : pc->check_contract.order_serial,
4108 : &deposit_paid_check,
4109 : pc);
4110 : /* Since orders with choices can have a price of zero,
4111 : 0 is also a valid query state */
4112 4 : if (qs < 0)
4113 : {
4114 0 : GNUNET_break (0);
4115 0 : pay_end (pc,
4116 : TALER_MHD_reply_with_error (
4117 : pc->connection,
4118 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4119 : TALER_EC_GENERIC_DB_FETCH_FAILED,
4120 : "lookup_deposits_by_order"));
4121 2 : return;
4122 : }
4123 : }
4124 4 : for (size_t i = 0;
4125 8 : i<pc->parse_pay.coins_cnt && ! unmatched;
4126 4 : i++)
4127 : {
4128 4 : struct DepositConfirmation *dci = &pc->parse_pay.dc[i];
4129 :
4130 4 : if (! dci->matched_in_db)
4131 2 : unmatched = true;
4132 : }
4133 : /* Check if provided input tokens match token in the database */
4134 : {
4135 : enum GNUNET_DB_QueryStatus qs;
4136 :
4137 : /* FIXME-Optimization: Maybe use h_contract instead of order_serial here? */
4138 4 : qs = TMH_db->lookup_spent_tokens_by_order (TMH_db->cls,
4139 : pc->check_contract.order_serial,
4140 : &input_tokens_paid_check,
4141 : pc);
4142 :
4143 4 : if (qs < 0)
4144 : {
4145 0 : GNUNET_break (0);
4146 0 : pay_end (pc,
4147 : TALER_MHD_reply_with_error (
4148 : pc->connection,
4149 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4150 : TALER_EC_GENERIC_DB_FETCH_FAILED,
4151 : "lookup_spent_tokens_by_order"));
4152 0 : return;
4153 : }
4154 : }
4155 4 : for (size_t i = 0; i<pc->parse_pay.tokens_cnt && ! unmatched; i++)
4156 : {
4157 0 : struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[i];
4158 :
4159 0 : if (! tuc->found_in_db)
4160 0 : unmatched = true;
4161 : }
4162 :
4163 : /* In this part we are fetching token_sigs related output */
4164 4 : if (! unmatched)
4165 : {
4166 : /* Everything fine, idempotent request, generate response immediately */
4167 : enum GNUNET_DB_QueryStatus qs;
4168 :
4169 2 : pc->output_index_gen = 0;
4170 2 : qs = TMH_db->select_order_blinded_sigs (
4171 2 : TMH_db->cls,
4172 : pc->order_id,
4173 : &append_output_token_sig,
4174 : pc);
4175 2 : if (0 > qs)
4176 : {
4177 0 : GNUNET_break (0);
4178 0 : pay_end (pc,
4179 : TALER_MHD_reply_with_error (
4180 : pc->connection,
4181 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4182 : TALER_EC_GENERIC_DB_FETCH_FAILED,
4183 : "select_order_blinded_sigs"));
4184 0 : return;
4185 : }
4186 :
4187 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4188 : "Idempotent pay request for order `%s', signing again\n",
4189 : pc->order_id);
4190 2 : pc->phase = PP_SUCCESS_RESPONSE;
4191 2 : return;
4192 : }
4193 : /* Conflict, double-payment detected! */
4194 : /* FIXME-#8674: What should we do with input tokens?
4195 : Currently there is no refund for tokens. */
4196 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4197 : "Client attempted to pay extra for already paid order `%s'\n",
4198 : pc->order_id);
4199 2 : refunds = json_array ();
4200 2 : GNUNET_assert (NULL != refunds);
4201 6 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
4202 : {
4203 4 : struct DepositConfirmation *dci = &pc->parse_pay.dc[i];
4204 : struct TALER_MerchantSignatureP merchant_sig;
4205 :
4206 4 : if (dci->matched_in_db)
4207 0 : continue;
4208 4 : TALER_merchant_refund_sign (&dci->cdd.coin_pub,
4209 4 : &pc->check_contract.h_contract_terms,
4210 : 0, /* rtransaction id */
4211 4 : &dci->cdd.amount,
4212 4 : &pc->hc->instance->merchant_priv,
4213 : &merchant_sig);
4214 4 : GNUNET_assert (
4215 : 0 ==
4216 : json_array_append_new (
4217 : refunds,
4218 : GNUNET_JSON_PACK (
4219 : GNUNET_JSON_pack_data_auto (
4220 : "coin_pub",
4221 : &dci->cdd.coin_pub),
4222 : GNUNET_JSON_pack_data_auto (
4223 : "merchant_sig",
4224 : &merchant_sig),
4225 : TALER_JSON_pack_amount ("amount",
4226 : &dci->cdd.amount),
4227 : GNUNET_JSON_pack_uint64 ("rtransaction_id",
4228 : 0))));
4229 : }
4230 2 : pay_end (pc,
4231 2 : TALER_MHD_REPLY_JSON_PACK (
4232 : pc->connection,
4233 : MHD_HTTP_CONFLICT,
4234 : TALER_MHD_PACK_EC (
4235 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_ALREADY_PAID),
4236 : GNUNET_JSON_pack_array_steal ("refunds",
4237 : refunds)));
4238 : }
4239 :
4240 :
4241 : /**
4242 : * Check the database state for the given order.
4243 : * Schedules an error response in the connection on failure.
4244 : *
4245 : * @param[in,out] pc context we use to handle the payment
4246 : */
4247 : static void
4248 45 : phase_check_contract (struct PayContext *pc)
4249 : {
4250 : /* obtain contract terms */
4251 : enum GNUNET_DB_QueryStatus qs;
4252 45 : bool paid = false;
4253 :
4254 45 : if (NULL != pc->check_contract.contract_terms_json)
4255 : {
4256 0 : json_decref (pc->check_contract.contract_terms_json);
4257 0 : pc->check_contract.contract_terms_json = NULL;
4258 : }
4259 45 : if (NULL != pc->check_contract.contract_terms)
4260 : {
4261 0 : TALER_MERCHANT_contract_free (pc->check_contract.contract_terms);
4262 0 : pc->check_contract.contract_terms = NULL;
4263 : }
4264 45 : qs = TMH_db->lookup_contract_terms2 (TMH_db->cls,
4265 45 : pc->hc->instance->settings.id,
4266 : pc->order_id,
4267 : &pc->check_contract.contract_terms_json,
4268 : &pc->check_contract.order_serial,
4269 : &paid,
4270 : NULL,
4271 : &pc->check_contract.pos_key,
4272 : &pc->check_contract.pos_alg);
4273 45 : if (0 > qs)
4274 : {
4275 : /* single, read-only SQL statements should never cause
4276 : serialization problems */
4277 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
4278 : /* Always report on hard error to enable diagnostics */
4279 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
4280 0 : pay_end (pc,
4281 : TALER_MHD_reply_with_error (
4282 : pc->connection,
4283 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4284 : TALER_EC_GENERIC_DB_FETCH_FAILED,
4285 : "contract terms"));
4286 4 : return;
4287 : }
4288 45 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
4289 : {
4290 0 : pay_end (pc,
4291 : TALER_MHD_reply_with_error (
4292 : pc->connection,
4293 : MHD_HTTP_NOT_FOUND,
4294 : TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
4295 : pc->order_id));
4296 0 : return;
4297 : }
4298 : /* hash contract (needed later) */
4299 : #if DEBUG
4300 : json_dumpf (pc->check_contract.contract_terms_json,
4301 : stderr,
4302 : JSON_INDENT (2));
4303 : #endif
4304 45 : if (GNUNET_OK !=
4305 45 : TALER_JSON_contract_hash (pc->check_contract.contract_terms_json,
4306 : &pc->check_contract.h_contract_terms))
4307 : {
4308 0 : GNUNET_break (0);
4309 0 : pay_end (pc,
4310 : TALER_MHD_reply_with_error (
4311 : pc->connection,
4312 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4313 : TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
4314 : NULL));
4315 0 : return;
4316 : }
4317 :
4318 : /* Parse the contract terms even for paid orders,
4319 : as later phases need it. */
4320 :
4321 45 : pc->check_contract.contract_terms = TALER_MERCHANT_contract_parse (
4322 : pc->check_contract.contract_terms_json,
4323 : true);
4324 :
4325 45 : if (NULL == pc->check_contract.contract_terms)
4326 : {
4327 : /* invalid contract */
4328 0 : GNUNET_break (0);
4329 0 : pay_end (pc,
4330 : TALER_MHD_reply_with_error (
4331 : pc->connection,
4332 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4333 : TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
4334 : pc->order_id));
4335 0 : return;
4336 : }
4337 :
4338 45 : if (paid)
4339 : {
4340 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4341 : "Order `%s' paid, checking for double-payment\n",
4342 : pc->order_id);
4343 4 : pc->phase = PP_CONTRACT_PAID;
4344 4 : return;
4345 : }
4346 41 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4347 : "Handling payment for order `%s' with contract hash `%s'\n",
4348 : pc->order_id,
4349 : GNUNET_h2s (&pc->check_contract.h_contract_terms.hash));
4350 :
4351 : /* Check fundamentals */
4352 : {
4353 41 : switch (pc->check_contract.contract_terms->version)
4354 : {
4355 35 : case TALER_MERCHANT_CONTRACT_VERSION_0:
4356 : {
4357 35 : if (pc->parse_wallet_data.choice_index > 0)
4358 : {
4359 0 : GNUNET_break (0);
4360 0 : pay_end (pc,
4361 : TALER_MHD_reply_with_error (
4362 : pc->connection,
4363 : MHD_HTTP_BAD_REQUEST,
4364 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_OUT_OF_BOUNDS,
4365 : "contract terms v0 has no choices"));
4366 0 : return;
4367 : }
4368 : }
4369 35 : break;
4370 6 : case TALER_MERCHANT_CONTRACT_VERSION_1:
4371 : {
4372 6 : if (pc->parse_wallet_data.choice_index < 0)
4373 : {
4374 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4375 : "Order `%s' has non-empty choices array but"
4376 : "request is missing 'choice_index' field\n",
4377 : pc->order_id);
4378 0 : GNUNET_break (0);
4379 0 : pay_end (pc,
4380 : TALER_MHD_reply_with_error (
4381 : pc->connection,
4382 : MHD_HTTP_BAD_REQUEST,
4383 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_MISSING,
4384 : NULL));
4385 0 : return;
4386 : }
4387 6 : if (pc->parse_wallet_data.choice_index >=
4388 6 : pc->check_contract.contract_terms->details.v1.choices_len)
4389 : {
4390 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4391 : "Order `%s' has choices array with %u elements but "
4392 : "request has 'choice_index' field with value %d\n",
4393 : pc->order_id,
4394 : pc->check_contract.contract_terms->details.v1.choices_len,
4395 : pc->parse_wallet_data.choice_index);
4396 0 : GNUNET_break (0);
4397 0 : pay_end (pc,
4398 : TALER_MHD_reply_with_error (
4399 : pc->connection,
4400 : MHD_HTTP_BAD_REQUEST,
4401 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_CHOICE_INDEX_OUT_OF_BOUNDS,
4402 : NULL));
4403 0 : return;
4404 : }
4405 : }
4406 6 : break;
4407 0 : default:
4408 0 : GNUNET_break (0);
4409 0 : pay_end (pc,
4410 : TALER_MHD_reply_with_error (
4411 : pc->connection,
4412 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4413 : TALER_EC_GENERIC_DB_FETCH_FAILED,
4414 : "contract 'version' in database not supported by this backend")
4415 : );
4416 0 : return;
4417 : }
4418 : }
4419 :
4420 41 : if (GNUNET_TIME_timestamp_cmp (pc->check_contract.contract_terms->
4421 : wire_deadline,
4422 : <,
4423 : pc->check_contract.contract_terms->
4424 : refund_deadline))
4425 : {
4426 : /* This should already have been checked when creating the order! */
4427 0 : GNUNET_break (0);
4428 0 : pay_end (pc,
4429 : TALER_MHD_reply_with_error (
4430 : pc->connection,
4431 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4432 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUND_DEADLINE_PAST_WIRE_TRANSFER_DEADLINE,
4433 : NULL));
4434 0 : return;
4435 : }
4436 41 : if (GNUNET_TIME_absolute_is_past (pc->check_contract.contract_terms->
4437 : pay_deadline.abs_time))
4438 : {
4439 : /* too late */
4440 0 : pay_end (pc,
4441 : TALER_MHD_reply_with_error (
4442 : pc->connection,
4443 : MHD_HTTP_GONE,
4444 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_OFFER_EXPIRED,
4445 : NULL));
4446 0 : return;
4447 : }
4448 :
4449 : /* Make sure wire method (still) exists for this instance */
4450 : {
4451 : struct TMH_WireMethod *wm;
4452 :
4453 41 : wm = pc->hc->instance->wm_head;
4454 42 : while (0 != GNUNET_memcmp (&pc->check_contract.contract_terms->h_wire,
4455 : &wm->h_wire))
4456 1 : wm = wm->next;
4457 41 : if (NULL == wm)
4458 : {
4459 0 : GNUNET_break (0);
4460 0 : pay_end (pc,
4461 : TALER_MHD_reply_with_error (
4462 : pc->connection,
4463 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4464 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_HASH_UNKNOWN,
4465 : NULL));
4466 0 : return;
4467 : }
4468 41 : pc->check_contract.wm = wm;
4469 : }
4470 41 : pc->phase = PP_VALIDATE_TOKENS;
4471 : }
4472 :
4473 :
4474 : /**
4475 : * Try to parse the wallet_data object of the pay request into
4476 : * the given context. Schedules an error response in the connection
4477 : * on failure.
4478 : *
4479 : * @param[in,out] pc context we use to handle the payment
4480 : */
4481 : static void
4482 45 : phase_parse_wallet_data (struct PayContext *pc)
4483 : {
4484 : const json_t *tokens_evs;
4485 : const json_t *donau_obj;
4486 :
4487 : struct GNUNET_JSON_Specification spec[] = {
4488 45 : GNUNET_JSON_spec_mark_optional (
4489 : GNUNET_JSON_spec_int16 ("choice_index",
4490 : &pc->parse_wallet_data.choice_index),
4491 : NULL),
4492 45 : GNUNET_JSON_spec_mark_optional (
4493 : GNUNET_JSON_spec_array_const ("tokens_evs",
4494 : &tokens_evs),
4495 : NULL),
4496 45 : GNUNET_JSON_spec_mark_optional (
4497 : GNUNET_JSON_spec_object_const ("donau",
4498 : &donau_obj),
4499 : NULL),
4500 45 : GNUNET_JSON_spec_end ()
4501 : };
4502 :
4503 45 : pc->parse_wallet_data.choice_index = -1;
4504 45 : if (NULL == pc->parse_pay.wallet_data)
4505 : {
4506 39 : pc->phase = PP_CHECK_CONTRACT;
4507 39 : return;
4508 : }
4509 : {
4510 : enum GNUNET_GenericReturnValue res;
4511 :
4512 6 : res = TALER_MHD_parse_json_data (pc->connection,
4513 : pc->parse_pay.wallet_data,
4514 : spec);
4515 6 : if (GNUNET_YES != res)
4516 : {
4517 0 : GNUNET_break_op (0);
4518 0 : pay_end (pc,
4519 : (GNUNET_NO == res)
4520 : ? MHD_YES
4521 : : MHD_NO);
4522 0 : return;
4523 : }
4524 : }
4525 :
4526 : pc->parse_wallet_data.token_envelopes_cnt
4527 6 : = json_array_size (tokens_evs);
4528 6 : if (pc->parse_wallet_data.token_envelopes_cnt >
4529 : MAX_TOKEN_ALLOWED_OUTPUTs)
4530 : {
4531 0 : GNUNET_break_op (0);
4532 0 : pay_end (pc,
4533 : TALER_MHD_reply_with_error (
4534 : pc->connection,
4535 : MHD_HTTP_BAD_REQUEST,
4536 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
4537 : "'tokens_evs' array too long"));
4538 0 : return;
4539 : }
4540 : pc->parse_wallet_data.token_envelopes
4541 6 : = GNUNET_new_array (pc->parse_wallet_data.token_envelopes_cnt,
4542 : struct TokenEnvelope);
4543 :
4544 : {
4545 : unsigned int tokens_ev_index;
4546 : json_t *token_ev;
4547 :
4548 12 : json_array_foreach (tokens_evs,
4549 : tokens_ev_index,
4550 : token_ev)
4551 : {
4552 6 : struct TokenEnvelope *ev
4553 6 : = &pc->parse_wallet_data.token_envelopes[tokens_ev_index];
4554 : struct GNUNET_JSON_Specification ispec[] = {
4555 6 : TALER_JSON_spec_token_envelope (NULL,
4556 : &ev->blinded_token),
4557 6 : GNUNET_JSON_spec_end ()
4558 : };
4559 : enum GNUNET_GenericReturnValue res;
4560 :
4561 6 : if (json_is_null (token_ev))
4562 0 : continue;
4563 6 : res = TALER_MHD_parse_json_data (pc->connection,
4564 : token_ev,
4565 : ispec);
4566 6 : if (GNUNET_YES != res)
4567 : {
4568 0 : GNUNET_break_op (0);
4569 0 : pay_end (pc,
4570 : (GNUNET_NO == res)
4571 : ? MHD_YES
4572 : : MHD_NO);
4573 0 : return;
4574 : }
4575 :
4576 6 : for (unsigned int j = 0; j<tokens_ev_index; j++)
4577 : {
4578 0 : if (0 ==
4579 0 : GNUNET_memcmp (ev->blinded_token.blinded_pub,
4580 : pc->parse_wallet_data.token_envelopes[j].
4581 : blinded_token.blinded_pub))
4582 : {
4583 0 : GNUNET_break_op (0);
4584 0 : pay_end (pc,
4585 : TALER_MHD_reply_with_error (
4586 : pc->connection,
4587 : MHD_HTTP_BAD_REQUEST,
4588 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
4589 : "duplicate token envelope in list"));
4590 0 : return;
4591 : }
4592 : }
4593 : }
4594 : }
4595 :
4596 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
4597 : if (NULL != donau_obj)
4598 : {
4599 : const char *donau_url_tmp;
4600 : const json_t *budikeypairs;
4601 : json_t *donau_keys_json;
4602 :
4603 : /* Fetching and checking that all 3 are present in some way */
4604 : struct GNUNET_JSON_Specification dspec[] = {
4605 : GNUNET_JSON_spec_string ("url",
4606 : &donau_url_tmp),
4607 : GNUNET_JSON_spec_uint64 ("year",
4608 : &pc->parse_wallet_data.donau.donation_year),
4609 : GNUNET_JSON_spec_array_const ("budikeypairs",
4610 : &budikeypairs),
4611 : GNUNET_JSON_spec_end ()
4612 : };
4613 : enum GNUNET_GenericReturnValue res;
4614 :
4615 : res = TALER_MHD_parse_json_data (pc->connection,
4616 : donau_obj,
4617 : dspec);
4618 : if (GNUNET_YES != res)
4619 : {
4620 : GNUNET_break_op (0);
4621 : pay_end (pc,
4622 : (GNUNET_NO == res)
4623 : ? MHD_YES
4624 : : MHD_NO);
4625 : return;
4626 : }
4627 :
4628 : /* Check if the needed data is present for the given donau URL */
4629 : {
4630 : enum GNUNET_DB_QueryStatus qs;
4631 :
4632 : qs = TMH_db->lookup_order_charity (
4633 : TMH_db->cls,
4634 : pc->hc->instance->settings.id,
4635 : donau_url_tmp,
4636 : &pc->parse_wallet_data.charity_id,
4637 : &pc->parse_wallet_data.charity_priv,
4638 : &pc->parse_wallet_data.charity_max_per_year,
4639 : &pc->parse_wallet_data.charity_receipts_to_date,
4640 : &donau_keys_json,
4641 : &pc->parse_wallet_data.donau_instance_serial);
4642 :
4643 : switch (qs)
4644 : {
4645 : case GNUNET_DB_STATUS_HARD_ERROR:
4646 : case GNUNET_DB_STATUS_SOFT_ERROR:
4647 : TMH_db->rollback (TMH_db->cls);
4648 : pay_end (pc,
4649 : TALER_MHD_reply_with_error (
4650 : pc->connection,
4651 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4652 : TALER_EC_GENERIC_DB_FETCH_FAILED,
4653 : "lookup_order_charity"));
4654 : return;
4655 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
4656 : TMH_db->rollback (TMH_db->cls);
4657 : pay_end (pc,
4658 : TALER_MHD_reply_with_error (
4659 : pc->connection,
4660 : MHD_HTTP_NOT_FOUND,
4661 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
4662 : "No matching Donau charity found for the given URL"));
4663 : return;
4664 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
4665 : pc->parse_wallet_data.donau.donau_url =
4666 : GNUNET_strdup (donau_url_tmp);
4667 : break;
4668 : }
4669 : }
4670 :
4671 : {
4672 : pc->parse_wallet_data.donau_keys =
4673 : DONAU_keys_from_json (donau_keys_json);
4674 : json_decref (donau_keys_json);
4675 : if (NULL == pc->parse_wallet_data.donau_keys)
4676 : {
4677 : GNUNET_break_op (0);
4678 : pay_end (pc,
4679 : TALER_MHD_reply_with_error (pc->connection,
4680 : MHD_HTTP_BAD_REQUEST,
4681 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
4682 : "Invalid donau_keys"));
4683 : return;
4684 : }
4685 : }
4686 :
4687 : /* Stage to parse the budikeypairs from json to struct */
4688 : if (0 != json_array_size (budikeypairs))
4689 : {
4690 : size_t num_bkps = json_array_size (budikeypairs);
4691 : struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps =
4692 : GNUNET_new_array (num_bkps,
4693 : struct DONAU_BlindedUniqueDonorIdentifierKeyPair);
4694 :
4695 : /* Change to json for each*/
4696 : for (size_t i = 0; i < num_bkps; i++)
4697 : {
4698 : const json_t *bkp_obj = json_array_get (budikeypairs,
4699 : i);
4700 : if (GNUNET_SYSERR ==
4701 : merchant_parse_json_bkp (&bkps[i],
4702 : bkp_obj))
4703 : {
4704 : GNUNET_break_op (0);
4705 : for (size_t j = 0; i < j; j++)
4706 : GNUNET_CRYPTO_blinded_message_decref (
4707 : bkps[j].blinded_udi.blinded_message);
4708 : GNUNET_free (bkps);
4709 : pay_end (pc,
4710 : TALER_MHD_reply_with_error (pc->connection,
4711 : MHD_HTTP_BAD_REQUEST,
4712 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
4713 : "Failed to parse budikeypairs"));
4714 : return;
4715 : }
4716 : }
4717 :
4718 : pc->parse_wallet_data.num_bkps = num_bkps;
4719 : pc->parse_wallet_data.bkps = bkps;
4720 : }
4721 : }
4722 : #else
4723 : /* Donau not compiled in: reject request if a donau object was given. */
4724 6 : if (NULL != donau_obj)
4725 : {
4726 0 : pay_end (pc,
4727 : TALER_MHD_reply_with_error (pc->connection,
4728 : MHD_HTTP_NOT_IMPLEMENTED,
4729 : TALER_EC_MERCHANT_GENERIC_DONAU_NOT_CONFIGURED,
4730 : "donau support disabled"));
4731 0 : return;
4732 : }
4733 : #endif /* HAVE_DONAU_DONAU_SERVICE_H */
4734 :
4735 6 : TALER_json_hash (pc->parse_pay.wallet_data,
4736 : &pc->parse_wallet_data.h_wallet_data);
4737 :
4738 6 : pc->phase = PP_CHECK_CONTRACT;
4739 : }
4740 :
4741 :
4742 : /**
4743 : * Try to parse the pay request into the given pay context.
4744 : * Schedules an error response in the connection on failure.
4745 : *
4746 : * @param[in,out] pc context we use to handle the payment
4747 : */
4748 : static void
4749 45 : phase_parse_pay (struct PayContext *pc)
4750 : {
4751 45 : const char *session_id = NULL;
4752 : const json_t *coins;
4753 : const json_t *tokens;
4754 : struct GNUNET_JSON_Specification spec[] = {
4755 45 : GNUNET_JSON_spec_array_const ("coins",
4756 : &coins),
4757 45 : GNUNET_JSON_spec_mark_optional (
4758 : GNUNET_JSON_spec_string ("session_id",
4759 : &session_id),
4760 : NULL),
4761 45 : GNUNET_JSON_spec_mark_optional (
4762 : GNUNET_JSON_spec_object_const ("wallet_data",
4763 : &pc->parse_pay.wallet_data),
4764 : NULL),
4765 45 : GNUNET_JSON_spec_mark_optional (
4766 : GNUNET_JSON_spec_array_const ("tokens",
4767 : &tokens),
4768 : NULL),
4769 45 : GNUNET_JSON_spec_end ()
4770 : };
4771 :
4772 : #if DEBUG
4773 : {
4774 : char *dump = json_dumps (pc->hc->request_body,
4775 : JSON_INDENT (2)
4776 : | JSON_ENCODE_ANY
4777 : | JSON_SORT_KEYS);
4778 :
4779 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4780 : "POST /orders/%s/pay – request body follows:\n%s\n",
4781 : pc->order_id,
4782 : dump);
4783 :
4784 : free (dump);
4785 :
4786 : }
4787 : #endif /* DEBUG */
4788 :
4789 45 : GNUNET_assert (PP_PARSE_PAY == pc->phase);
4790 : {
4791 : enum GNUNET_GenericReturnValue res;
4792 :
4793 45 : res = TALER_MHD_parse_json_data (pc->connection,
4794 45 : pc->hc->request_body,
4795 : spec);
4796 45 : if (GNUNET_YES != res)
4797 : {
4798 0 : GNUNET_break_op (0);
4799 0 : pay_end (pc,
4800 : (GNUNET_NO == res)
4801 : ? MHD_YES
4802 : : MHD_NO);
4803 0 : return;
4804 : }
4805 : }
4806 :
4807 : /* copy session ID (if set) */
4808 45 : if (NULL != session_id)
4809 : {
4810 19 : pc->parse_pay.session_id = GNUNET_strdup (session_id);
4811 : }
4812 : else
4813 : {
4814 : /* use empty string as default if client didn't specify it */
4815 26 : pc->parse_pay.session_id = GNUNET_strdup ("");
4816 : }
4817 :
4818 45 : pc->parse_pay.coins_cnt = json_array_size (coins);
4819 45 : if (pc->parse_pay.coins_cnt > MAX_COIN_ALLOWED_COINS)
4820 : {
4821 0 : GNUNET_break_op (0);
4822 0 : pay_end (pc,
4823 : TALER_MHD_reply_with_error (
4824 : pc->connection,
4825 : MHD_HTTP_BAD_REQUEST,
4826 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
4827 : "'coins' array too long"));
4828 0 : return;
4829 : }
4830 : /* note: 1 coin = 1 deposit confirmation expected */
4831 45 : pc->parse_pay.dc = GNUNET_new_array (pc->parse_pay.coins_cnt,
4832 : struct DepositConfirmation);
4833 :
4834 : /* This loop populates the array 'dc' in 'pc' */
4835 : {
4836 : unsigned int coins_index;
4837 : json_t *coin;
4838 :
4839 96 : json_array_foreach (coins, coins_index, coin)
4840 : {
4841 51 : struct DepositConfirmation *dc = &pc->parse_pay.dc[coins_index];
4842 : const char *exchange_url;
4843 : struct GNUNET_JSON_Specification ispec[] = {
4844 51 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
4845 : &dc->cdd.coin_sig),
4846 51 : GNUNET_JSON_spec_fixed_auto ("coin_pub",
4847 : &dc->cdd.coin_pub),
4848 51 : TALER_JSON_spec_denom_sig ("ub_sig",
4849 : &dc->cdd.denom_sig),
4850 51 : GNUNET_JSON_spec_fixed_auto ("h_denom",
4851 : &dc->cdd.h_denom_pub),
4852 51 : TALER_JSON_spec_amount_any ("contribution",
4853 : &dc->cdd.amount),
4854 51 : TALER_JSON_spec_web_url ("exchange_url",
4855 : &exchange_url),
4856 : /* if a minimum age was required, the minimum_age_sig and
4857 : * age_commitment must be provided */
4858 51 : GNUNET_JSON_spec_mark_optional (
4859 51 : GNUNET_JSON_spec_fixed_auto ("minimum_age_sig",
4860 : &dc->minimum_age_sig),
4861 : &dc->no_minimum_age_sig),
4862 51 : GNUNET_JSON_spec_mark_optional (
4863 : TALER_JSON_spec_age_commitment ("age_commitment",
4864 : &dc->age_commitment),
4865 : &dc->no_age_commitment),
4866 : /* if minimum age was not required, but coin with age restriction set
4867 : * was used, h_age_commitment must be provided. */
4868 51 : GNUNET_JSON_spec_mark_optional (
4869 51 : GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
4870 : &dc->cdd.h_age_commitment),
4871 : &dc->no_h_age_commitment),
4872 51 : GNUNET_JSON_spec_end ()
4873 : };
4874 : enum GNUNET_GenericReturnValue res;
4875 51 : struct ExchangeGroup *eg = NULL;
4876 :
4877 51 : res = TALER_MHD_parse_json_data (pc->connection,
4878 : coin,
4879 : ispec);
4880 51 : if (GNUNET_YES != res)
4881 : {
4882 0 : GNUNET_break_op (0);
4883 0 : pay_end (pc,
4884 : (GNUNET_NO == res)
4885 : ? MHD_YES
4886 : : MHD_NO);
4887 0 : return;
4888 : }
4889 61 : for (unsigned int j = 0; j<coins_index; j++)
4890 : {
4891 10 : if (0 ==
4892 10 : GNUNET_memcmp (&dc->cdd.coin_pub,
4893 : &pc->parse_pay.dc[j].cdd.coin_pub))
4894 : {
4895 0 : GNUNET_break_op (0);
4896 0 : pay_end (pc,
4897 : TALER_MHD_reply_with_error (pc->connection,
4898 : MHD_HTTP_BAD_REQUEST,
4899 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
4900 : "duplicate coin in list"));
4901 0 : return;
4902 : }
4903 : }
4904 :
4905 51 : dc->exchange_url = GNUNET_strdup (exchange_url);
4906 51 : dc->index = coins_index;
4907 51 : dc->pc = pc;
4908 :
4909 : /* Check the consistency of the (potential) age restriction
4910 : * information. */
4911 51 : if (dc->no_age_commitment != dc->no_minimum_age_sig)
4912 : {
4913 0 : GNUNET_break_op (0);
4914 0 : pay_end (pc,
4915 : TALER_MHD_reply_with_error (
4916 : pc->connection,
4917 : MHD_HTTP_BAD_REQUEST,
4918 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
4919 : "inconsistent: 'age_commitment' vs. 'minimum_age_sig'"
4920 : ));
4921 0 : return;
4922 : }
4923 :
4924 : /* Setup exchange group */
4925 51 : for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
4926 : {
4927 10 : if (0 ==
4928 10 : strcmp (pc->parse_pay.egs[i]->exchange_url,
4929 : exchange_url))
4930 : {
4931 10 : eg = pc->parse_pay.egs[i];
4932 10 : break;
4933 : }
4934 : }
4935 51 : if (NULL == eg)
4936 : {
4937 41 : eg = GNUNET_new (struct ExchangeGroup);
4938 41 : eg->pc = pc;
4939 41 : eg->exchange_url = dc->exchange_url;
4940 41 : eg->total = dc->cdd.amount;
4941 41 : GNUNET_array_append (pc->parse_pay.egs,
4942 : pc->parse_pay.num_exchanges,
4943 : eg);
4944 : }
4945 : else
4946 : {
4947 10 : if (0 >
4948 10 : TALER_amount_add (&eg->total,
4949 10 : &eg->total,
4950 10 : &dc->cdd.amount))
4951 : {
4952 0 : GNUNET_break_op (0);
4953 0 : pay_end (pc,
4954 : TALER_MHD_reply_with_error (
4955 : pc->connection,
4956 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4957 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
4958 : "Overflow adding up amounts"));
4959 0 : return;
4960 : }
4961 : }
4962 : }
4963 : }
4964 :
4965 45 : pc->parse_pay.tokens_cnt = json_array_size (tokens);
4966 45 : if (pc->parse_pay.tokens_cnt > MAX_TOKEN_ALLOWED_INPUTs)
4967 : {
4968 0 : GNUNET_break_op (0);
4969 0 : pay_end (pc,
4970 : TALER_MHD_reply_with_error (
4971 : pc->connection,
4972 : MHD_HTTP_BAD_REQUEST,
4973 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
4974 : "'tokens' array too long"));
4975 0 : return;
4976 : }
4977 :
4978 45 : pc->parse_pay.tokens = GNUNET_new_array (pc->parse_pay.tokens_cnt,
4979 : struct TokenUseConfirmation);
4980 :
4981 : /* This look populates the array 'tokens' in 'pc' */
4982 : {
4983 : unsigned int tokens_index;
4984 : json_t *token;
4985 :
4986 49 : json_array_foreach (tokens, tokens_index, token)
4987 : {
4988 4 : struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[tokens_index];
4989 : struct GNUNET_JSON_Specification ispec[] = {
4990 4 : GNUNET_JSON_spec_fixed_auto ("token_sig",
4991 : &tuc->sig),
4992 4 : GNUNET_JSON_spec_fixed_auto ("token_pub",
4993 : &tuc->pub),
4994 4 : GNUNET_JSON_spec_fixed_auto ("h_issue",
4995 : &tuc->h_issue),
4996 4 : TALER_JSON_spec_token_issue_sig ("ub_sig",
4997 : &tuc->unblinded_sig),
4998 4 : GNUNET_JSON_spec_end ()
4999 : };
5000 : enum GNUNET_GenericReturnValue res;
5001 :
5002 4 : res = TALER_MHD_parse_json_data (pc->connection,
5003 : token,
5004 : ispec);
5005 4 : if (GNUNET_YES != res)
5006 : {
5007 0 : GNUNET_break_op (0);
5008 0 : pay_end (pc,
5009 : (GNUNET_NO == res)
5010 : ? MHD_YES
5011 : : MHD_NO);
5012 0 : return;
5013 : }
5014 :
5015 4 : for (unsigned int j = 0; j<tokens_index; j++)
5016 : {
5017 0 : if (0 ==
5018 0 : GNUNET_memcmp (&tuc->pub,
5019 : &pc->parse_pay.tokens[j].pub))
5020 : {
5021 0 : GNUNET_break_op (0);
5022 0 : pay_end (pc,
5023 : TALER_MHD_reply_with_error (pc->connection,
5024 : MHD_HTTP_BAD_REQUEST,
5025 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
5026 : "duplicate token in list"));
5027 0 : return;
5028 : }
5029 : }
5030 : }
5031 : }
5032 :
5033 45 : pc->phase = PP_PARSE_WALLET_DATA;
5034 : }
5035 :
5036 :
5037 : /**
5038 : * Custom cleanup routine for a `struct PayContext`.
5039 : *
5040 : * @param cls the `struct PayContext` to clean up.
5041 : */
5042 : static void
5043 45 : pay_context_cleanup (void *cls)
5044 : {
5045 45 : struct PayContext *pc = cls;
5046 :
5047 45 : if (NULL != pc->batch_deposits.timeout_task)
5048 : {
5049 31 : GNUNET_SCHEDULER_cancel (pc->batch_deposits.timeout_task);
5050 31 : pc->batch_deposits.timeout_task = NULL;
5051 : }
5052 45 : if (NULL != pc->check_contract.contract_terms_json)
5053 : {
5054 45 : json_decref (pc->check_contract.contract_terms_json);
5055 45 : pc->check_contract.contract_terms_json = NULL;
5056 : }
5057 96 : for (unsigned int i = 0; i<pc->parse_pay.coins_cnt; i++)
5058 : {
5059 51 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
5060 :
5061 51 : TALER_denom_sig_free (&dc->cdd.denom_sig);
5062 51 : GNUNET_free (dc->exchange_url);
5063 : }
5064 45 : GNUNET_free (pc->parse_pay.dc);
5065 49 : for (unsigned int i = 0; i<pc->parse_pay.tokens_cnt; i++)
5066 : {
5067 4 : struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[i];
5068 :
5069 4 : TALER_token_issue_sig_free (&tuc->unblinded_sig);
5070 : }
5071 45 : GNUNET_free (pc->parse_pay.tokens);
5072 86 : for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
5073 : {
5074 41 : struct ExchangeGroup *eg = pc->parse_pay.egs[i];
5075 :
5076 41 : if (NULL != eg->fo)
5077 0 : TMH_EXCHANGES_keys4exchange_cancel (eg->fo);
5078 41 : GNUNET_free (eg);
5079 : }
5080 45 : GNUNET_free (pc->parse_pay.egs);
5081 45 : if (NULL != pc->check_contract.contract_terms)
5082 : {
5083 45 : TALER_MERCHANT_contract_free (pc->check_contract.contract_terms);
5084 45 : pc->check_contract.contract_terms = NULL;
5085 : }
5086 45 : if (NULL != pc->response)
5087 : {
5088 6 : MHD_destroy_response (pc->response);
5089 6 : pc->response = NULL;
5090 : }
5091 45 : GNUNET_free (pc->parse_pay.session_id);
5092 45 : GNUNET_CONTAINER_DLL_remove (pc_head,
5093 : pc_tail,
5094 : pc);
5095 45 : GNUNET_free (pc->check_contract.pos_key);
5096 45 : GNUNET_free (pc->compute_money_pots.pots);
5097 45 : GNUNET_free (pc->compute_money_pots.increments);
5098 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
5099 : if (NULL != pc->parse_wallet_data.bkps)
5100 : {
5101 : for (size_t i = 0; i < pc->parse_wallet_data.num_bkps; i++)
5102 : GNUNET_CRYPTO_blinded_message_decref (
5103 : pc->parse_wallet_data.bkps[i].blinded_udi.blinded_message);
5104 : GNUNET_array_grow (pc->parse_wallet_data.bkps,
5105 : pc->parse_wallet_data.num_bkps,
5106 : 0);
5107 : }
5108 : if (NULL != pc->parse_wallet_data.donau_keys)
5109 : {
5110 : DONAU_keys_decref (pc->parse_wallet_data.donau_keys);
5111 : pc->parse_wallet_data.donau_keys = NULL;
5112 : }
5113 : GNUNET_free (pc->parse_wallet_data.donau.donau_url);
5114 : #endif
5115 51 : for (unsigned int i = 0; i<pc->parse_wallet_data.token_envelopes_cnt; i++)
5116 : {
5117 6 : struct TokenEnvelope *ev
5118 6 : = &pc->parse_wallet_data.token_envelopes[i];
5119 :
5120 6 : GNUNET_CRYPTO_blinded_message_decref (ev->blinded_token.blinded_pub);
5121 : }
5122 45 : GNUNET_free (pc->parse_wallet_data.token_envelopes);
5123 45 : if (NULL != pc->output_tokens)
5124 : {
5125 12 : for (unsigned int i = 0; i<pc->output_tokens_len; i++)
5126 6 : if (NULL != pc->output_tokens[i].sig.signature)
5127 6 : GNUNET_CRYPTO_blinded_sig_decref (pc->output_tokens[i].sig.signature);
5128 6 : GNUNET_free (pc->output_tokens);
5129 6 : pc->output_tokens = NULL;
5130 : }
5131 45 : GNUNET_free (pc);
5132 45 : }
5133 :
5134 :
5135 : MHD_RESULT
5136 82 : TMH_post_orders_ID_pay (const struct TMH_RequestHandler *rh,
5137 : struct MHD_Connection *connection,
5138 : struct TMH_HandlerContext *hc)
5139 : {
5140 82 : struct PayContext *pc = hc->ctx;
5141 :
5142 82 : GNUNET_assert (NULL != hc->infix);
5143 82 : if (NULL == pc)
5144 : {
5145 45 : pc = GNUNET_new (struct PayContext);
5146 45 : pc->connection = connection;
5147 45 : pc->hc = hc;
5148 45 : pc->order_id = hc->infix;
5149 45 : hc->ctx = pc;
5150 45 : hc->cc = &pay_context_cleanup;
5151 45 : GNUNET_CONTAINER_DLL_insert (pc_head,
5152 : pc_tail,
5153 : pc);
5154 : }
5155 : while (1)
5156 : {
5157 994 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
5158 : "Processing /pay in phase %d\n",
5159 : (int) pc->phase);
5160 538 : switch (pc->phase)
5161 : {
5162 45 : case PP_PARSE_PAY:
5163 45 : phase_parse_pay (pc);
5164 45 : break;
5165 45 : case PP_PARSE_WALLET_DATA:
5166 45 : phase_parse_wallet_data (pc);
5167 45 : break;
5168 45 : case PP_CHECK_CONTRACT:
5169 45 : phase_check_contract (pc);
5170 45 : break;
5171 41 : case PP_VALIDATE_TOKENS:
5172 41 : phase_validate_tokens (pc);
5173 41 : break;
5174 4 : case PP_CONTRACT_PAID:
5175 4 : phase_contract_paid (pc);
5176 4 : break;
5177 72 : case PP_COMPUTE_MONEY_POTS:
5178 72 : phase_compute_money_pots (pc);
5179 72 : break;
5180 72 : case PP_PAY_TRANSACTION:
5181 72 : phase_execute_pay_transaction (pc);
5182 72 : break;
5183 31 : case PP_REQUEST_DONATION_RECEIPT:
5184 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
5185 : phase_request_donation_receipt (pc);
5186 : #else
5187 31 : pc->phase++;
5188 : #endif
5189 31 : break;
5190 31 : case PP_FINAL_OUTPUT_TOKEN_PROCESSING:
5191 31 : phase_final_output_token_processing (pc);
5192 31 : break;
5193 31 : case PP_PAYMENT_NOTIFICATION:
5194 31 : phase_payment_notification (pc);
5195 31 : break;
5196 33 : case PP_SUCCESS_RESPONSE:
5197 33 : phase_success_response (pc);
5198 33 : break;
5199 37 : case PP_BATCH_DEPOSITS:
5200 37 : phase_batch_deposits (pc);
5201 37 : break;
5202 6 : case PP_RETURN_RESPONSE:
5203 6 : phase_return_response (pc);
5204 6 : break;
5205 0 : case PP_FAIL_LEGAL_REASONS:
5206 0 : phase_fail_for_legal_reasons (pc);
5207 0 : break;
5208 45 : case PP_END_YES:
5209 45 : return MHD_YES;
5210 0 : case PP_END_NO:
5211 0 : return MHD_NO;
5212 0 : default:
5213 : /* should not be reachable */
5214 0 : GNUNET_assert (0);
5215 : return MHD_NO;
5216 : }
5217 493 : switch (pc->suspended)
5218 : {
5219 0 : case GNUNET_SYSERR:
5220 : /* during shutdown, we don't generate any more replies */
5221 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
5222 : "Processing /pay ends due to shutdown in phase %d\n",
5223 : (int) pc->phase);
5224 0 : return MHD_NO;
5225 456 : case GNUNET_NO:
5226 : /* continue to next phase */
5227 456 : break;
5228 37 : case GNUNET_YES:
5229 37 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
5230 : "Processing /pay suspended in phase %d\n",
5231 : (int) pc->phase);
5232 37 : return MHD_YES;
5233 : }
5234 : }
5235 : /* impossible to get here */
5236 : GNUNET_assert (0);
5237 : return MHD_YES;
5238 : }
5239 :
5240 :
5241 : /* end of taler-merchant-httpd_post-orders-ID-pay.c */
|