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