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