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 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
129 : /**
130 : * Communicate with DONAU to generate a donation receipt from the donor BUDIs.
131 : */
132 : PP_REQUEST_DONATION_RECEIPT,
133 : #endif
134 :
135 : /**
136 : * Process the donation receipt response from DONAU (save the donau_sigs to the db).
137 : */
138 : PP_FINAL_OUTPUT_TOKEN_PROCESSING,
139 :
140 : /**
141 : * Notify other processes about successful payment.
142 : */
143 : PP_PAYMENT_NOTIFICATION,
144 :
145 : /**
146 : * Create final success response.
147 : */
148 : PP_SUCCESS_RESPONSE,
149 :
150 : /**
151 : * Perform batch deposits with exchange(s).
152 : */
153 : PP_BATCH_DEPOSITS,
154 :
155 : /**
156 : * Return response in payment context.
157 : */
158 : PP_RETURN_RESPONSE,
159 :
160 : /**
161 : * An exchange denied a deposit, fail for
162 : * legal reasons.
163 : */
164 : PP_FAIL_LEGAL_REASONS,
165 :
166 : /**
167 : * Return #MHD_YES to end processing.
168 : */
169 : PP_END_YES,
170 :
171 : /**
172 : * Return #MHD_NO to end processing.
173 : */
174 : PP_END_NO
175 : };
176 :
177 :
178 : /**
179 : * Information kept during a pay request for each coin.
180 : */
181 : struct DepositConfirmation
182 : {
183 :
184 : /**
185 : * Reference to the main PayContext
186 : */
187 : struct PayContext *pc;
188 :
189 : /**
190 : * URL of the exchange that issued this coin.
191 : */
192 : char *exchange_url;
193 :
194 : /**
195 : * Details about the coin being deposited.
196 : */
197 : struct TALER_EXCHANGE_CoinDepositDetail cdd;
198 :
199 : /**
200 : * Fee charged by the exchange for the deposit operation of this coin.
201 : */
202 : struct TALER_Amount deposit_fee;
203 :
204 : /**
205 : * Fee charged by the exchange for the refund operation of this coin.
206 : */
207 : struct TALER_Amount refund_fee;
208 :
209 : /**
210 : * If a minimum age was required (i. e. pc->minimum_age is large enough),
211 : * this is the signature of the minimum age (as a single uint8_t), using the
212 : * private key to the corresponding age group. Might be all zeroes for no
213 : * age attestation.
214 : */
215 : struct TALER_AgeAttestationP minimum_age_sig;
216 :
217 : /**
218 : * If a minimum age was required (i. e. pc->minimum_age is large enough),
219 : * this is the age commitment (i. e. age mask and vector of EdDSA public
220 : * keys, one per age group) that went into the mining of the coin. The
221 : * SHA256 hash of the mask and the vector of public keys was bound to the
222 : * key.
223 : */
224 : struct TALER_AgeCommitment age_commitment;
225 :
226 : /**
227 : * Age mask in the denomination that defines the age groups. Only
228 : * applicable, if minimum age was required.
229 : */
230 : struct TALER_AgeMask age_mask;
231 :
232 : /**
233 : * Offset of this coin into the `dc` array of all coins in the
234 : * @e pc.
235 : */
236 : unsigned int index;
237 :
238 : /**
239 : * true, if no field "age_commitment" was found in the JSON blob
240 : */
241 : bool no_age_commitment;
242 :
243 : /**
244 : * True, if no field "minimum_age_sig" was found in the JSON blob
245 : */
246 : bool no_minimum_age_sig;
247 :
248 : /**
249 : * true, if no field "h_age_commitment" was found in the JSON blob
250 : */
251 : bool no_h_age_commitment;
252 :
253 : /**
254 : * true if we found this coin in the database.
255 : */
256 : bool found_in_db;
257 :
258 : /**
259 : * true if we #deposit_paid_check() matched this coin in the database.
260 : */
261 : bool matched_in_db;
262 :
263 : };
264 :
265 : struct TokenUseConfirmation
266 : {
267 :
268 : /**
269 : * Signature on the deposit request made using the token use private key.
270 : */
271 : struct TALER_TokenUseSignatureP sig;
272 :
273 : /**
274 : * Token use public key. This key was blindly signed by the merchant during
275 : * the token issuance process.
276 : */
277 : struct TALER_TokenUsePublicKeyP pub;
278 :
279 : /**
280 : * Unblinded signature on the token use public key done by the merchant.
281 : */
282 : struct TALER_TokenIssueSignature unblinded_sig;
283 :
284 : /**
285 : * Hash of the token issue public key associated with this token.
286 : * Note this is set in the validate_tokens phase.
287 : */
288 : struct TALER_TokenIssuePublicKeyHashP h_issue;
289 :
290 : /**
291 : * true if we found this token in the database.
292 : */
293 : bool found_in_db;
294 :
295 : };
296 :
297 :
298 : /**
299 : * Information about a token envelope.
300 : */
301 : struct TokenEnvelope
302 : {
303 :
304 : /**
305 : * Blinded token use public keys waiting to be signed.
306 : */
307 : struct TALER_TokenEnvelope blinded_token;
308 :
309 : };
310 :
311 :
312 : /**
313 : * (Blindly) signed token to be returned to the wallet.
314 : */
315 : struct SignedOutputToken
316 : {
317 :
318 : /**
319 : * Index of the output token that produced
320 : * this blindly signed token.
321 : */
322 : unsigned int output_index;
323 :
324 : /**
325 : * Blinded token use public keys waiting to be signed.
326 : */
327 : struct TALER_BlindedTokenIssueSignature sig;
328 :
329 : /**
330 : * Hash of token issue public key.
331 : */
332 : struct TALER_TokenIssuePublicKeyHashP h_issue;
333 :
334 : };
335 :
336 :
337 : /**
338 : * Information kept during a pay request for each exchange.
339 : */
340 : struct ExchangeGroup
341 : {
342 :
343 : /**
344 : * Payment context this group is part of.
345 : */
346 : struct PayContext *pc;
347 :
348 : /**
349 : * Handle to the batch deposit operation we are performing for this
350 : * exchange, NULL after the operation is done.
351 : */
352 : struct TALER_EXCHANGE_BatchDepositHandle *bdh;
353 :
354 : /**
355 : * Handle for operation to lookup /keys (and auditors) from
356 : * the exchange used for this transaction; NULL if no operation is
357 : * pending.
358 : */
359 : struct TMH_EXCHANGES_KeysOperation *fo;
360 :
361 : /**
362 : * URL of the exchange that issued this coin. Aliases
363 : * the exchange URL of one of the coins, do not free!
364 : */
365 : const char *exchange_url;
366 :
367 : /**
368 : * Total deposit amount in this exchange group.
369 : */
370 : struct TALER_Amount total;
371 :
372 : /**
373 : * Wire fee that applies to this exchange for the
374 : * given payment context's wire method.
375 : */
376 : struct TALER_Amount wire_fee;
377 :
378 : /**
379 : * true if we already tried a forced /keys download.
380 : */
381 : bool tried_force_keys;
382 :
383 : /**
384 : * Did this exchange deny the transaction for legal reasons?
385 : */
386 : bool got_451;
387 : };
388 :
389 :
390 : /**
391 : * Information about donau, that can be fetched even
392 : * if the merhchant doesn't support donau
393 : */
394 : struct DonauData
395 : {
396 : /**
397 : * The user-selected Donau URL.
398 : */
399 : char *donau_url;
400 :
401 : /**
402 : * The donation year, as parsed from "year".
403 : */
404 : uint64_t donation_year;
405 :
406 : /**
407 : * The original BUDI key-pairs array from the donor
408 : * to be used for the receipt creation.
409 : */
410 : const json_t *budikeypairs;
411 : };
412 :
413 : /**
414 : * Information we keep for an individual call to the /pay handler.
415 : */
416 : struct PayContext
417 : {
418 :
419 : /**
420 : * Stored in a DLL.
421 : */
422 : struct PayContext *next;
423 :
424 : /**
425 : * Stored in a DLL.
426 : */
427 : struct PayContext *prev;
428 :
429 : /**
430 : * MHD connection to return to
431 : */
432 : struct MHD_Connection *connection;
433 :
434 : /**
435 : * Details about the client's request.
436 : */
437 : struct TMH_HandlerContext *hc;
438 :
439 : /**
440 : * Transaction ID given in @e root.
441 : */
442 : const char *order_id;
443 :
444 : /**
445 : * Response to return, NULL if we don't have one yet.
446 : */
447 : struct MHD_Response *response;
448 :
449 : /**
450 : * Array with @e output_tokens_len signed tokens returned in
451 : * the response to the wallet.
452 : */
453 : struct SignedOutputToken *output_tokens;
454 :
455 : /**
456 : * Number of output tokens to return in the response.
457 : * Length of the @e output_tokens array.
458 : */
459 : unsigned int output_tokens_len;
460 :
461 : /**
462 : * Counter used to generate the output index in append_output_token_sig().
463 : */
464 : unsigned int output_index_gen;
465 :
466 : /**
467 : * Counter used to generate the output index in append_output_token_sig().
468 : *
469 : * Counts the generated tokens _within_ the current output_index_gen.
470 : */
471 : unsigned int output_token_cnt;
472 :
473 : /**
474 : * HTTP status code to use for the reply, i.e 200 for "OK".
475 : * Special value UINT_MAX is used to indicate hard errors
476 : * (no reply, return #MHD_NO).
477 : */
478 : unsigned int response_code;
479 :
480 : /**
481 : * Payment processing phase we are in.
482 : */
483 : enum PayPhase phase;
484 :
485 : /**
486 : * #GNUNET_NO if the @e connection was not suspended,
487 : * #GNUNET_YES if the @e connection was suspended,
488 : * #GNUNET_SYSERR if @e connection was resumed to as
489 : * part of #MH_force_pc_resume during shutdown.
490 : */
491 : enum GNUNET_GenericReturnValue suspended;
492 :
493 : /**
494 : * Results from the phase_parse_pay()
495 : */
496 : struct
497 : {
498 :
499 : /**
500 : * Array with @e num_exchanges exchanges we are depositing
501 : * coins into.
502 : */
503 : struct ExchangeGroup **egs;
504 :
505 : /**
506 : * Array with @e coins_cnt coins we are despositing.
507 : */
508 : struct DepositConfirmation *dc;
509 :
510 : /**
511 : * Array with @e tokens_cnt input tokens passed to this request.
512 : */
513 : struct TokenUseConfirmation *tokens;
514 :
515 : /**
516 : * Optional session id given in @e root.
517 : * NULL if not given.
518 : */
519 : char *session_id;
520 :
521 : /**
522 : * Wallet data json object from the request. Containing additional
523 : * wallet data such as the selected choice_index.
524 : */
525 : const json_t *wallet_data;
526 :
527 : /**
528 : * Number of coins this payment is made of. Length
529 : * of the @e dc array.
530 : */
531 : size_t coins_cnt;
532 :
533 : /**
534 : * Number of input tokens passed to this request. Length
535 : * of the @e tokens array.
536 : */
537 : size_t tokens_cnt;
538 :
539 : /**
540 : * Number of exchanges involved in the payment. Length
541 : * of the @e eg array.
542 : */
543 : unsigned int num_exchanges;
544 :
545 : } parse_pay;
546 :
547 : /**
548 : * Results from the phase_wallet_data()
549 : */
550 : struct
551 : {
552 :
553 : /**
554 : * Array with @e token_envelopes_cnt (blinded) token envelopes.
555 : */
556 : struct TokenEnvelope *token_envelopes;
557 :
558 : /**
559 : * Index of selected choice in the @e contract_terms choices array.
560 : */
561 : int16_t choice_index;
562 :
563 : /**
564 : * Number of token envelopes passed to this request.
565 : * Length of the @e token_envelopes array.
566 : */
567 : size_t token_envelopes_cnt;
568 :
569 : /**
570 : * Hash of the canonicalized wallet data json object.
571 : */
572 : struct GNUNET_HashCode h_wallet_data;
573 :
574 : /**
575 : * Donau related information
576 : */
577 : struct DonauData donau;
578 :
579 : /**
580 : * Serial from the DB of the donau instance that we are using
581 : */
582 : uint64_t donau_instance_serial;
583 :
584 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
585 : /**
586 : * Number of the blinded key pairs @e bkps
587 : */
588 : unsigned int num_bkps;
589 :
590 : /**
591 : * Blinded key pairs received from the wallet
592 : */
593 : struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps;
594 :
595 : /**
596 : * The id of the charity as saved on the donau.
597 : */
598 : uint64_t charity_id;
599 :
600 : /**
601 : * Private key of the charity(related to the private key of the merchant).
602 : */
603 : struct DONAU_CharityPrivateKeyP charity_priv;
604 :
605 : /**
606 : * Maximum amount of donations that the charity can receive per year.
607 : */
608 : struct TALER_Amount charity_max_per_year;
609 :
610 : /**
611 : * Amount of donations that the charity has received so far this year.
612 : */
613 : struct TALER_Amount charity_receipts_to_date;
614 :
615 : /**
616 : * Donau keys, that we are using to get the information about the bkps.
617 : */
618 : struct DONAU_Keys *donau_keys;
619 :
620 : /**
621 : * Amount from BKPS
622 : */
623 : struct TALER_Amount donation_amount;
624 : #endif
625 :
626 : } parse_wallet_data;
627 :
628 : /**
629 : * Results from the phase_check_contract()
630 : */
631 : struct
632 : {
633 :
634 : /**
635 : * Hashed @e contract_terms.
636 : */
637 : struct TALER_PrivateContractHashP h_contract_terms;
638 :
639 : /**
640 : * Our contract (or NULL if not available).
641 : */
642 : json_t *contract_terms_json;
643 :
644 : /**
645 : * Parsed contract terms, NULL when parsing failed.
646 : */
647 : struct TALER_MERCHANT_Contract *contract_terms;
648 :
649 : /**
650 : * What wire method (of the @e mi) was selected by the wallet?
651 : * Set in #phase_parse_pay().
652 : */
653 : struct TMH_WireMethod *wm;
654 :
655 : /**
656 : * Set to the POS key, if applicable for this order.
657 : */
658 : char *pos_key;
659 :
660 : /**
661 : * Serial number of this order in the database (set once we did the lookup).
662 : */
663 : uint64_t order_serial;
664 :
665 : /**
666 : * Algorithm chosen for generating the confirmation code.
667 : */
668 : enum TALER_MerchantConfirmationAlgorithm pos_alg;
669 :
670 : } check_contract;
671 :
672 : /**
673 : * Results from the phase_validate_tokens()
674 : */
675 : struct
676 : {
677 :
678 : /**
679 : * Maximum fee the merchant is willing to pay, from @e root.
680 : * Note that IF the total fee of the exchange is higher, that is
681 : * acceptable to the merchant if the customer is willing to
682 : * pay the difference
683 : * (i.e. amount - max_fee <= actual_amount - actual_fee).
684 : */
685 : struct TALER_Amount max_fee;
686 :
687 : /**
688 : * Amount from @e root. This is the amount the merchant expects
689 : * to make, minus @e max_fee.
690 : */
691 : struct TALER_Amount brutto;
692 :
693 : /**
694 : * Index of the donau output in the list of tokens.
695 : * Set to -1 if no donau output exists.
696 : */
697 : int donau_output_index;
698 :
699 : } validate_tokens;
700 :
701 : /**
702 : * Results from the phase_execute_pay_transaction()
703 : */
704 : struct
705 : {
706 :
707 : /**
708 : * Considering all the coins with the "found_in_db" flag
709 : * set, what is the total amount we were so far paid on
710 : * this contract?
711 : */
712 : struct TALER_Amount total_paid;
713 :
714 : /**
715 : * Considering all the coins with the "found_in_db" flag
716 : * set, what is the total amount we had to pay in deposit
717 : * fees so far on this contract?
718 : */
719 : struct TALER_Amount total_fees_paid;
720 :
721 : /**
722 : * Considering all the coins with the "found_in_db" flag
723 : * set, what is the total amount we already refunded?
724 : */
725 : struct TALER_Amount total_refunded;
726 :
727 : /**
728 : * Number of coin deposits pending.
729 : */
730 : unsigned int pending;
731 :
732 : /**
733 : * How often have we retried the 'main' transaction?
734 : */
735 : unsigned int retry_counter;
736 :
737 : /**
738 : * Set to true if the deposit currency of a coin
739 : * does not match the contract currency.
740 : */
741 : bool deposit_currency_mismatch;
742 :
743 : /**
744 : * Set to true if the database contains a (bogus)
745 : * refund for a different currency.
746 : */
747 : bool refund_currency_mismatch;
748 :
749 : } pay_transaction;
750 :
751 : /**
752 : * Results from the phase_batch_deposits()
753 : */
754 : struct
755 : {
756 :
757 : /**
758 : * Task called when the (suspended) processing for
759 : * the /pay request times out.
760 : * Happens when we don't get a response from the exchange.
761 : */
762 : struct GNUNET_SCHEDULER_Task *timeout_task;
763 :
764 : /**
765 : * Number of batch transactions pending.
766 : */
767 : unsigned int pending_at_eg;
768 :
769 : /**
770 : * Did any exchange deny a deposit for legal reasons?
771 : */
772 : bool got_451;
773 :
774 : } batch_deposits;
775 :
776 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
777 : /**
778 : * Struct for #phase_request_donation_receipt()
779 : */
780 : struct
781 : {
782 : /**
783 : * Handler of the donau request
784 : */
785 : struct DONAU_BatchIssueReceiptHandle *birh;
786 :
787 : } donau_receipt;
788 : #endif
789 : };
790 :
791 :
792 : /**
793 : * Head of active pay context DLL.
794 : */
795 : static struct PayContext *pc_head;
796 :
797 : /**
798 : * Tail of active pay context DLL.
799 : */
800 : static struct PayContext *pc_tail;
801 :
802 :
803 : void
804 15 : TMH_force_pc_resume ()
805 : {
806 15 : for (struct PayContext *pc = pc_head;
807 15 : NULL != pc;
808 0 : pc = pc->next)
809 : {
810 0 : if (NULL != pc->batch_deposits.timeout_task)
811 : {
812 0 : GNUNET_SCHEDULER_cancel (pc->batch_deposits.timeout_task);
813 0 : pc->batch_deposits.timeout_task = NULL;
814 : }
815 0 : if (GNUNET_YES == pc->suspended)
816 : {
817 0 : pc->suspended = GNUNET_SYSERR;
818 0 : MHD_resume_connection (pc->connection);
819 : }
820 : }
821 15 : }
822 :
823 :
824 : /**
825 : * Resume payment processing.
826 : *
827 : * @param[in,out] pc payment process to resume
828 : */
829 : static void
830 35 : pay_resume (struct PayContext *pc)
831 : {
832 35 : GNUNET_assert (GNUNET_YES == pc->suspended);
833 35 : pc->suspended = GNUNET_NO;
834 35 : MHD_resume_connection (pc->connection);
835 35 : TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
836 35 : }
837 :
838 :
839 : /**
840 : * Resume the given pay context and send the given response.
841 : * Stores the response in the @a pc and signals MHD to resume
842 : * the connection. Also ensures MHD runs immediately.
843 : *
844 : * @param pc payment context
845 : * @param response_code response code to use
846 : * @param response response data to send back
847 : */
848 : static void
849 6 : resume_pay_with_response (struct PayContext *pc,
850 : unsigned int response_code,
851 : struct MHD_Response *response)
852 : {
853 6 : pc->response_code = response_code;
854 6 : pc->response = response;
855 6 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
856 : "Resuming /pay handling. HTTP status for our reply is %u.\n",
857 : response_code);
858 12 : for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
859 : {
860 6 : struct ExchangeGroup *eg = pc->parse_pay.egs[i];
861 :
862 6 : if (NULL != eg->fo)
863 : {
864 0 : TMH_EXCHANGES_keys4exchange_cancel (eg->fo);
865 0 : eg->fo = NULL;
866 0 : pc->batch_deposits.pending_at_eg--;
867 : }
868 6 : if (NULL != eg->bdh)
869 : {
870 0 : TALER_EXCHANGE_batch_deposit_cancel (eg->bdh);
871 0 : eg->bdh = NULL;
872 0 : pc->batch_deposits.pending_at_eg--;
873 : }
874 : }
875 6 : GNUNET_assert (0 == pc->batch_deposits.pending_at_eg);
876 6 : if (NULL != pc->batch_deposits.timeout_task)
877 : {
878 6 : GNUNET_SCHEDULER_cancel (pc->batch_deposits.timeout_task);
879 6 : pc->batch_deposits.timeout_task = NULL;
880 : }
881 6 : pc->phase = PP_RETURN_RESPONSE;
882 6 : pay_resume (pc);
883 6 : }
884 :
885 :
886 : /**
887 : * Resume payment processing with an error.
888 : *
889 : * @param pc operation to resume
890 : * @param ec taler error code to return
891 : * @param msg human readable error message
892 : */
893 : static void
894 0 : resume_pay_with_error (struct PayContext *pc,
895 : enum TALER_ErrorCode ec,
896 : const char *msg)
897 : {
898 0 : resume_pay_with_response (
899 : pc,
900 : TALER_ErrorCode_get_http_status_safe (ec),
901 : TALER_MHD_make_error (ec,
902 : msg));
903 0 : }
904 :
905 :
906 : /**
907 : * Conclude payment processing for @a pc with the
908 : * given @a res MHD status code.
909 : *
910 : * @param[in,out] pc payment context for final state transition
911 : * @param res MHD return code to end with
912 : */
913 : static void
914 43 : pay_end (struct PayContext *pc,
915 : MHD_RESULT res)
916 : {
917 43 : pc->phase = (MHD_YES == res)
918 : ? PP_END_YES
919 43 : : PP_END_NO;
920 43 : }
921 :
922 :
923 : /**
924 : * Return response stored in @a pc.
925 : *
926 : * @param[in,out] pc payment context we are processing
927 : */
928 : static void
929 6 : phase_return_response (struct PayContext *pc)
930 : {
931 6 : GNUNET_assert (0 != pc->response_code);
932 : /* We are *done* processing the request, just queue the response (!) */
933 6 : if (UINT_MAX == pc->response_code)
934 : {
935 0 : GNUNET_break (0);
936 0 : pay_end (pc,
937 : MHD_NO); /* hard error */
938 0 : return;
939 : }
940 6 : pay_end (pc,
941 : MHD_queue_response (pc->connection,
942 : pc->response_code,
943 : pc->response));
944 : }
945 :
946 :
947 : /**
948 : * Return a response indicating failure for legal reasons.
949 : *
950 : * @param[in,out] pc payment context we are processing
951 : */
952 : static void
953 0 : phase_fail_for_legal_reasons (struct PayContext *pc)
954 : {
955 : json_t *exchanges;
956 :
957 0 : GNUNET_assert (0 == pc->pay_transaction.pending);
958 0 : GNUNET_assert (pc->batch_deposits.got_451);
959 0 : exchanges = json_array ();
960 0 : GNUNET_assert (NULL != exchanges);
961 0 : for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
962 : {
963 0 : struct ExchangeGroup *eg = pc->parse_pay.egs[i];
964 :
965 0 : GNUNET_assert (NULL == eg->fo);
966 0 : GNUNET_assert (NULL == eg->bdh);
967 0 : if (! eg->got_451)
968 0 : continue;
969 0 : GNUNET_assert (
970 : 0 ==
971 : json_array_append_new (
972 : exchanges,
973 : json_string (eg->exchange_url)));
974 : }
975 0 : pay_end (pc,
976 0 : TALER_MHD_REPLY_JSON_PACK (
977 : pc->connection,
978 : MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
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 29 : batch_deposit_transaction (const struct ExchangeGroup *eg,
993 : const struct TALER_EXCHANGE_BatchDepositResult *dr)
994 : {
995 29 : 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 29 : uint32_t off = 0;
1000 :
1001 29 : GNUNET_assert (GNUNET_OK ==
1002 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
1003 : &total_without_fees));
1004 64 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1005 : {
1006 35 : 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 35 : if (0 != strcmp (eg->exchange_url,
1011 35 : dc->exchange_url))
1012 0 : continue;
1013 35 : if (dc->found_in_db)
1014 0 : continue;
1015 35 : GNUNET_assert (0 <=
1016 : TALER_amount_subtract (&amount_without_fees,
1017 : &dc->cdd.amount,
1018 : &dc->deposit_fee));
1019 35 : GNUNET_assert (0 <=
1020 : TALER_amount_add (&total_without_fees,
1021 : &total_without_fees,
1022 : &amount_without_fees));
1023 : }
1024 29 : qs = TMH_db->insert_deposit_confirmation (
1025 29 : TMH_db->cls,
1026 29 : pc->hc->instance->settings.id,
1027 : dr->details.ok.deposit_timestamp,
1028 : &pc->check_contract.h_contract_terms,
1029 29 : eg->exchange_url,
1030 29 : pc->check_contract.contract_terms->wire_deadline,
1031 : &total_without_fees,
1032 : &eg->wire_fee,
1033 29 : &pc->check_contract.wm->h_wire,
1034 29 : dr->details.ok.exchange_sig,
1035 29 : dr->details.ok.exchange_pub,
1036 : &b_dep_serial);
1037 29 : if (qs <= 0)
1038 0 : return qs; /* Entire batch already known or failure, we're done */
1039 :
1040 64 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1041 : {
1042 35 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1043 :
1044 : /* might want to group deposits by batch more explicitly ... */
1045 35 : if (0 != strcmp (eg->exchange_url,
1046 35 : dc->exchange_url))
1047 0 : continue;
1048 35 : 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 35 : qs = TMH_db->insert_deposit (
1056 35 : TMH_db->cls,
1057 : off++, /* might want to group deposits by batch more explicitly ... */
1058 : b_dep_serial,
1059 35 : &dc->cdd.coin_pub,
1060 35 : &dc->cdd.coin_sig,
1061 35 : &dc->cdd.amount,
1062 35 : &dc->deposit_fee,
1063 35 : &dc->refund_fee,
1064 : GNUNET_TIME_absolute_add (
1065 35 : pc->check_contract.contract_terms->wire_deadline.abs_time,
1066 : GNUNET_TIME_randomize (GNUNET_TIME_UNIT_MINUTES)));
1067 35 : if (qs < 0)
1068 0 : return qs;
1069 35 : GNUNET_break (qs > 0);
1070 : }
1071 29 : 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 29 : handle_batch_deposit_ok (struct ExchangeGroup *eg,
1084 : const struct TALER_EXCHANGE_BatchDepositResult *dr)
1085 : {
1086 29 : struct PayContext *pc = eg->pc;
1087 29 : enum GNUNET_DB_QueryStatus qs
1088 : = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
1089 :
1090 : /* store result to DB */
1091 29 : 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 29 : for (unsigned int r = 0; r<MAX_RETRIES; r++)
1097 : {
1098 29 : TMH_db->preflight (TMH_db->cls);
1099 29 : if (GNUNET_OK !=
1100 29 : 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 29 : qs = batch_deposit_transaction (eg,
1113 : dr);
1114 29 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
1115 : {
1116 0 : TMH_db->rollback (TMH_db->cls);
1117 0 : continue;
1118 : }
1119 29 : 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 29 : qs = TMH_db->commit (TMH_db->cls);
1127 29 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
1128 : {
1129 0 : TMH_db->rollback (TMH_db->cls);
1130 0 : continue;
1131 : }
1132 29 : 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 29 : break; /* DB transaction succeeded */
1140 : }
1141 29 : 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 64 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1151 : {
1152 35 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1153 :
1154 35 : if (0 != strcmp (eg->exchange_url,
1155 35 : pc->parse_pay.dc[i].exchange_url))
1156 0 : continue;
1157 35 : if (dc->found_in_db)
1158 0 : continue;
1159 35 : dc->found_in_db = true; /* well, at least NOW it'd be true ;-) */
1160 35 : 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 35 : batch_deposit_cb (
1206 : void *cls,
1207 : const struct TALER_EXCHANGE_BatchDepositResult *dr)
1208 : {
1209 35 : struct ExchangeGroup *eg = cls;
1210 35 : struct PayContext *pc = eg->pc;
1211 :
1212 35 : eg->bdh = NULL;
1213 35 : pc->batch_deposits.pending_at_eg--;
1214 35 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1215 : "Batch deposit completed with status %u\n",
1216 : dr->hr.http_status);
1217 35 : GNUNET_assert (GNUNET_YES == pc->suspended);
1218 35 : switch (dr->hr.http_status)
1219 : {
1220 29 : case MHD_HTTP_OK:
1221 29 : handle_batch_deposit_ok (eg,
1222 : dr);
1223 29 : if (0 == pc->batch_deposits.pending_at_eg)
1224 : {
1225 29 : pc->phase = PP_PAY_TRANSACTION;
1226 29 : pay_resume (pc);
1227 : }
1228 29 : 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 35 : process_pay_with_keys (
1328 : void *cls,
1329 : struct TALER_EXCHANGE_Keys *keys,
1330 : struct TMH_Exchange *exchange)
1331 : {
1332 35 : struct ExchangeGroup *eg = cls;
1333 35 : struct PayContext *pc = eg->pc;
1334 35 : struct TMH_HandlerContext *hc = pc->hc;
1335 : unsigned int group_size;
1336 : struct TALER_Amount max_amount;
1337 : enum TMH_ExchangeStatus es;
1338 :
1339 35 : eg->fo = NULL;
1340 35 : pc->batch_deposits.pending_at_eg--;
1341 35 : GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
1342 35 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1343 : "Processing payment with keys from exchange %s\n",
1344 : eg->exchange_url);
1345 35 : GNUNET_assert (GNUNET_YES == pc->suspended);
1346 35 : 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 35 : if (! TMH_EXCHANGES_is_below_limit (keys,
1356 : TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION,
1357 35 : &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 35 : max_amount = eg->total;
1368 35 : es = TMH_exchange_check_debit (
1369 35 : pc->hc->instance->settings.id,
1370 : exchange,
1371 35 : pc->check_contract.wm,
1372 : &max_amount);
1373 35 : 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 35 : if (-1 ==
1390 35 : TALER_amount_cmp (&max_amount,
1391 35 : &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 35 : if (GNUNET_OK !=
1403 35 : TMH_EXCHANGES_lookup_wire_fee (exchange,
1404 35 : 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 35 : 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 35 : group_size = 0;
1426 78 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1427 : {
1428 43 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1429 : const struct TALER_EXCHANGE_DenomPublicKey *denom_details;
1430 43 : bool is_age_restricted_denom = false;
1431 :
1432 43 : if (0 != strcmp (eg->exchange_url,
1433 43 : pc->parse_pay.dc[i].exchange_url))
1434 0 : continue;
1435 43 : if (dc->found_in_db)
1436 0 : continue;
1437 :
1438 : denom_details
1439 43 : = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
1440 43 : &dc->cdd.h_denom_pub);
1441 43 : 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 43 : dc->deposit_fee = denom_details->fees.deposit;
1468 43 : dc->refund_fee = denom_details->fees.refund;
1469 :
1470 43 : 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 43 : is_age_restricted_denom = (0 != denom_details->key.age_mask.bits);
1490 :
1491 43 : 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 43 : 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 43 : group_size++;
1559 : }
1560 :
1561 35 : 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 35 : if (group_size > TALER_MAX_COINS)
1576 0 : group_size = TALER_MAX_COINS;
1577 35 : {
1578 35 : struct TALER_EXCHANGE_CoinDepositDetail cdds[group_size];
1579 35 : struct TALER_EXCHANGE_DepositContractDetail dcd = {
1580 35 : .wire_deadline = pc->check_contract.contract_terms->wire_deadline,
1581 35 : .merchant_payto_uri = pc->check_contract.wm->payto_uri,
1582 35 : .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 35 : .wallet_timestamp = pc->check_contract.contract_terms->timestamp,
1586 35 : .merchant_pub = hc->instance->merchant_pub,
1587 35 : .refund_deadline = pc->check_contract.contract_terms->refund_deadline
1588 : };
1589 : enum TALER_ErrorCode ec;
1590 35 : size_t off = 0;
1591 :
1592 35 : TALER_merchant_contract_sign (&pc->check_contract.h_contract_terms,
1593 35 : &pc->hc->instance->merchant_priv,
1594 : &dcd.merchant_sig);
1595 43 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
1596 : {
1597 43 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
1598 :
1599 43 : if (dc->found_in_db)
1600 0 : continue;
1601 43 : if (0 != strcmp (dc->exchange_url,
1602 : eg->exchange_url))
1603 0 : continue;
1604 43 : cdds[off++] = dc->cdd;
1605 43 : if (off >= group_size)
1606 35 : break;
1607 : }
1608 35 : 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 35 : 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 35 : 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 35 : pc->batch_deposits.pending_at_eg++;
1639 35 : 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 35 : 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 35 : t = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
1707 35 : 15 * (1 + (num_coins / 5)));
1708 :
1709 35 : 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 35 : phase_batch_deposits (struct PayContext *pc)
1721 : {
1722 70 : for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
1723 : {
1724 35 : struct ExchangeGroup *eg = pc->parse_pay.egs[i];
1725 35 : bool have_coins = false;
1726 :
1727 35 : for (size_t j = 0; j<pc->parse_pay.coins_cnt; j++)
1728 : {
1729 35 : struct DepositConfirmation *dc = &pc->parse_pay.dc[j];
1730 :
1731 35 : if (0 != strcmp (eg->exchange_url,
1732 35 : dc->exchange_url))
1733 0 : continue;
1734 35 : if (dc->found_in_db)
1735 0 : continue;
1736 35 : have_coins = true;
1737 35 : break;
1738 : }
1739 35 : if (! have_coins)
1740 0 : continue; /* no coins left to deposit at this exchange */
1741 35 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1742 : "Getting /keys for %s\n",
1743 : eg->exchange_url);
1744 35 : eg->fo = TMH_EXCHANGES_keys4exchange (
1745 : eg->exchange_url,
1746 : false,
1747 : &process_pay_with_keys,
1748 : eg);
1749 35 : 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 35 : pc->batch_deposits.pending_at_eg++;
1761 : }
1762 35 : 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 35 : MHD_suspend_connection (pc->connection);
1770 35 : pc->suspended = GNUNET_YES;
1771 35 : GNUNET_assert (NULL == pc->batch_deposits.timeout_task);
1772 : pc->batch_deposits.timeout_task
1773 35 : = 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 31 : build_token_sigs (struct PayContext *pc)
1787 : {
1788 : json_t *token_sigs;
1789 :
1790 31 : if (0 == pc->output_tokens_len)
1791 27 : 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 31 : 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 31 : TALER_merchant_pay_sign (&pc->check_contract.h_contract_terms,
1823 31 : &pc->hc->instance->merchant_priv,
1824 : &sig);
1825 : /* Build the response */
1826 62 : pos_confirmation = (NULL == pc->check_contract.pos_key)
1827 : ? NULL
1828 31 : : 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 31 : pay_end (pc,
1834 31 : 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 31 : GNUNET_free (pos_confirmation);
1846 31 : }
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 29 : phase_payment_notification (struct PayContext *pc)
1857 : {
1858 : {
1859 29 : struct TMH_OrderPayEventP pay_eh = {
1860 29 : .header.size = htons (sizeof (pay_eh)),
1861 29 : .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_PAID),
1862 29 : .merchant_pub = pc->hc->instance->merchant_pub
1863 : };
1864 :
1865 29 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1866 : "Notifying clients about payment of order %s\n",
1867 : pc->order_id);
1868 29 : GNUNET_CRYPTO_hash (pc->order_id,
1869 : strlen (pc->order_id),
1870 : &pay_eh.h_order_id);
1871 29 : TMH_db->event_notify (TMH_db->cls,
1872 : &pay_eh.header,
1873 : NULL,
1874 : 0);
1875 : }
1876 29 : if ( (NULL != pc->parse_pay.session_id) &&
1877 29 : (NULL != pc->check_contract.contract_terms->fulfillment_url) )
1878 : {
1879 17 : struct TMH_SessionEventP session_eh = {
1880 17 : .header.size = htons (sizeof (session_eh)),
1881 17 : .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED),
1882 17 : .merchant_pub = pc->hc->instance->merchant_pub
1883 : };
1884 :
1885 17 : 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 17 : GNUNET_CRYPTO_hash (pc->parse_pay.session_id,
1890 17 : strlen (pc->parse_pay.session_id),
1891 : &session_eh.h_session_id);
1892 17 : GNUNET_CRYPTO_hash (pc->check_contract.contract_terms->fulfillment_url,
1893 17 : strlen (pc->check_contract.contract_terms->
1894 : fulfillment_url),
1895 : &session_eh.h_fulfillment_url);
1896 17 : TMH_db->event_notify (TMH_db->cls,
1897 : &session_eh.header,
1898 : NULL,
1899 : 0);
1900 : }
1901 29 : pc->phase = PP_SUCCESS_RESPONSE;
1902 29 : }
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 29 : phase_final_output_token_processing (struct PayContext *pc)
1914 : {
1915 29 : if (0 == pc->output_tokens_len)
1916 : {
1917 25 : pc->phase++;
1918 25 : 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 37 : 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 37 : struct PayContext *pc = cls;
2232 :
2233 86 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
2234 : {
2235 49 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
2236 :
2237 49 : if (dc->found_in_db)
2238 6 : continue; /* processed earlier, skip "expensive" memcmp() */
2239 : /* Get matching coin from results*/
2240 43 : if ( (0 != GNUNET_memcmp (coin_pub,
2241 35 : &dc->cdd.coin_pub)) ||
2242 : (0 !=
2243 35 : strcmp (exchange_url,
2244 70 : dc->exchange_url)) ||
2245 : (GNUNET_OK !=
2246 35 : TALER_amount_cmp_currency (amount_with_fee,
2247 70 : &dc->cdd.amount)) ||
2248 35 : (0 != TALER_amount_cmp (amount_with_fee,
2249 35 : &dc->cdd.amount)) )
2250 8 : continue; /* does not match, skip */
2251 35 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2252 : "Deposit of coin `%s' already in our DB.\n",
2253 : TALER_B2S (coin_pub));
2254 35 : if ( (GNUNET_OK !=
2255 35 : TALER_amount_cmp_currency (&pc->pay_transaction.total_paid,
2256 35 : amount_with_fee)) ||
2257 : (GNUNET_OK !=
2258 35 : 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 35 : GNUNET_assert (0 <=
2266 : TALER_amount_add (&pc->pay_transaction.total_paid,
2267 : &pc->pay_transaction.total_paid,
2268 : amount_with_fee));
2269 35 : GNUNET_assert (0 <=
2270 : TALER_amount_add (&pc->pay_transaction.total_fees_paid,
2271 : &pc->pay_transaction.total_fees_paid,
2272 : deposit_fee));
2273 35 : dc->deposit_fee = *deposit_fee;
2274 35 : dc->refund_fee = *refund_fee;
2275 35 : dc->cdd.amount = *amount_with_fee;
2276 35 : dc->found_in_db = true;
2277 35 : pc->pay_transaction.pending--;
2278 : }
2279 37 : }
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 31 : 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 31 : if (0 == pc->parse_pay.coins_cnt)
2349 2 : return TALER_amount_is_zero (&pc->validate_tokens.brutto);
2350 29 : GNUNET_assert (GNUNET_OK ==
2351 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2352 : &total_wire_fee));
2353 58 : for (unsigned int i = 0; i < pc->parse_pay.num_exchanges; i++)
2354 : {
2355 29 : if (GNUNET_OK !=
2356 29 : TALER_amount_cmp_currency (&total_wire_fee,
2357 29 : &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 29 : if (0 >
2368 29 : TALER_amount_add (&total_wire_fee,
2369 : &total_wire_fee,
2370 29 : &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 29 : GNUNET_assert (GNUNET_OK ==
2388 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2389 : &acc_fee));
2390 29 : GNUNET_assert (GNUNET_OK ==
2391 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2392 : &acc_amount));
2393 64 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
2394 : {
2395 35 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
2396 :
2397 35 : GNUNET_assert (dc->found_in_db);
2398 35 : if ( (GNUNET_OK !=
2399 35 : TALER_amount_cmp_currency (&acc_fee,
2400 70 : &dc->deposit_fee)) ||
2401 : (GNUNET_OK !=
2402 35 : TALER_amount_cmp_currency (&acc_amount,
2403 35 : &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 35 : if ( (0 >
2415 35 : TALER_amount_add (&acc_fee,
2416 35 : &dc->deposit_fee,
2417 35 : &acc_fee)) ||
2418 : (0 >
2419 35 : TALER_amount_add (&acc_amount,
2420 35 : &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 35 : if (1 ==
2434 35 : TALER_amount_cmp (&dc->deposit_fee,
2435 35 : &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 29 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2449 : "Amount received from wallet: %s\n",
2450 : TALER_amount2s (&acc_amount));
2451 29 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2452 : "Deposit fee for all coins: %s\n",
2453 : TALER_amount2s (&acc_fee));
2454 29 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2455 : "Total wire fee: %s\n",
2456 : TALER_amount2s (&total_wire_fee));
2457 29 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2458 : "Deposit fee limit for merchant: %s\n",
2459 : TALER_amount2s (&pc->validate_tokens.max_fee));
2460 29 : 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 29 : if (GNUNET_YES !=
2466 29 : 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 29 : if (0 >
2481 29 : 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 29 : 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 21 : total_needed = pc->validate_tokens.brutto;
2530 : }
2531 :
2532 : /* Do not count refunds towards the payment */
2533 29 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2534 : "Subtracting total refunds from paid amount: %s\n",
2535 : TALER_amount2s (&pc->pay_transaction.total_refunded));
2536 29 : if (0 >
2537 29 : TALER_amount_subtract (&final_amount,
2538 : &acc_amount,
2539 29 : &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 29 : 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 27 : 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 68 : phase_execute_pay_transaction (struct PayContext *pc)
2601 : {
2602 68 : struct TMH_HandlerContext *hc = pc->hc;
2603 68 : const char *instance_id = hc->instance->settings.id;
2604 :
2605 68 : 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 68 : 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 68 : GNUNET_break (GNUNET_OK ==
2626 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2627 : &pc->pay_transaction.total_paid));
2628 68 : GNUNET_break (GNUNET_OK ==
2629 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2630 : &pc->pay_transaction.total_fees_paid));
2631 68 : GNUNET_break (GNUNET_OK ==
2632 : TALER_amount_set_zero (pc->validate_tokens.brutto.currency,
2633 : &pc->pay_transaction.total_refunded));
2634 146 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
2635 78 : pc->parse_pay.dc[i].found_in_db = false;
2636 68 : pc->pay_transaction.pending = pc->parse_pay.coins_cnt;
2637 :
2638 : /* First, try to see if we have all we need already done */
2639 68 : TMH_db->preflight (TMH_db->cls);
2640 68 : if (GNUNET_OK !=
2641 68 : 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 70 : 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 66 : qs = TMH_db->lookup_deposits (TMH_db->cls,
2701 : instance_id,
2702 66 : &pc->check_contract.h_contract_terms,
2703 : &check_coin_paid,
2704 : pc);
2705 66 : 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 66 : 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 66 : qs = TMH_db->lookup_refunds (TMH_db->cls,
2738 : instance_id,
2739 66 : &pc->check_contract.h_contract_terms,
2740 : &check_coin_refunded,
2741 : pc);
2742 66 : 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 66 : 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 66 : if (0 != pc->pay_transaction.pending)
2770 : {
2771 : /* we made no DB changes, so we can just rollback */
2772 35 : 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 35 : pc->phase = PP_BATCH_DEPOSITS;
2777 35 : return;
2778 : }
2779 :
2780 : /* 0 == pc->pay_transaction.pending: all coins processed, let's see if that was enough */
2781 31 : 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 29 : 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 29 : qs = TMH_db->mark_contract_paid (TMH_db->cls,
2797 : instance_id,
2798 29 : &pc->check_contract.h_contract_terms,
2799 29 : pc->parse_pay.session_id,
2800 29 : pc->parse_wallet_data.choice_index);
2801 29 : 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 29 : const struct TALER_MERCHANT_ContractChoice *choice =
2819 29 : &pc->check_contract.contract_terms->details.v1
2820 29 : .choices[pc->parse_wallet_data.choice_index];
2821 :
2822 33 : 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 29 : TMH_notify_order_change (hc->instance,
2888 : TMH_OSF_CLAIMED | TMH_OSF_PAID,
2889 29 : 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 29 : 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 29 : GNUNET_assert (NULL != jhook);
2902 29 : qs = TMH_trigger_webhook (pc->hc->instance->settings.id,
2903 : "pay",
2904 : jhook);
2905 29 : json_decref (jhook);
2906 29 : 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 29 : qs = TMH_db->commit (TMH_db->cls);
2925 29 : 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 29 : 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 39 : phase_validate_tokens (struct PayContext *pc)
3536 : {
3537 : /* We haven't seen a donau output yet. */
3538 39 : pc->validate_tokens.donau_output_index = -1;
3539 :
3540 39 : switch (pc->check_contract.contract_terms->version)
3541 : {
3542 33 : case TALER_MERCHANT_CONTRACT_VERSION_0:
3543 : /* No tokens to validate */
3544 33 : pc->phase = PP_PAY_TRANSACTION;
3545 : pc->validate_tokens.max_fee
3546 33 : = pc->check_contract.contract_terms->details.v0.max_fee;
3547 : pc->validate_tokens.brutto
3548 33 : = pc->check_contract.contract_terms->details.v0.brutto;
3549 33 : 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 82 : for (size_t i = 0; i<pc->parse_pay.coins_cnt; i++)
3747 : {
3748 43 : const struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
3749 :
3750 43 : if (GNUNET_OK !=
3751 43 : TALER_amount_cmp_currency (&dc->cdd.amount,
3752 43 : &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 39 : 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 43 : phase_check_contract (struct PayContext *pc)
4064 : {
4065 : /* obtain contract terms */
4066 : enum GNUNET_DB_QueryStatus qs;
4067 43 : bool paid = false;
4068 :
4069 43 : 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 43 : 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 43 : qs = TMH_db->lookup_contract_terms2 (TMH_db->cls,
4080 43 : 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 43 : 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 43 : 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 43 : if (GNUNET_OK !=
4120 43 : 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 43 : pc->check_contract.contract_terms = TALER_MERCHANT_contract_parse (
4137 : pc->check_contract.contract_terms_json,
4138 : true);
4139 :
4140 43 : 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 43 : 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 39 : 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 39 : switch (pc->check_contract.contract_terms->version)
4169 : {
4170 33 : case TALER_MERCHANT_CONTRACT_VERSION_0:
4171 : {
4172 33 : 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 33 : 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 39 : 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 39 : 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 39 : wm = pc->hc->instance->wm_head;
4269 40 : while (0 != GNUNET_memcmp (&pc->check_contract.contract_terms->h_wire,
4270 : &wm->h_wire))
4271 1 : wm = wm->next;
4272 39 : 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 39 : pc->check_contract.wm = wm;
4284 : }
4285 39 : 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 43 : 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 43 : GNUNET_JSON_spec_mark_optional (
4304 : GNUNET_JSON_spec_int16 ("choice_index",
4305 : &pc->parse_wallet_data.choice_index),
4306 : NULL),
4307 43 : GNUNET_JSON_spec_mark_optional (
4308 : GNUNET_JSON_spec_array_const ("tokens_evs",
4309 : &tokens_evs),
4310 : NULL),
4311 43 : GNUNET_JSON_spec_mark_optional (
4312 : GNUNET_JSON_spec_object_const ("donau",
4313 : &donau_obj),
4314 : NULL),
4315 43 : GNUNET_JSON_spec_end ()
4316 : };
4317 :
4318 43 : pc->parse_wallet_data.choice_index = -1;
4319 43 : if (NULL == pc->parse_pay.wallet_data)
4320 : {
4321 37 : pc->phase = PP_CHECK_CONTRACT;
4322 37 : 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 43 : phase_parse_pay (struct PayContext *pc)
4565 : {
4566 43 : const char *session_id = NULL;
4567 : const json_t *coins;
4568 : const json_t *tokens;
4569 : struct GNUNET_JSON_Specification spec[] = {
4570 43 : GNUNET_JSON_spec_array_const ("coins",
4571 : &coins),
4572 43 : GNUNET_JSON_spec_mark_optional (
4573 : GNUNET_JSON_spec_string ("session_id",
4574 : &session_id),
4575 : NULL),
4576 43 : GNUNET_JSON_spec_mark_optional (
4577 : GNUNET_JSON_spec_object_const ("wallet_data",
4578 : &pc->parse_pay.wallet_data),
4579 : NULL),
4580 43 : GNUNET_JSON_spec_mark_optional (
4581 : GNUNET_JSON_spec_array_const ("tokens",
4582 : &tokens),
4583 : NULL),
4584 43 : 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 43 : GNUNET_assert (PP_PARSE_PAY == pc->phase);
4605 : {
4606 : enum GNUNET_GenericReturnValue res;
4607 :
4608 43 : res = TALER_MHD_parse_json_data (pc->connection,
4609 43 : pc->hc->request_body,
4610 : spec);
4611 43 : 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 43 : if (NULL != session_id)
4624 : {
4625 17 : 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 43 : pc->parse_pay.coins_cnt = json_array_size (coins);
4634 43 : 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 43 : 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 92 : json_array_foreach (coins, coins_index, coin)
4655 : {
4656 49 : struct DepositConfirmation *dc = &pc->parse_pay.dc[coins_index];
4657 : const char *exchange_url;
4658 : struct GNUNET_JSON_Specification ispec[] = {
4659 49 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
4660 : &dc->cdd.coin_sig),
4661 49 : GNUNET_JSON_spec_fixed_auto ("coin_pub",
4662 : &dc->cdd.coin_pub),
4663 49 : TALER_JSON_spec_denom_sig ("ub_sig",
4664 : &dc->cdd.denom_sig),
4665 49 : GNUNET_JSON_spec_fixed_auto ("h_denom",
4666 : &dc->cdd.h_denom_pub),
4667 49 : TALER_JSON_spec_amount_any ("contribution",
4668 : &dc->cdd.amount),
4669 49 : 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 49 : GNUNET_JSON_spec_mark_optional (
4674 49 : GNUNET_JSON_spec_fixed_auto ("minimum_age_sig",
4675 : &dc->minimum_age_sig),
4676 : &dc->no_minimum_age_sig),
4677 49 : 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 49 : GNUNET_JSON_spec_mark_optional (
4684 49 : GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
4685 : &dc->cdd.h_age_commitment),
4686 : &dc->no_h_age_commitment),
4687 49 : GNUNET_JSON_spec_end ()
4688 : };
4689 : enum GNUNET_GenericReturnValue res;
4690 49 : struct ExchangeGroup *eg = NULL;
4691 :
4692 49 : res = TALER_MHD_parse_json_data (pc->connection,
4693 : coin,
4694 : ispec);
4695 49 : 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 59 : 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 49 : dc->exchange_url = GNUNET_strdup (exchange_url);
4721 49 : dc->index = coins_index;
4722 49 : dc->pc = pc;
4723 :
4724 : /* Check the consistency of the (potential) age restriction
4725 : * information. */
4726 49 : 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 49 : 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 49 : if (NULL == eg)
4751 : {
4752 39 : eg = GNUNET_new (struct ExchangeGroup);
4753 39 : eg->pc = pc;
4754 39 : eg->exchange_url = dc->exchange_url;
4755 39 : eg->total = dc->cdd.amount;
4756 39 : 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 43 : pc->parse_pay.tokens_cnt = json_array_size (tokens);
4781 43 : 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 43 : 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 47 : 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 43 : 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 43 : pay_context_cleanup (void *cls)
4859 : {
4860 43 : struct PayContext *pc = cls;
4861 :
4862 43 : if (NULL != pc->batch_deposits.timeout_task)
4863 : {
4864 29 : GNUNET_SCHEDULER_cancel (pc->batch_deposits.timeout_task);
4865 29 : pc->batch_deposits.timeout_task = NULL;
4866 : }
4867 43 : if (NULL != pc->check_contract.contract_terms_json)
4868 : {
4869 43 : json_decref (pc->check_contract.contract_terms_json);
4870 43 : pc->check_contract.contract_terms_json = NULL;
4871 : }
4872 92 : for (unsigned int i = 0; i<pc->parse_pay.coins_cnt; i++)
4873 : {
4874 49 : struct DepositConfirmation *dc = &pc->parse_pay.dc[i];
4875 :
4876 49 : TALER_denom_sig_free (&dc->cdd.denom_sig);
4877 49 : GNUNET_free (dc->exchange_url);
4878 : }
4879 43 : GNUNET_free (pc->parse_pay.dc);
4880 47 : 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 43 : GNUNET_free (pc->parse_pay.tokens);
4887 82 : for (unsigned int i = 0; i<pc->parse_pay.num_exchanges; i++)
4888 : {
4889 39 : struct ExchangeGroup *eg = pc->parse_pay.egs[i];
4890 :
4891 39 : if (NULL != eg->fo)
4892 0 : TMH_EXCHANGES_keys4exchange_cancel (eg->fo);
4893 39 : GNUNET_free (eg);
4894 : }
4895 43 : GNUNET_free (pc->parse_pay.egs);
4896 43 : if (NULL != pc->check_contract.contract_terms)
4897 : {
4898 43 : TALER_MERCHANT_contract_free (pc->check_contract.contract_terms);
4899 43 : pc->check_contract.contract_terms = NULL;
4900 : }
4901 43 : if (NULL != pc->response)
4902 : {
4903 6 : MHD_destroy_response (pc->response);
4904 6 : pc->response = NULL;
4905 : }
4906 43 : GNUNET_free (pc->parse_pay.session_id);
4907 43 : GNUNET_CONTAINER_DLL_remove (pc_head,
4908 : pc_tail,
4909 : pc);
4910 43 : 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 49 : 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 43 : GNUNET_free (pc->parse_wallet_data.token_envelopes);
4936 43 : if (NULL != pc->output_tokens)
4937 : {
4938 12 : for (unsigned int i = 0; i<pc->output_tokens_len; i++)
4939 6 : GNUNET_CRYPTO_blinded_sig_decref (pc->output_tokens[i].sig.signature);
4940 6 : GNUNET_free (pc->output_tokens);
4941 6 : pc->output_tokens = NULL;
4942 : }
4943 43 : GNUNET_free (pc);
4944 43 : }
4945 :
4946 :
4947 : MHD_RESULT
4948 78 : TMH_post_orders_ID_pay (const struct TMH_RequestHandler *rh,
4949 : struct MHD_Connection *connection,
4950 : struct TMH_HandlerContext *hc)
4951 : {
4952 78 : struct PayContext *pc = hc->ctx;
4953 :
4954 78 : GNUNET_assert (NULL != hc->infix);
4955 78 : if (NULL == pc)
4956 : {
4957 43 : pc = GNUNET_new (struct PayContext);
4958 43 : pc->connection = connection;
4959 43 : pc->hc = hc;
4960 43 : pc->order_id = hc->infix;
4961 43 : hc->ctx = pc;
4962 43 : hc->cc = &pay_context_cleanup;
4963 43 : GNUNET_CONTAINER_DLL_insert (pc_head,
4964 : pc_tail,
4965 : pc);
4966 : }
4967 : while (1)
4968 : {
4969 748 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4970 : "Processing /pay in phase %d\n",
4971 : (int) pc->phase);
4972 413 : switch (pc->phase)
4973 : {
4974 43 : case PP_PARSE_PAY:
4975 43 : phase_parse_pay (pc);
4976 43 : break;
4977 43 : case PP_PARSE_WALLET_DATA:
4978 43 : phase_parse_wallet_data (pc);
4979 43 : break;
4980 43 : case PP_CHECK_CONTRACT:
4981 43 : phase_check_contract (pc);
4982 43 : break;
4983 39 : case PP_VALIDATE_TOKENS:
4984 39 : phase_validate_tokens (pc);
4985 39 : break;
4986 4 : case PP_CONTRACT_PAID:
4987 4 : phase_contract_paid (pc);
4988 4 : break;
4989 68 : case PP_PAY_TRANSACTION:
4990 68 : phase_execute_pay_transaction (pc);
4991 68 : break;
4992 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
4993 : case PP_REQUEST_DONATION_RECEIPT:
4994 : phase_request_donation_receipt (pc);
4995 : break;
4996 : #endif
4997 29 : case PP_FINAL_OUTPUT_TOKEN_PROCESSING:
4998 29 : phase_final_output_token_processing (pc);
4999 29 : break;
5000 29 : case PP_PAYMENT_NOTIFICATION:
5001 29 : phase_payment_notification (pc);
5002 29 : break;
5003 31 : case PP_SUCCESS_RESPONSE:
5004 31 : phase_success_response (pc);
5005 31 : break;
5006 35 : case PP_BATCH_DEPOSITS:
5007 35 : phase_batch_deposits (pc);
5008 35 : break;
5009 6 : case PP_RETURN_RESPONSE:
5010 6 : phase_return_response (pc);
5011 6 : break;
5012 0 : case PP_FAIL_LEGAL_REASONS:
5013 0 : phase_fail_for_legal_reasons (pc);
5014 0 : break;
5015 43 : case PP_END_YES:
5016 43 : return MHD_YES;
5017 0 : case PP_END_NO:
5018 0 : return MHD_NO;
5019 0 : default:
5020 : /* should not be reachable */
5021 0 : GNUNET_assert (0);
5022 : return MHD_NO;
5023 : }
5024 370 : switch (pc->suspended)
5025 : {
5026 0 : case GNUNET_SYSERR:
5027 : /* during shutdown, we don't generate any more replies */
5028 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
5029 : "Processing /pay ends due to shutdown in phase %d\n",
5030 : (int) pc->phase);
5031 0 : return MHD_NO;
5032 335 : case GNUNET_NO:
5033 : /* continue to next phase */
5034 335 : break;
5035 35 : case GNUNET_YES:
5036 35 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
5037 : "Processing /pay suspended in phase %d\n",
5038 : (int) pc->phase);
5039 35 : return MHD_YES;
5040 : }
5041 : }
5042 : /* impossible to get here */
5043 : GNUNET_assert (0);
5044 : return MHD_YES;
5045 : }
5046 :
5047 :
5048 : /* end of taler-merchant-httpd_post-orders-ID-pay.c */
|