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