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