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