Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2014-2022 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 <taler/taler_dbevents.h>
29 : #include <taler/taler_signatures.h>
30 : #include <taler/taler_json_lib.h>
31 : #include <taler/taler_exchange_service.h>
32 : #include "taler-merchant-httpd_auditors.h"
33 : #include "taler-merchant-httpd_exchanges.h"
34 : #include "taler-merchant-httpd_helper.h"
35 : #include "taler-merchant-httpd_post-orders-ID-pay.h"
36 : #include "taler-merchant-httpd_private-get-orders.h"
37 :
38 :
39 : /**
40 : * How often do we retry the (complex!) database transaction?
41 : */
42 : #define MAX_RETRIES 5
43 :
44 : /**
45 : * Maximum number of coins that we allow per transaction
46 : */
47 : #define MAX_COIN_ALLOWED_COINS 1024
48 :
49 : /**
50 : * How often do we ask the exchange again about our
51 : * KYC status? Very rarely, as if the user actively
52 : * changes it, we should usually notice anyway.
53 : */
54 : #define KYC_RETRY_FREQUENCY GNUNET_TIME_UNIT_WEEKS
55 :
56 : /**
57 : * Information we keep for an individual call to the pay handler.
58 : */
59 : struct PayContext;
60 :
61 :
62 : /**
63 : * Information kept during a pay request for each coin.
64 : */
65 : struct DepositConfirmation
66 : {
67 :
68 : /**
69 : * Reference to the main PayContext
70 : */
71 : struct PayContext *pc;
72 :
73 : /**
74 : * Handle to the deposit operation we are performing for
75 : * this coin, NULL after the operation is done.
76 : */
77 : struct TALER_EXCHANGE_DepositHandle *dh;
78 :
79 : /**
80 : * URL of the exchange that issued this coin.
81 : */
82 : char *exchange_url;
83 :
84 : /**
85 : * Details about the coin being deposited.
86 : */
87 : struct TALER_EXCHANGE_CoinDepositDetail cdd;
88 :
89 : /**
90 : * Fee charged by the exchange for the deposit operation of this coin.
91 : */
92 : struct TALER_Amount deposit_fee;
93 :
94 : /**
95 : * Fee charged by the exchange for the refund operation of this coin.
96 : */
97 : struct TALER_Amount refund_fee;
98 :
99 : /**
100 : * Wire fee charged by the exchange of this coin.
101 : */
102 : struct TALER_Amount wire_fee;
103 :
104 : /**
105 : * If a minimum age was required (i. e. pc->minimum_age is large enough),
106 : * this is the signature of the minimum age (as a single uint8_t), using the
107 : * private key to the corresponding age group. Might be all zeroes for no
108 : * age attestation.
109 : */
110 : struct TALER_AgeAttestation minimum_age_sig;
111 :
112 : /**
113 : * If a minimum age was required (i. e. pc->minimum_age is large enough),
114 : * this is the age commitment (i. e. age mask and vector of EdDSA public
115 : * keys, one per age group) that went into the mining of the coin. The
116 : * SHA256 hash of the mask and the vector of public keys was bound to the
117 : * key.
118 : */
119 : struct TALER_AgeCommitment age_commitment;
120 :
121 : /**
122 : * Age mask in the denomination that defines the age groups. Only
123 : * applicable, if minimum age was required.
124 : */
125 : struct TALER_AgeMask age_mask;
126 :
127 : /**
128 : * Offset of this coin into the `dc` array of all coins in the
129 : * @e pc.
130 : */
131 : unsigned int index;
132 :
133 : /**
134 : * true, if no field "age_commitment" was found in the JSON blob
135 : */
136 : bool no_age_commitment;
137 :
138 : /**
139 : * True, if no field "minimum_age_sig" was found in the JSON blob
140 : */
141 : bool no_minimum_age_sig;
142 :
143 : /**
144 : * true, if no field "h_age_commitment" was found in the JSON blob
145 : */
146 : bool no_h_age_commitment;
147 :
148 : /**
149 : * true if we found this coin in the database.
150 : */
151 : bool found_in_db;
152 :
153 : /**
154 : * true if we #deposit_paid_check() matched this coin in the database.
155 : */
156 : bool matched_in_db;
157 :
158 : };
159 :
160 :
161 : /**
162 : * Information kept during a pay request for each exchange.
163 : */
164 : struct ExchangeGroup
165 : {
166 :
167 : /**
168 : * Payment context this group is part of.
169 : */
170 : struct PayContext *pc;
171 :
172 : /**
173 : * Handle to the batch deposit operation we are performing for this
174 : * exchange, NULL after the operation is done.
175 : */
176 : struct TALER_EXCHANGE_BatchDepositHandle *bdh;
177 :
178 : /**
179 : * Handle for operation to lookup /keys (and auditors) from
180 : * the exchange used for this transaction; NULL if no operation is
181 : * pending.
182 : */
183 : struct TMH_EXCHANGES_FindOperation *fo;
184 :
185 : /**
186 : * URL of the exchange that issued this coin. Aliases
187 : * the exchange URL of one of the coins, do not free!
188 : */
189 : const char *exchange_url;
190 :
191 : /**
192 : * true if we already tried a forced /keys download.
193 : */
194 : bool tried_force_keys;
195 : };
196 :
197 :
198 : /**
199 : * Information we keep for an individual call to the /pay handler.
200 : */
201 : struct PayContext
202 : {
203 :
204 : /**
205 : * Stored in a DLL.
206 : */
207 : struct PayContext *next;
208 :
209 : /**
210 : * Stored in a DLL.
211 : */
212 : struct PayContext *prev;
213 :
214 : /**
215 : * Array with @e num_exchange exchanges we are depositing
216 : * coins into.
217 : */
218 : struct ExchangeGroup **egs;
219 :
220 : /**
221 : * Array with @e coins_cnt coins we are despositing.
222 : */
223 : struct DepositConfirmation *dc;
224 :
225 : /**
226 : * MHD connection to return to
227 : */
228 : struct MHD_Connection *connection;
229 :
230 : /**
231 : * Details about the client's request.
232 : */
233 : struct TMH_HandlerContext *hc;
234 :
235 : /**
236 : * What wire method (of the @e mi) was selected by the wallet?
237 : * Set in #parse_pay().
238 : */
239 : struct TMH_WireMethod *wm;
240 :
241 : /**
242 : * Task called when the (suspended) processing for
243 : * the /pay request times out.
244 : * Happens when we don't get a response from the exchange.
245 : */
246 : struct GNUNET_SCHEDULER_Task *timeout_task;
247 :
248 : /**
249 : * Response to return, NULL if we don't have one yet.
250 : */
251 : struct MHD_Response *response;
252 :
253 : /**
254 : * Placeholder for #TALER_MHD_parse_post_json() to keep its internal state.
255 : */
256 : void *json_parse_context;
257 :
258 : /**
259 : * Optional session id given in @e root.
260 : * NULL if not given.
261 : */
262 : char *session_id;
263 :
264 : /**
265 : * Transaction ID given in @e root.
266 : */
267 : const char *order_id;
268 :
269 : /**
270 : * Fulfillment URL from the contract, or NULL if we don't have one.
271 : */
272 : char *fulfillment_url;
273 :
274 : /**
275 : * Serial number of this order in the database (set once we did the lookup).
276 : */
277 : uint64_t order_serial;
278 :
279 : /**
280 : * Hashed proposal.
281 : */
282 : struct TALER_PrivateContractHashP h_contract_terms;
283 :
284 : /**
285 : * "h_wire" from @e contract_terms. Used to identify
286 : * the instance's wire transfer method.
287 : */
288 : struct TALER_MerchantWireHashP h_wire;
289 :
290 : /**
291 : * Maximum fee the merchant is willing to pay, from @e root.
292 : * Note that IF the total fee of the exchange is higher, that is
293 : * acceptable to the merchant if the customer is willing to
294 : * pay the difference
295 : * (i.e. amount - max_fee <= actual-amount - actual-fee).
296 : */
297 : struct TALER_Amount max_fee;
298 :
299 : /**
300 : * Maximum wire fee the merchant is willing to pay, from @e root.
301 : * Note that IF the total fee of the exchange is higher, that is
302 : * acceptable to the merchant if the customer is willing to
303 : * pay the amorized difference. Wire fees are charged over an
304 : * aggregate of several translations, hence unlike the deposit
305 : * fees, they are amortized over several customer's transactions.
306 : * The contract specifies under @e wire_fee_amortization how many
307 : * customer's transactions he expects the wire fees to be amortized
308 : * over on average. Thus, if the wire fees are larger than
309 : * @e max_wire_fee, each customer is expected to contribute
310 : * $\frac{actual-wire-fee - max_wire_fee}{wire_fee_amortization}$.
311 : * The customer's contribution may be further reduced by the
312 : * difference between @e max_fee and the sum of the deposit fees.
313 : *
314 : * Default is that the merchant is unwilling to pay any wire fees.
315 : */
316 : struct TALER_Amount max_wire_fee;
317 :
318 : /**
319 : * Amount from @e root. This is the amount the merchant expects
320 : * to make, minus @e max_fee.
321 : */
322 : struct TALER_Amount amount;
323 :
324 : /**
325 : * Considering all the coins with the "found_in_db" flag
326 : * set, what is the total amount we were so far paid on
327 : * this contract?
328 : */
329 : struct TALER_Amount total_paid;
330 :
331 : /**
332 : * Considering all the coins with the "found_in_db" flag
333 : * set, what is the total amount we had to pay in deposit
334 : * fees so far on this contract?
335 : */
336 : struct TALER_Amount total_fees_paid;
337 :
338 : /**
339 : * Considering all the coins with the "found_in_db" flag
340 : * set, what is the total amount we already refunded?
341 : */
342 : struct TALER_Amount total_refunded;
343 :
344 : /**
345 : * Wire transfer deadline. How soon would the merchant like the
346 : * wire transfer to be executed?
347 : */
348 : struct GNUNET_TIME_Timestamp wire_transfer_deadline;
349 :
350 : /**
351 : * Timestamp from @e contract_terms.
352 : */
353 : struct GNUNET_TIME_Timestamp timestamp;
354 :
355 : /**
356 : * Refund deadline from @e contract_terms.
357 : */
358 : struct GNUNET_TIME_Timestamp refund_deadline;
359 :
360 : /**
361 : * Deadline for the customer to pay for this proposal.
362 : */
363 : struct GNUNET_TIME_Timestamp pay_deadline;
364 :
365 : /**
366 : * Number of transactions that the wire fees are expected to be
367 : * amortized over. Never zero, defaults (conservateively) to 1.
368 : * May be higher if merchants expect many small transactions to
369 : * be aggregated and thus wire fees to be reasonably amortized
370 : * due to aggregation.
371 : */
372 : uint32_t wire_fee_amortization;
373 :
374 : /**
375 : * Minimum age required for this purchase.
376 : */
377 : unsigned int minimum_age;
378 :
379 : /**
380 : * Number of coins this payment is made of. Length
381 : * of the @e dc array.
382 : */
383 : unsigned int coins_cnt;
384 :
385 : /**
386 : * Number of exchanges involved in the payment. Length
387 : * of the @e eg array.
388 : */
389 : unsigned int num_exchanges;
390 :
391 : /**
392 : * How often have we retried the 'main' transaction?
393 : */
394 : unsigned int retry_counter;
395 :
396 : /**
397 : * Number of batch transactions pending.
398 : */
399 : unsigned int pending_at_eg;
400 :
401 : /**
402 : * Number of coin deposits pending.
403 : */
404 : unsigned int pending;
405 :
406 : /**
407 : * HTTP status code to use for the reply, i.e 200 for "OK".
408 : * Special value UINT_MAX is used to indicate hard errors
409 : * (no reply, return #MHD_NO).
410 : */
411 : unsigned int response_code;
412 :
413 : /**
414 : * #GNUNET_NO if the @e connection was not suspended,
415 : * #GNUNET_YES if the @e connection was suspended,
416 : * #GNUNET_SYSERR if @e connection was resumed to as
417 : * part of #MH_force_pc_resume during shutdown.
418 : */
419 : enum GNUNET_GenericReturnValue suspended;
420 :
421 : };
422 :
423 :
424 : /**
425 : * Active KYC operation with an exchange.
426 : */
427 : struct KycContext
428 : {
429 : /**
430 : * Kept in a DLL.
431 : */
432 : struct KycContext *next;
433 :
434 : /**
435 : * Kept in a DLL.
436 : */
437 : struct KycContext *prev;
438 :
439 : /**
440 : * Looking for the exchange.
441 : */
442 : struct TMH_EXCHANGES_FindOperation *fo;
443 :
444 : /**
445 : * Exchange this is about.
446 : */
447 : char *exchange_url;
448 :
449 : /**
450 : * Merchant instance this is for.
451 : */
452 : struct TMH_MerchantInstance *mi;
453 :
454 : /**
455 : * Wire method we are checking the status of.
456 : */
457 : struct TMH_WireMethod *wm;
458 :
459 : /**
460 : * Handle for the GET /deposits operation.
461 : */
462 : struct TALER_EXCHANGE_DepositGetHandle *dg;
463 :
464 : /**
465 : * Contract we are looking up.
466 : */
467 : struct TALER_PrivateContractHashP h_contract_terms;
468 :
469 : /**
470 : * Coin we are looking up.
471 : */
472 : struct TALER_CoinSpendPublicKeyP coin_pub;
473 :
474 : /**
475 : * Initial DB timestamp.
476 : */
477 : struct GNUNET_TIME_Timestamp kyc_timestamp;
478 :
479 : /**
480 : * Initial KYC status.
481 : */
482 : bool kyc_ok;
483 :
484 : };
485 :
486 :
487 : /**
488 : * Head of active pay context DLL.
489 : */
490 : static struct PayContext *pc_head;
491 :
492 : /**
493 : * Tail of active pay context DLL.
494 : */
495 : static struct PayContext *pc_tail;
496 :
497 : /**
498 : * Head of active KYC context DLL.
499 : */
500 : static struct KycContext *kc_head;
501 :
502 : /**
503 : * Tail of active KYC context DLL.
504 : */
505 : static struct KycContext *kc_tail;
506 :
507 :
508 : /**
509 : * Free resources used by @a kc.
510 : *
511 : * @param[in] kc object to free
512 : */
513 : static void
514 0 : destroy_kc (struct KycContext *kc)
515 : {
516 0 : if (NULL != kc->fo)
517 : {
518 0 : TMH_EXCHANGES_find_exchange_cancel (kc->fo);
519 0 : kc->fo = NULL;
520 : }
521 0 : if (NULL != kc->dg)
522 : {
523 0 : TALER_EXCHANGE_deposits_get_cancel (kc->dg);
524 0 : kc->dg = NULL;
525 : }
526 0 : TMH_instance_decref (kc->mi);
527 0 : kc->mi = NULL;
528 0 : GNUNET_free (kc->exchange_url);
529 0 : GNUNET_CONTAINER_DLL_remove (kc_head,
530 : kc_tail,
531 : kc);
532 0 : GNUNET_free (kc);
533 0 : }
534 :
535 :
536 : /**
537 : * Compute the timeout for a /pay request based on the number of coins
538 : * involved.
539 : *
540 : * @param num_coins number of coins
541 : * @returns timeout for the /pay request
542 : */
543 : static struct GNUNET_TIME_Relative
544 0 : get_pay_timeout (unsigned int num_coins)
545 : {
546 : struct GNUNET_TIME_Relative t;
547 :
548 : /* FIXME: Do some benchmarking to come up with a better timeout.
549 : * We've increased this value so the wallet integration test passes again
550 : * on my (Florian) machine.
551 : */
552 0 : t = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
553 0 : 15 * (1 + (num_coins / 5)));
554 :
555 0 : return t;
556 : }
557 :
558 :
559 : /**
560 : * Abort all pending /deposit operations.
561 : *
562 : * @param pc pay context to abort
563 : */
564 : static void
565 0 : abort_active_deposits (struct PayContext *pc)
566 : {
567 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
568 : "Aborting pending /deposit operations\n");
569 0 : for (unsigned int i = 0; i<pc->coins_cnt; i++)
570 : {
571 0 : struct DepositConfirmation *dci = &pc->dc[i];
572 :
573 0 : if (NULL != dci->dh)
574 : {
575 0 : TALER_EXCHANGE_deposit_cancel (dci->dh);
576 0 : dci->dh = NULL;
577 : }
578 : }
579 0 : }
580 :
581 :
582 : void
583 0 : TMH_force_pc_resume ()
584 : {
585 : struct KycContext *kc;
586 :
587 0 : while (NULL != (kc = kc_head))
588 : {
589 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
590 : "Aborting KYC check at %s\n",
591 : kc->exchange_url);
592 0 : destroy_kc (kc);
593 : }
594 0 : for (struct PayContext *pc = pc_head;
595 : NULL != pc;
596 0 : pc = pc->next)
597 : {
598 0 : abort_active_deposits (pc);
599 0 : if (NULL != pc->timeout_task)
600 : {
601 0 : GNUNET_SCHEDULER_cancel (pc->timeout_task);
602 0 : pc->timeout_task = NULL;
603 : }
604 0 : if (GNUNET_YES == pc->suspended)
605 : {
606 0 : pc->suspended = GNUNET_SYSERR;
607 0 : MHD_resume_connection (pc->connection);
608 : }
609 : }
610 0 : }
611 :
612 :
613 : /**
614 : * Resume the given pay context and send the given response.
615 : * Stores the response in the @a pc and signals MHD to resume
616 : * the connection. Also ensures MHD runs immediately.
617 : *
618 : * @param pc payment context
619 : * @param response_code response code to use
620 : * @param response response data to send back
621 : */
622 : static void
623 0 : resume_pay_with_response (struct PayContext *pc,
624 : unsigned int response_code,
625 : struct MHD_Response *response)
626 : {
627 0 : abort_active_deposits (pc);
628 0 : pc->response_code = response_code;
629 0 : pc->response = response;
630 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
631 : "Resuming /pay handling. HTTP status for our reply is %u.\n",
632 : response_code);
633 0 : if (NULL != pc->timeout_task)
634 : {
635 0 : GNUNET_SCHEDULER_cancel (pc->timeout_task);
636 0 : pc->timeout_task = NULL;
637 : }
638 0 : GNUNET_assert (GNUNET_YES == pc->suspended);
639 0 : pc->suspended = GNUNET_NO;
640 0 : MHD_resume_connection (pc->connection);
641 0 : TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
642 0 : }
643 :
644 :
645 : /**
646 : * Resume payment processing with an error.
647 : *
648 : * @param pc operation to resume
649 : * @param http_status http status code to return
650 : * @param ec taler error code to return
651 : * @param msg human readable error message
652 : */
653 : static void
654 0 : resume_pay_with_error (struct PayContext *pc,
655 : unsigned int http_status,
656 : enum TALER_ErrorCode ec,
657 : const char *msg)
658 : {
659 0 : resume_pay_with_response (pc,
660 : http_status,
661 : TALER_MHD_make_error (ec,
662 : msg));
663 0 : }
664 :
665 :
666 : /**
667 : * Custom cleanup routine for a `struct PayContext`.
668 : *
669 : * @param cls the `struct PayContext` to clean up.
670 : */
671 : static void
672 0 : pay_context_cleanup (void *cls)
673 : {
674 0 : struct PayContext *pc = cls;
675 :
676 0 : if (NULL != pc->timeout_task)
677 : {
678 0 : GNUNET_SCHEDULER_cancel (pc->timeout_task);
679 0 : pc->timeout_task = NULL;
680 : }
681 0 : abort_active_deposits (pc);
682 0 : for (unsigned int i = 0; i<pc->coins_cnt; i++)
683 : {
684 0 : struct DepositConfirmation *dc = &pc->dc[i];
685 :
686 0 : TALER_denom_sig_free (&dc->cdd.denom_sig);
687 0 : GNUNET_free (dc->exchange_url);
688 : }
689 0 : GNUNET_free (pc->dc);
690 0 : for (unsigned int i = 0; i<pc->num_exchanges; i++)
691 : {
692 0 : struct ExchangeGroup *eg = pc->egs[i];
693 :
694 0 : if (NULL != eg->fo)
695 0 : TMH_EXCHANGES_find_exchange_cancel (eg->fo);
696 0 : GNUNET_free (eg);
697 : }
698 0 : GNUNET_free (pc->egs);
699 0 : if (NULL != pc->response)
700 : {
701 0 : MHD_destroy_response (pc->response);
702 0 : pc->response = NULL;
703 : }
704 0 : GNUNET_free (pc->fulfillment_url);
705 0 : GNUNET_free (pc->session_id);
706 0 : GNUNET_CONTAINER_DLL_remove (pc_head,
707 : pc_tail,
708 : pc);
709 0 : GNUNET_free (pc);
710 0 : }
711 :
712 :
713 : /**
714 : * Execute the DB transaction. If required (from
715 : * soft/serialization errors), the transaction can be
716 : * restarted here.
717 : *
718 : * @param pc payment context to transact
719 : */
720 : static void
721 : execute_pay_transaction (struct PayContext *pc);
722 :
723 :
724 : /**
725 : * Function called with detailed wire transfer data.
726 : *
727 : * @param cls a `struct KycContext *`
728 : * @param dr HTTP response data
729 : */
730 : static void
731 0 : deposit_get_callback (
732 : void *cls,
733 : const struct TALER_EXCHANGE_GetDepositResponse *dr)
734 : {
735 0 : struct KycContext *kc = cls;
736 : enum GNUNET_DB_QueryStatus qs;
737 : struct GNUNET_TIME_Timestamp now;
738 :
739 0 : kc->dg = NULL;
740 0 : now = GNUNET_TIME_timestamp_get ();
741 0 : switch (dr->hr.http_status)
742 : {
743 0 : case MHD_HTTP_OK:
744 0 : qs = TMH_db->account_kyc_set_status (
745 0 : TMH_db->cls,
746 0 : kc->mi->settings.id,
747 0 : &kc->wm->h_wire,
748 0 : kc->exchange_url,
749 : 0LL,
750 : NULL, /* no signature */
751 : NULL, /* no signature */
752 : now,
753 : true);
754 0 : GNUNET_break (qs > 0);
755 0 : break;
756 0 : case MHD_HTTP_ACCEPTED:
757 0 : qs = TMH_db->account_kyc_set_status (
758 0 : TMH_db->cls,
759 0 : kc->mi->settings.id,
760 0 : &kc->wm->h_wire,
761 0 : kc->exchange_url,
762 : dr->details.accepted.requirement_row,
763 : NULL, /* no signature */
764 : NULL, /* no signature */
765 : now,
766 0 : dr->details.accepted.kyc_ok);
767 0 : GNUNET_break (qs > 0);
768 0 : break;
769 0 : default:
770 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
771 : "KYC check failed at %s with unexpected status %u\n",
772 : kc->exchange_url,
773 : dr->hr.http_status);
774 : }
775 0 : destroy_kc (kc);
776 0 : }
777 :
778 :
779 : /**
780 : * Function called with the result of our exchange lookup.
781 : *
782 : * @param cls the `struct KycContext`
783 : * @param hr HTTP response details
784 : * @param exchange_handle NULL if exchange was not found to be acceptable
785 : * @param payto_uri payto://-URI of the exchange
786 : * @param wire_fee current applicable fee for dealing with @a exchange_handle,
787 : * NULL if not available
788 : * @param exchange_trusted true if this exchange is
789 : * trusted by config
790 : */
791 : static void
792 0 : process_kyc_with_exchange (
793 : void *cls,
794 : const struct TALER_EXCHANGE_HttpResponse *hr,
795 : struct TALER_EXCHANGE_Handle *exchange_handle,
796 : const char *payto_uri,
797 : const struct TALER_Amount *wire_fee,
798 : bool exchange_trusted)
799 : {
800 0 : struct KycContext *kc = cls;
801 :
802 0 : kc->fo = NULL;
803 0 : if (NULL == exchange_handle)
804 : {
805 0 : destroy_kc (kc);
806 0 : return;
807 : }
808 0 : kc->dg = TALER_EXCHANGE_deposits_get (exchange_handle,
809 0 : &kc->mi->merchant_priv,
810 0 : &kc->wm->h_wire,
811 0 : &kc->h_contract_terms,
812 0 : &kc->coin_pub,
813 : &deposit_get_callback,
814 : kc);
815 0 : if (NULL == kc->dg)
816 : {
817 0 : GNUNET_break (0);
818 0 : destroy_kc (kc);
819 : }
820 : }
821 :
822 :
823 : /**
824 : * Function called from ``account_kyc_get_status``
825 : * with KYC status information for this merchant.
826 : *
827 : * @param cls a `struct KycContext *`
828 : * @param h_wire hash of the wire account
829 : * @param exchange_kyc_serial serial number for the KYC process at the exchange, 0 if unknown
830 : * @param payto_uri payto:// URI of the merchant's bank account
831 : * @param exchange_url base URL of the exchange for which this is a status
832 : * @param last_check when did we last get an update on our KYC status from the exchange
833 : * @param kyc_ok true if we satisfied the KYC requirements
834 : */
835 : static void
836 0 : kyc_cb (
837 : void *cls,
838 : const struct TALER_MerchantWireHashP *h_wire,
839 : uint64_t exchange_kyc_serial,
840 : const char *payto_uri,
841 : const char *exchange_url,
842 : struct GNUNET_TIME_Timestamp last_check,
843 : bool kyc_ok)
844 : {
845 0 : struct KycContext *kc = cls;
846 :
847 0 : kc->kyc_timestamp = last_check;
848 0 : kc->kyc_ok = kyc_ok;
849 0 : }
850 :
851 :
852 : /**
853 : * Check for our KYC status at @a exchange_url for the
854 : * payment of @a pc. First checks if we already have a
855 : * positive result from the exchange, and if not checks
856 : * with the exchange.
857 : *
858 : * @param pc payment context to use as starting point
859 : * @param eg exchange group of the exchange we are triggering on
860 : */
861 : static void
862 0 : check_kyc (struct PayContext *pc,
863 : const struct ExchangeGroup *eg)
864 : {
865 : enum GNUNET_DB_QueryStatus qs;
866 : struct KycContext *kc;
867 :
868 0 : kc = GNUNET_new (struct KycContext);
869 0 : qs = TMH_db->account_kyc_get_status (TMH_db->cls,
870 0 : pc->hc->instance->settings.id,
871 0 : &pc->wm->h_wire,
872 : eg->exchange_url,
873 : &kyc_cb,
874 : kc);
875 0 : if (qs < 0)
876 : {
877 0 : GNUNET_break (0);
878 0 : GNUNET_free (kc);
879 0 : return;
880 : }
881 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
882 : {
883 0 : if (kc->kyc_ok)
884 : {
885 0 : GNUNET_free (kc);
886 0 : return; /* we are done */
887 : }
888 0 : if (GNUNET_TIME_relative_cmp (
889 : GNUNET_TIME_absolute_get_duration (
890 : kc->kyc_timestamp.abs_time),
891 : <,
892 : KYC_RETRY_FREQUENCY))
893 : {
894 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
895 : "Not re-checking KYC status at `%s', as we already recently asked\n",
896 : eg->exchange_url);
897 0 : GNUNET_free (kc);
898 0 : return;
899 : }
900 : }
901 0 : kc->mi = pc->hc->instance;
902 0 : kc->mi->rc++;
903 0 : kc->wm = pc->wm;
904 0 : kc->exchange_url = GNUNET_strdup (eg->exchange_url);
905 0 : kc->h_contract_terms = pc->h_contract_terms;
906 : /* find one of the coins of the batch */
907 0 : for (unsigned int i = 0; i<pc->coins_cnt; i++)
908 : {
909 0 : struct DepositConfirmation *dc = &pc->dc[i];
910 :
911 0 : if (0 != strcmp (eg->exchange_url,
912 0 : pc->dc[i].exchange_url))
913 0 : continue;
914 0 : kc->coin_pub = dc->cdd.coin_pub;
915 0 : break;
916 : }
917 0 : GNUNET_CONTAINER_DLL_insert (kc_head,
918 : kc_tail,
919 : kc);
920 0 : kc->fo = TMH_EXCHANGES_find_exchange (kc->exchange_url,
921 : NULL,
922 : GNUNET_NO,
923 : &process_kyc_with_exchange,
924 : kc);
925 0 : if (NULL == kc->fo)
926 : {
927 0 : GNUNET_break (0);
928 0 : destroy_kc (kc);
929 : }
930 : }
931 :
932 :
933 : /**
934 : * Handle case where the batch deposit completed
935 : * with a status of #MHD_HTTP_OK.
936 : *
937 : * @param eg group that completed
938 : * @param dr response from the server
939 : */
940 : static void
941 0 : handle_batch_deposit_ok (struct ExchangeGroup *eg,
942 : const struct TALER_EXCHANGE_BatchDepositResult *dr)
943 : {
944 0 : struct PayContext *pc = eg->pc;
945 0 : enum GNUNET_DB_QueryStatus qs
946 : = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
947 :
948 : /* store result to DB */
949 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
950 : "Storing successful payment %s (%s) at instance `%s'\n",
951 : pc->hc->infix,
952 : GNUNET_h2s (&pc->h_contract_terms.hash),
953 : pc->hc->instance->settings.id);
954 0 : for (unsigned int r = 0; r<MAX_RETRIES; r++)
955 : {
956 0 : unsigned int j = 0;
957 :
958 0 : TMH_db->preflight (TMH_db->cls);
959 0 : if (GNUNET_OK !=
960 0 : TMH_db->start (TMH_db->cls,
961 : "batch-deposit-insert-confirmation"))
962 : {
963 0 : resume_pay_with_response (
964 : pc,
965 : MHD_HTTP_INTERNAL_SERVER_ERROR,
966 0 : TALER_MHD_MAKE_JSON_PACK (
967 : TALER_JSON_pack_ec (
968 : TALER_EC_GENERIC_DB_START_FAILED),
969 : TMH_pack_exchange_reply (&dr->hr)));
970 0 : return;
971 : }
972 0 : for (unsigned int i = 0; i<pc->coins_cnt; i++)
973 : {
974 0 : struct DepositConfirmation *dc = &pc->dc[i];
975 :
976 0 : if (0 != strcmp (eg->exchange_url,
977 0 : pc->dc[i].exchange_url))
978 0 : continue;
979 0 : if (dc->found_in_db)
980 0 : continue;
981 : /* NOTE: We might want to check if the order was fully paid concurrently
982 : by some other wallet here, and if so, issue an auto-refund. Right now,
983 : it is possible to over-pay if two wallets literally make a concurrent
984 : payment, as the earlier check for 'paid' is not in the same transaction
985 : scope as this 'insert' operation. */
986 0 : GNUNET_assert (j < dr->details.success.num_signatures);
987 0 : qs = TMH_db->insert_deposit (
988 0 : TMH_db->cls,
989 0 : pc->hc->instance->settings.id,
990 : dr->details.success.deposit_timestamp,
991 0 : &pc->h_contract_terms,
992 0 : &dc->cdd.coin_pub,
993 0 : dc->exchange_url,
994 0 : &dc->cdd.amount,
995 0 : &dc->deposit_fee,
996 0 : &dc->refund_fee,
997 0 : &dc->wire_fee,
998 0 : &pc->wm->h_wire,
999 0 : &dr->details.success.exchange_sigs[j++],
1000 : dr->details.success.exchange_pub);
1001 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
1002 : {
1003 0 : TMH_db->rollback (TMH_db->cls);
1004 0 : break;
1005 : }
1006 0 : if (0 > qs)
1007 : {
1008 : /* Always report on hard error as well to enable diagnostics */
1009 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
1010 : /* Forward error including 'proof' for the body */
1011 0 : resume_pay_with_error (pc,
1012 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1013 : TALER_EC_GENERIC_DB_STORE_FAILED,
1014 : "insert_deposit");
1015 0 : return;
1016 : }
1017 : }
1018 0 : qs = TMH_db->commit (TMH_db->cls);
1019 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
1020 : {
1021 0 : TMH_db->rollback (TMH_db->cls);
1022 0 : continue;
1023 : }
1024 0 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
1025 : {
1026 0 : GNUNET_break (0);
1027 0 : resume_pay_with_error (pc,
1028 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1029 : TALER_EC_GENERIC_DB_COMMIT_FAILED,
1030 : "insert_deposit");
1031 : }
1032 0 : break; /* DB transaction succeeded */
1033 : } /* FOR DB retries */
1034 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
1035 : {
1036 0 : resume_pay_with_error (pc,
1037 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1038 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
1039 : "insert_deposit");
1040 0 : return;
1041 : }
1042 :
1043 : /* Transaction is done, mark affected coins as complete as well. */
1044 0 : for (unsigned int i = 0; i<pc->coins_cnt; i++)
1045 : {
1046 0 : struct DepositConfirmation *dc = &pc->dc[i];
1047 :
1048 0 : if (0 != strcmp (eg->exchange_url,
1049 0 : pc->dc[i].exchange_url))
1050 0 : continue;
1051 0 : if (dc->found_in_db)
1052 0 : continue;
1053 0 : dc->found_in_db = true; /* well, at least NOW it'd be true ;-) */
1054 0 : pc->pending--;
1055 : }
1056 0 : check_kyc (pc,
1057 : eg);
1058 : }
1059 :
1060 :
1061 : /**
1062 : * Callback to handle a batch deposit permission's response.
1063 : *
1064 : * @param cls a `struct ExchangeGroup`
1065 : * @param dr HTTP response code details
1066 : */
1067 : static void
1068 0 : batch_deposit_cb (
1069 : void *cls,
1070 : const struct TALER_EXCHANGE_BatchDepositResult *dr)
1071 : {
1072 0 : struct ExchangeGroup *eg = cls;
1073 0 : struct PayContext *pc = eg->pc;
1074 :
1075 0 : eg->bdh = NULL;
1076 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1077 : "Batch deposit completed with status %u\n",
1078 : dr->hr.http_status);
1079 0 : GNUNET_assert (GNUNET_YES == pc->suspended);
1080 0 : pc->pending_at_eg--;
1081 0 : switch (dr->hr.http_status)
1082 : {
1083 0 : case MHD_HTTP_OK:
1084 0 : handle_batch_deposit_ok (eg,
1085 : dr);
1086 0 : if (0 == pc->pending_at_eg)
1087 0 : execute_pay_transaction (eg->pc);
1088 0 : return;
1089 0 : default:
1090 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1091 : "Deposit operation failed with HTTP code %u/%d\n",
1092 : dr->hr.http_status,
1093 : (int) dr->hr.ec);
1094 : /* Transaction failed */
1095 0 : if (5 == dr->hr.http_status / 100)
1096 : {
1097 : /* internal server error at exchange */
1098 0 : resume_pay_with_response (pc,
1099 : MHD_HTTP_BAD_GATEWAY,
1100 0 : TALER_MHD_MAKE_JSON_PACK (
1101 : TALER_JSON_pack_ec (
1102 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS),
1103 : TMH_pack_exchange_reply (&dr->hr)));
1104 0 : return;
1105 : }
1106 0 : if (NULL == dr->hr.reply)
1107 : {
1108 : /* We can't do anything meaningful here, the exchange did something wrong */
1109 0 : resume_pay_with_response (
1110 : pc,
1111 : MHD_HTTP_BAD_GATEWAY,
1112 0 : TALER_MHD_MAKE_JSON_PACK (
1113 : TALER_JSON_pack_ec (
1114 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_REPLY_MALFORMED),
1115 : TMH_pack_exchange_reply (&dr->hr)));
1116 0 : return;
1117 : }
1118 :
1119 : /* Forward error, adding the "exchange_url" for which the
1120 : error was being generated */
1121 0 : if (TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS == dr->hr.ec)
1122 : {
1123 0 : resume_pay_with_response (
1124 : pc,
1125 : MHD_HTTP_CONFLICT,
1126 0 : TALER_MHD_MAKE_JSON_PACK (
1127 : TALER_JSON_pack_ec (
1128 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS),
1129 : TMH_pack_exchange_reply (&dr->hr),
1130 : GNUNET_JSON_pack_data_auto ("exchange_url",
1131 : &eg->exchange_url)));
1132 0 : return;
1133 : }
1134 0 : resume_pay_with_response (
1135 : pc,
1136 : MHD_HTTP_BAD_GATEWAY,
1137 0 : TALER_MHD_MAKE_JSON_PACK (
1138 : TALER_JSON_pack_ec (
1139 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS),
1140 : TMH_pack_exchange_reply (&dr->hr),
1141 : GNUNET_JSON_pack_data_auto ("exchange_url",
1142 : &eg->exchange_url)));
1143 0 : return;
1144 : } /* end switch */
1145 : }
1146 :
1147 :
1148 : /**
1149 : * Function called with the result of our exchange lookup.
1150 : *
1151 : * @param cls the `struct ExchangeGroup`
1152 : * @param hr HTTP response details
1153 : * @param exchange_handle NULL if exchange was not found to be acceptable
1154 : * @param payto_uri payto://-URI of the exchange
1155 : * @param wire_fee current applicable fee for dealing with @a exchange_handle,
1156 : * NULL if not available
1157 : * @param exchange_trusted true if this exchange is
1158 : * trusted by config
1159 : */
1160 : static void
1161 0 : process_pay_with_exchange (
1162 : void *cls,
1163 : const struct TALER_EXCHANGE_HttpResponse *hr,
1164 : struct TALER_EXCHANGE_Handle *exchange_handle,
1165 : const char *payto_uri,
1166 : const struct TALER_Amount *wire_fee,
1167 : bool exchange_trusted)
1168 : {
1169 0 : struct ExchangeGroup *eg = cls;
1170 0 : struct PayContext *pc = eg->pc;
1171 0 : struct TMH_HandlerContext *hc = pc->hc;
1172 : const struct TALER_EXCHANGE_Keys *keys;
1173 : unsigned int group_size;
1174 :
1175 : (void) payto_uri;
1176 0 : eg->fo = NULL;
1177 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1178 : "Processing payment with exchange %s\n",
1179 : (NULL == exchange_handle)
1180 : ? "<null>"
1181 : : TALER_EXCHANGE_get_base_url (exchange_handle));
1182 0 : GNUNET_assert (GNUNET_YES == pc->suspended);
1183 0 : if (NULL == hr)
1184 : {
1185 0 : pc->pending_at_eg--;
1186 0 : resume_pay_with_response (
1187 : pc,
1188 : MHD_HTTP_GATEWAY_TIMEOUT,
1189 0 : TALER_MHD_MAKE_JSON_PACK (
1190 : TALER_JSON_pack_ec (TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT)));
1191 0 : return;
1192 : }
1193 0 : if ( (MHD_HTTP_OK != hr->http_status) ||
1194 : (NULL == exchange_handle) )
1195 : {
1196 0 : pc->pending_at_eg--;
1197 0 : resume_pay_with_response (
1198 : pc,
1199 : MHD_HTTP_BAD_GATEWAY,
1200 0 : TALER_MHD_MAKE_JSON_PACK (
1201 : TALER_JSON_pack_ec (
1202 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE),
1203 : TMH_pack_exchange_reply (hr)));
1204 0 : return;
1205 : }
1206 0 : keys = TALER_EXCHANGE_get_keys (exchange_handle);
1207 0 : if (NULL == keys)
1208 : {
1209 0 : pc->pending_at_eg--;
1210 0 : GNUNET_break (0); /* should not be possible if HTTP status is #MHD_HTTP_OK */
1211 0 : resume_pay_with_error (pc,
1212 : MHD_HTTP_BAD_GATEWAY,
1213 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE,
1214 : NULL);
1215 0 : return;
1216 : }
1217 :
1218 : /* Initiate /batch-deposit operation for all coins of
1219 : the current exchange (!) */
1220 0 : group_size = 0;
1221 0 : for (unsigned int i = 0; i<pc->coins_cnt; i++)
1222 : {
1223 0 : struct DepositConfirmation *dc = &pc->dc[i];
1224 : const struct TALER_EXCHANGE_DenomPublicKey *denom_details;
1225 : unsigned int http_status;
1226 : enum TALER_ErrorCode ec;
1227 0 : bool is_age_restricted_denom = false;
1228 :
1229 0 : if (0 != strcmp (eg->exchange_url,
1230 0 : pc->dc[i].exchange_url))
1231 0 : continue;
1232 0 : if (dc->found_in_db)
1233 0 : continue;
1234 :
1235 : denom_details
1236 0 : = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
1237 0 : &dc->cdd.h_denom_pub);
1238 0 : if (NULL == denom_details)
1239 : {
1240 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1241 : "Denomination not found, re-fetching /keys\n");
1242 0 : if (! eg->tried_force_keys)
1243 : {
1244 : /* let's try *forcing* a re-download of /keys from the exchange.
1245 : Maybe the wallet has seen /keys that we missed. */
1246 0 : eg->tried_force_keys = true;
1247 0 : eg->fo = TMH_EXCHANGES_find_exchange (eg->exchange_url,
1248 0 : pc->wm->wire_method,
1249 : GNUNET_YES,
1250 : &process_pay_with_exchange,
1251 : eg);
1252 0 : if (NULL != eg->fo)
1253 0 : return;
1254 : }
1255 : /* Forcing failed or we already did it, give up */
1256 0 : pc->pending_at_eg--;
1257 0 : resume_pay_with_response (
1258 : pc,
1259 : MHD_HTTP_BAD_REQUEST,
1260 0 : TALER_MHD_MAKE_JSON_PACK (
1261 : TALER_JSON_pack_ec (
1262 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND),
1263 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1264 : &dc->cdd.h_denom_pub),
1265 : GNUNET_JSON_pack_allow_null (
1266 : GNUNET_JSON_pack_object_incref (
1267 : "exchange_keys",
1268 : (json_t *) TALER_EXCHANGE_get_keys_raw (exchange_handle)))));
1269 0 : return;
1270 : }
1271 0 : dc->deposit_fee = denom_details->fees.deposit;
1272 0 : dc->refund_fee = denom_details->fees.refund;
1273 :
1274 0 : if (GNUNET_OK !=
1275 0 : TMH_AUDITORS_check_dk (exchange_handle,
1276 : denom_details,
1277 : exchange_trusted,
1278 : &http_status,
1279 : &ec))
1280 : {
1281 0 : if (! eg->tried_force_keys)
1282 : {
1283 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1284 : "Denomination not audited by trusted auditor, re-fetching /keys\n");
1285 : /* let's try *forcing* a re-download of /keys from the exchange.
1286 : Maybe the wallet has seen auditors that we missed. */
1287 0 : eg->tried_force_keys = true;
1288 0 : eg->fo = TMH_EXCHANGES_find_exchange (eg->exchange_url,
1289 0 : pc->wm->wire_method,
1290 : GNUNET_YES,
1291 : &process_pay_with_exchange,
1292 : eg);
1293 0 : if (NULL != eg->fo)
1294 0 : return;
1295 : }
1296 0 : pc->pending_at_eg--;
1297 0 : resume_pay_with_response (
1298 : pc,
1299 : http_status,
1300 0 : TALER_MHD_MAKE_JSON_PACK (
1301 : TALER_JSON_pack_ec (ec),
1302 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1303 : &denom_details->h_key)));
1304 0 : return;
1305 : }
1306 :
1307 :
1308 : /* Now that we have the details about the denomination, we can verify age
1309 : * restriction requirements, if applicable. Note that denominations with an
1310 : * age_mask equal to zero always pass the age verification. */
1311 0 : is_age_restricted_denom = (0 != denom_details->key.age_mask.bits);
1312 :
1313 0 : if (is_age_restricted_denom &&
1314 0 : (0 < pc->minimum_age))
1315 0 : {
1316 : /* Minimum age given and restricted coin provided: We need to verify the
1317 : * minimum age */
1318 0 : unsigned int code = 0;
1319 :
1320 0 : if (dc->no_age_commitment)
1321 : {
1322 0 : GNUNET_break_op (0);
1323 0 : code = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_MISSING;
1324 0 : goto AGE_FAIL;
1325 : }
1326 0 : dc->age_commitment.mask = denom_details->key.age_mask;
1327 0 : if (((int) (dc->age_commitment.num + 1)) !=
1328 0 : __builtin_popcount (dc->age_commitment.mask.bits))
1329 : {
1330 0 : GNUNET_break_op (0);
1331 0 : code =
1332 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_SIZE_MISMATCH;
1333 0 : goto AGE_FAIL;
1334 : }
1335 0 : if (GNUNET_OK !=
1336 0 : TALER_age_commitment_verify (
1337 0 : &dc->age_commitment,
1338 0 : pc->minimum_age,
1339 0 : &dc->minimum_age_sig))
1340 0 : code = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_VERIFICATION_FAILED;
1341 0 : AGE_FAIL:
1342 0 : if (0 < code)
1343 : {
1344 0 : pc->pending_at_eg--;
1345 0 : GNUNET_free (dc->age_commitment.keys);
1346 0 : resume_pay_with_response (
1347 : pc,
1348 : MHD_HTTP_BAD_REQUEST,
1349 0 : TALER_MHD_MAKE_JSON_PACK (
1350 : TALER_JSON_pack_ec (code),
1351 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1352 : &denom_details->h_key)));
1353 0 : return;
1354 : }
1355 :
1356 : /* Age restriction successfully verified!
1357 : * Calculate the hash of the age commitment. */
1358 0 : TALER_age_commitment_hash (&dc->age_commitment,
1359 : &dc->cdd.h_age_commitment);
1360 0 : GNUNET_free (dc->age_commitment.keys);
1361 : }
1362 0 : else if (is_age_restricted_denom && dc->no_h_age_commitment)
1363 : {
1364 : /* The contract did not ask for a minimum_age but the client paid
1365 : * with a coin that has age restriction enabled. We lack the hash
1366 : * of the age commitment in this case in order to verify the coin
1367 : * and to deposit it with the exchange. */
1368 0 : pc->pending_at_eg--;
1369 0 : GNUNET_break_op (0);
1370 0 : resume_pay_with_response (
1371 : pc,
1372 : MHD_HTTP_BAD_REQUEST,
1373 0 : TALER_MHD_MAKE_JSON_PACK (
1374 : TALER_JSON_pack_ec (
1375 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_COMMITMENT_HASH_MISSING),
1376 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1377 : &denom_details->h_key)));
1378 0 : return;
1379 : }
1380 0 : group_size++;
1381 : }
1382 :
1383 0 : if (0 == group_size)
1384 : {
1385 0 : GNUNET_break (0);
1386 0 : pc->pending_at_eg--;
1387 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1388 : "Group size zero, %u batch transactions remain pending\n",
1389 : pc->pending_at_eg);
1390 0 : if (0 == pc->pending_at_eg)
1391 0 : execute_pay_transaction (pc);
1392 0 : return;
1393 : }
1394 :
1395 0 : {
1396 0 : struct TALER_EXCHANGE_CoinDepositDetail cdds[group_size];
1397 0 : struct TALER_EXCHANGE_DepositContractDetail dcd = {
1398 : .wire_deadline = pc->wire_transfer_deadline,
1399 0 : .merchant_payto_uri = pc->wm->payto_uri,
1400 0 : .wire_salt = pc->wm->wire_salt,
1401 : .h_contract_terms = pc->h_contract_terms,
1402 : .extension_details = NULL /* FIXME #7270 */,
1403 : .timestamp = pc->timestamp,
1404 0 : .merchant_pub = hc->instance->merchant_pub,
1405 : .refund_deadline = pc->refund_deadline
1406 : };
1407 : enum TALER_ErrorCode ec;
1408 :
1409 0 : for (unsigned int i = 0; i<pc->coins_cnt; i++)
1410 : {
1411 0 : struct DepositConfirmation *dc = &pc->dc[i];
1412 :
1413 0 : if (dc->found_in_db)
1414 0 : continue;
1415 0 : if (0 != strcmp (dc->exchange_url,
1416 : eg->exchange_url))
1417 0 : continue;
1418 0 : cdds[i] = dc->cdd;
1419 0 : dc->wire_fee = *wire_fee;
1420 0 : GNUNET_assert (NULL != pc->wm);
1421 : }
1422 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1423 : "Initiating batch deposit with %u coins\n",
1424 : group_size);
1425 0 : eg->bdh = TALER_EXCHANGE_batch_deposit (exchange_handle,
1426 : &dcd,
1427 : group_size,
1428 : cdds,
1429 : &batch_deposit_cb,
1430 : eg,
1431 : &ec);
1432 0 : if (NULL == eg->bdh)
1433 : {
1434 : /* Signature was invalid or some other constraint was not satisfied. If
1435 : the exchange was unavailable, we'd get that information in the
1436 : callback. */
1437 0 : pc->pending_at_eg--;
1438 0 : GNUNET_break_op (0);
1439 0 : resume_pay_with_response (
1440 : pc,
1441 : TALER_ErrorCode_get_http_status_safe (ec),
1442 0 : TALER_MHD_MAKE_JSON_PACK (
1443 : TALER_JSON_pack_ec (ec),
1444 : GNUNET_JSON_pack_string ("exchange_url",
1445 : eg->exchange_url)));
1446 0 : return;
1447 : }
1448 0 : if (TMH_force_audit)
1449 0 : TALER_EXCHANGE_batch_deposit_force_dc (eg->bdh);
1450 : }
1451 : }
1452 :
1453 :
1454 : /**
1455 : * Start batch deposits for all exchanges involved
1456 : * in this payment.
1457 : *
1458 : * @param pc payment context we are processing
1459 : */
1460 : static void
1461 0 : start_batch_deposits (struct PayContext *pc)
1462 : {
1463 0 : for (unsigned int i = 0; i<pc->num_exchanges; i++)
1464 : {
1465 0 : struct ExchangeGroup *eg = pc->egs[i];
1466 0 : bool have_coins = false;
1467 :
1468 0 : for (unsigned int j = 0; j<pc->coins_cnt; j++)
1469 : {
1470 0 : struct DepositConfirmation *dc = &pc->dc[j];
1471 :
1472 0 : if (0 != strcmp (eg->exchange_url,
1473 0 : pc->dc[j].exchange_url))
1474 0 : continue;
1475 0 : if (dc->found_in_db)
1476 0 : continue;
1477 0 : have_coins = true;
1478 0 : break;
1479 : }
1480 0 : if (! have_coins)
1481 0 : continue; /* no coins left to deposit at this exchange */
1482 0 : eg->fo = TMH_EXCHANGES_find_exchange (eg->exchange_url,
1483 0 : pc->wm->wire_method,
1484 : GNUNET_NO,
1485 : &process_pay_with_exchange,
1486 : eg);
1487 0 : if (NULL == eg->fo)
1488 : {
1489 0 : GNUNET_break (0);
1490 0 : resume_pay_with_error (pc,
1491 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1492 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LOOKUP_FAILED,
1493 : "Failed to lookup exchange by URL");
1494 0 : return;
1495 : }
1496 0 : pc->pending_at_eg++;
1497 : }
1498 0 : if (0 == pc->pending_at_eg)
1499 0 : execute_pay_transaction (pc);
1500 : }
1501 :
1502 :
1503 : /**
1504 : * Function called with information about a coin that was deposited.
1505 : *
1506 : * @param cls closure
1507 : * @param exchange_url exchange where @a coin_pub was deposited
1508 : * @param coin_pub public key of the coin
1509 : * @param amount_with_fee amount the exchange will deposit for this coin
1510 : * @param deposit_fee fee the exchange will charge for this coin
1511 : * @param refund_fee fee the exchange will charge for refunding this coin
1512 : * @param wire_fee wire fee the exchange of this coin charges
1513 : */
1514 : static void
1515 0 : check_coin_paid (void *cls,
1516 : const char *exchange_url,
1517 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
1518 : const struct TALER_Amount *amount_with_fee,
1519 : const struct TALER_Amount *deposit_fee,
1520 : const struct TALER_Amount *refund_fee,
1521 : const struct TALER_Amount *wire_fee)
1522 : {
1523 0 : struct PayContext *pc = cls;
1524 :
1525 0 : for (unsigned int i = 0; i<pc->coins_cnt; i++)
1526 : {
1527 0 : struct DepositConfirmation *dc = &pc->dc[i];
1528 :
1529 0 : if (dc->found_in_db)
1530 0 : continue; /* processed earlier, skip "expensive" memcmp() */
1531 : /* Get matching coin from results*/
1532 0 : if ( (0 != GNUNET_memcmp (coin_pub,
1533 0 : &dc->cdd.coin_pub)) ||
1534 : (0 !=
1535 0 : strcmp (exchange_url,
1536 0 : dc->exchange_url)) ||
1537 0 : (0 != TALER_amount_cmp (amount_with_fee,
1538 0 : &dc->cdd.amount)) )
1539 0 : continue; /* does not match, skip */
1540 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1541 : "Deposit of coin `%s' already in our DB.\n",
1542 : TALER_B2S (coin_pub));
1543 :
1544 0 : GNUNET_assert (0 <=
1545 : TALER_amount_add (&pc->total_paid,
1546 : &pc->total_paid,
1547 : amount_with_fee));
1548 0 : GNUNET_assert (0 <=
1549 : TALER_amount_add (&pc->total_fees_paid,
1550 : &pc->total_fees_paid,
1551 : deposit_fee));
1552 0 : dc->deposit_fee = *deposit_fee;
1553 0 : dc->refund_fee = *refund_fee;
1554 0 : dc->wire_fee = *wire_fee;
1555 0 : dc->cdd.amount = *amount_with_fee;
1556 0 : dc->found_in_db = true;
1557 0 : pc->pending--;
1558 : }
1559 0 : }
1560 :
1561 :
1562 : /**
1563 : * Function called with information about a refund. Check if this coin was
1564 : * claimed by the wallet for the transaction, and if so add the refunded
1565 : * amount to the pc's "total_refunded" amount.
1566 : *
1567 : * @param cls closure with a `struct PayContext`
1568 : * @param coin_pub public coin from which the refund comes from
1569 : * @param refund_amount refund amount which is being taken from @a coin_pub
1570 : */
1571 : static void
1572 0 : check_coin_refunded (void *cls,
1573 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
1574 : const struct TALER_Amount *refund_amount)
1575 : {
1576 0 : struct PayContext *pc = cls;
1577 :
1578 : /* We look at refunds here that apply to the coins
1579 : that the customer is currently trying to pay us with.
1580 :
1581 : Such refunds are not "normal" refunds, but abort-pay refunds, which are
1582 : given in the case that the wallet aborts the payment.
1583 : In the case the wallet then decides to complete the payment *after* doing
1584 : an abort-pay refund (an unusual but possible case), we need
1585 : to make sure that existing refunds are accounted for. */
1586 :
1587 0 : for (unsigned int i = 0; i<pc->coins_cnt; i++)
1588 : {
1589 0 : struct DepositConfirmation *dc = &pc->dc[i];
1590 :
1591 : /* Get matching coins from results. */
1592 0 : if (0 != GNUNET_memcmp (coin_pub,
1593 : &dc->cdd.coin_pub))
1594 0 : continue;
1595 0 : GNUNET_assert (0 <=
1596 : TALER_amount_add (&pc->total_refunded,
1597 : &pc->total_refunded,
1598 : refund_amount));
1599 0 : break;
1600 : }
1601 0 : }
1602 :
1603 :
1604 : /**
1605 : * Check whether the amount paid is sufficient to cover the price.
1606 : *
1607 : * @param pc payment context to check
1608 : * @return true if the payment is sufficient, false if it is
1609 : * insufficient
1610 : */
1611 : static bool
1612 0 : check_payment_sufficient (struct PayContext *pc)
1613 : {
1614 : struct TALER_Amount acc_fee;
1615 : struct TALER_Amount acc_amount;
1616 : struct TALER_Amount final_amount;
1617 : struct TALER_Amount wire_fee_delta;
1618 : struct TALER_Amount wire_fee_customer_contribution;
1619 : struct TALER_Amount total_wire_fee;
1620 : struct TALER_Amount total_needed;
1621 :
1622 0 : if (0 == pc->coins_cnt)
1623 : {
1624 0 : return ((0 == pc->amount.value) &&
1625 0 : (0 == pc->amount.fraction));
1626 : }
1627 :
1628 0 : acc_fee = pc->dc[0].deposit_fee;
1629 0 : total_wire_fee = pc->dc[0].wire_fee;
1630 0 : acc_amount = pc->dc[0].cdd.amount;
1631 :
1632 : /**
1633 : * This loops calculates what are the deposit fee / total
1634 : * amount with fee / and wire fee, for all the coins.
1635 : */
1636 0 : for (unsigned int i = 1; i<pc->coins_cnt; i++)
1637 : {
1638 0 : struct DepositConfirmation *dc = &pc->dc[i];
1639 :
1640 0 : GNUNET_assert (dc->found_in_db);
1641 0 : if ( (0 >
1642 0 : TALER_amount_add (&acc_fee,
1643 0 : &dc->deposit_fee,
1644 0 : &acc_fee)) ||
1645 : (0 >
1646 0 : TALER_amount_add (&acc_amount,
1647 0 : &dc->cdd.amount,
1648 : &acc_amount)) )
1649 : {
1650 0 : GNUNET_break (0);
1651 : /* Overflow in these amounts? Very strange. */
1652 0 : resume_pay_with_error (pc,
1653 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1654 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
1655 : "Overflow adding up amounts");
1656 0 : return false;
1657 : }
1658 0 : if (1 ==
1659 0 : TALER_amount_cmp (&dc->deposit_fee,
1660 0 : &dc->cdd.amount))
1661 : {
1662 0 : GNUNET_break_op (0);
1663 0 : resume_pay_with_error (pc,
1664 : MHD_HTTP_BAD_REQUEST,
1665 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_FEES_EXCEED_PAYMENT,
1666 : "Deposit fees exceed coin's contribution");
1667 0 : return false;
1668 : }
1669 :
1670 : /* If exchange differs, add wire fee */
1671 : {
1672 0 : bool new_exchange = true;
1673 :
1674 0 : for (unsigned int j = 0; j<i; j++)
1675 0 : if (0 == strcasecmp (dc->exchange_url,
1676 0 : pc->dc[j].exchange_url))
1677 : {
1678 0 : new_exchange = false;
1679 0 : break;
1680 : }
1681 :
1682 0 : if (! new_exchange)
1683 0 : continue;
1684 :
1685 0 : if (GNUNET_OK !=
1686 0 : TALER_amount_cmp_currency (&total_wire_fee,
1687 0 : &dc->wire_fee))
1688 : {
1689 0 : GNUNET_break_op (0);
1690 0 : resume_pay_with_error (pc,
1691 : MHD_HTTP_CONFLICT,
1692 : TALER_EC_GENERIC_CURRENCY_MISMATCH,
1693 : total_wire_fee.currency);
1694 0 : return false;
1695 : }
1696 0 : if (0 >
1697 0 : TALER_amount_add (&total_wire_fee,
1698 : &total_wire_fee,
1699 0 : &dc->wire_fee))
1700 : {
1701 0 : GNUNET_break (0);
1702 0 : resume_pay_with_error (pc,
1703 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1704 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_WIRE_FEE_ADDITION_FAILED,
1705 : "could not add exchange wire fee to total");
1706 0 : return false;
1707 : }
1708 : }
1709 : } /* deposit loop */
1710 :
1711 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1712 : "Amount received from wallet: %s\n",
1713 : TALER_amount2s (&acc_amount));
1714 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1715 : "Deposit fee for all coins: %s\n",
1716 : TALER_amount2s (&acc_fee));
1717 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1718 : "Total wire fee: %s\n",
1719 : TALER_amount2s (&total_wire_fee));
1720 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1721 : "Max wire fee: %s\n",
1722 : TALER_amount2s (&pc->max_wire_fee));
1723 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1724 : "Deposit fee limit for merchant: %s\n",
1725 : TALER_amount2s (&pc->max_fee));
1726 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1727 : "Total refunded amount: %s\n",
1728 : TALER_amount2s (&pc->total_refunded));
1729 :
1730 : /* Now compare exchange wire fee compared to
1731 : * what we are willing to pay */
1732 0 : if (GNUNET_YES !=
1733 0 : TALER_amount_cmp_currency (&total_wire_fee,
1734 0 : &pc->max_wire_fee))
1735 : {
1736 0 : GNUNET_break (0);
1737 0 : resume_pay_with_error (pc,
1738 : MHD_HTTP_CONFLICT,
1739 : TALER_EC_GENERIC_CURRENCY_MISMATCH,
1740 : total_wire_fee.currency);
1741 0 : return false;
1742 : }
1743 :
1744 0 : switch (TALER_amount_subtract (&wire_fee_delta,
1745 : &total_wire_fee,
1746 0 : &pc->max_wire_fee))
1747 : {
1748 0 : case TALER_AAR_RESULT_POSITIVE:
1749 : /* Actual wire fee is indeed higher than our maximum,
1750 : compute how much the customer is expected to cover! */
1751 0 : TALER_amount_divide (&wire_fee_customer_contribution,
1752 : &wire_fee_delta,
1753 : pc->wire_fee_amortization);
1754 0 : break;
1755 0 : case TALER_AAR_RESULT_ZERO:
1756 : case TALER_AAR_INVALID_NEGATIVE_RESULT:
1757 : /* Wire fee threshold is still above the wire fee amount.
1758 : Customer is not going to contribute on this. */
1759 0 : GNUNET_assert (GNUNET_OK ==
1760 : TALER_amount_set_zero (total_wire_fee.currency,
1761 : &wire_fee_customer_contribution));
1762 0 : break;
1763 0 : default:
1764 0 : GNUNET_assert (0);
1765 : }
1766 :
1767 : /* add wire fee contribution to the total fees */
1768 0 : if (0 >
1769 0 : TALER_amount_add (&acc_fee,
1770 : &acc_fee,
1771 : &wire_fee_customer_contribution))
1772 : {
1773 0 : GNUNET_break (0);
1774 0 : resume_pay_with_error (pc,
1775 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1776 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
1777 : "Overflow adding up amounts");
1778 0 : return false;
1779 : }
1780 0 : if (-1 == TALER_amount_cmp (&pc->max_fee,
1781 : &acc_fee))
1782 : {
1783 : /**
1784 : * Sum of fees of *all* the different exchanges of all the coins are
1785 : * higher than the fixed limit that the merchant is willing to pay. The
1786 : * difference must be paid by the customer.
1787 : */
1788 : struct TALER_Amount excess_fee;
1789 :
1790 : /* compute fee amount to be covered by customer */
1791 0 : GNUNET_assert (TALER_AAR_RESULT_POSITIVE ==
1792 : TALER_amount_subtract (&excess_fee,
1793 : &acc_fee,
1794 : &pc->max_fee));
1795 : /* add that to the total */
1796 0 : if (0 >
1797 0 : TALER_amount_add (&total_needed,
1798 : &excess_fee,
1799 0 : &pc->amount))
1800 : {
1801 0 : GNUNET_break (0);
1802 0 : resume_pay_with_error (pc,
1803 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1804 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AMOUNT_OVERFLOW,
1805 : "Overflow adding up amounts");
1806 0 : return false;
1807 : }
1808 : }
1809 : else
1810 : {
1811 : /* Fees are fully covered by the merchant, all we require
1812 : is that the total payment is not below the contract's amount */
1813 0 : total_needed = pc->amount;
1814 : }
1815 :
1816 : /* Do not count refunds towards the payment */
1817 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1818 : "Subtracting total refunds from paid amount: %s\n",
1819 : TALER_amount2s (&pc->total_refunded));
1820 0 : if (0 >
1821 0 : TALER_amount_subtract (&final_amount,
1822 : &acc_amount,
1823 0 : &pc->total_refunded))
1824 : {
1825 0 : GNUNET_break (0);
1826 0 : resume_pay_with_error (pc,
1827 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1828 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUNDS_EXCEED_PAYMENTS,
1829 : "refunded amount exceeds total payments");
1830 0 : return false;
1831 : }
1832 :
1833 0 : if (-1 == TALER_amount_cmp (&final_amount,
1834 : &total_needed))
1835 : {
1836 : /* acc_amount < total_needed */
1837 0 : if (-1 < TALER_amount_cmp (&acc_amount,
1838 : &total_needed))
1839 : {
1840 0 : GNUNET_break_op (0);
1841 0 : resume_pay_with_error (pc,
1842 : MHD_HTTP_PAYMENT_REQUIRED,
1843 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUNDED,
1844 : "contract not paid up due to refunds");
1845 : }
1846 0 : else if (-1 < TALER_amount_cmp (&acc_amount,
1847 0 : &pc->amount))
1848 : {
1849 0 : GNUNET_break_op (0);
1850 0 : resume_pay_with_error (pc,
1851 : MHD_HTTP_NOT_ACCEPTABLE,
1852 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_DUE_TO_FEES,
1853 : "contract not paid up due to fees (client may have calculated them badly)");
1854 : }
1855 : else
1856 : {
1857 0 : GNUNET_break_op (0);
1858 0 : resume_pay_with_error (pc,
1859 : MHD_HTTP_NOT_ACCEPTABLE,
1860 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_PAYMENT_INSUFFICIENT,
1861 : "payment insufficient");
1862 : }
1863 0 : return false;
1864 : }
1865 0 : return true;
1866 : }
1867 :
1868 :
1869 : /**
1870 : * Use database to notify other clients about the
1871 : * payment being completed.
1872 : *
1873 : * @param pc context to trigger notification for
1874 : */
1875 : static void
1876 0 : trigger_payment_notification (struct PayContext *pc)
1877 : {
1878 : {
1879 0 : struct TMH_OrderPayEventP pay_eh = {
1880 0 : .header.size = htons (sizeof (pay_eh)),
1881 0 : .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_PAID),
1882 0 : .merchant_pub = pc->hc->instance->merchant_pub
1883 : };
1884 :
1885 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1886 : "Notifying clients about payment of order %s\n",
1887 : pc->order_id);
1888 0 : GNUNET_CRYPTO_hash (pc->order_id,
1889 : strlen (pc->order_id),
1890 : &pay_eh.h_order_id);
1891 0 : TMH_db->event_notify (TMH_db->cls,
1892 : &pay_eh.header,
1893 : NULL,
1894 : 0);
1895 : }
1896 0 : if ( (NULL != pc->session_id) &&
1897 0 : (NULL != pc->fulfillment_url) )
1898 : {
1899 0 : struct TMH_SessionEventP session_eh = {
1900 0 : .header.size = htons (sizeof (session_eh)),
1901 0 : .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED),
1902 0 : .merchant_pub = pc->hc->instance->merchant_pub
1903 : };
1904 :
1905 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1906 : "Notifying clients about session change to %s for %s\n",
1907 : pc->session_id,
1908 : pc->fulfillment_url);
1909 0 : GNUNET_CRYPTO_hash (pc->session_id,
1910 0 : strlen (pc->session_id),
1911 : &session_eh.h_session_id);
1912 0 : GNUNET_CRYPTO_hash (pc->fulfillment_url,
1913 0 : strlen (pc->fulfillment_url),
1914 : &session_eh.h_fulfillment_url);
1915 0 : TMH_db->event_notify (TMH_db->cls,
1916 : &session_eh.header,
1917 : NULL,
1918 : 0);
1919 : }
1920 0 : }
1921 :
1922 :
1923 : /**
1924 : * Generate response (payment successful)
1925 : *
1926 : * @param[in,out] pc payment context where the payment was successful
1927 : */
1928 : static void
1929 0 : generate_success_response (struct PayContext *pc)
1930 : {
1931 : struct GNUNET_CRYPTO_EddsaSignature sig;
1932 :
1933 : /* Sign on our end (as the payment did go through, even if it may
1934 : have been refunded already) */
1935 0 : TALER_merchant_pay_sign (&pc->h_contract_terms,
1936 0 : &pc->hc->instance->merchant_priv,
1937 : &sig);
1938 : /* Build the response */
1939 0 : resume_pay_with_response (
1940 : pc,
1941 : MHD_HTTP_OK,
1942 0 : TALER_MHD_MAKE_JSON_PACK (
1943 : GNUNET_JSON_pack_data_auto ("sig",
1944 : &sig)));
1945 0 : }
1946 :
1947 :
1948 : static void
1949 0 : execute_pay_transaction (struct PayContext *pc)
1950 : {
1951 0 : struct TMH_HandlerContext *hc = pc->hc;
1952 0 : const char *instance_id = hc->instance->settings.id;
1953 :
1954 : /* Avoid re-trying transactions on soft errors forever! */
1955 0 : if (pc->retry_counter++ > MAX_RETRIES)
1956 : {
1957 0 : GNUNET_break (0);
1958 0 : resume_pay_with_error (pc,
1959 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1960 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
1961 : NULL);
1962 0 : return;
1963 : }
1964 0 : GNUNET_assert (GNUNET_YES == pc->suspended);
1965 :
1966 : /* Initialize some amount accumulators
1967 : (used in check_coin_paid(), check_coin_refunded()
1968 : and check_payment_sufficient()). */
1969 0 : GNUNET_break (GNUNET_OK ==
1970 : TALER_amount_set_zero (pc->amount.currency,
1971 : &pc->total_paid));
1972 0 : GNUNET_break (GNUNET_OK ==
1973 : TALER_amount_set_zero (pc->amount.currency,
1974 : &pc->total_fees_paid));
1975 0 : GNUNET_break (GNUNET_OK ==
1976 : TALER_amount_set_zero (pc->amount.currency,
1977 : &pc->total_refunded));
1978 0 : for (unsigned int i = 0; i<pc->coins_cnt; i++)
1979 0 : pc->dc[i].found_in_db = false;
1980 0 : pc->pending = pc->coins_cnt;
1981 :
1982 : /* First, try to see if we have all we need already done */
1983 0 : TMH_db->preflight (TMH_db->cls);
1984 0 : if (GNUNET_OK !=
1985 0 : TMH_db->start (TMH_db->cls,
1986 : "run pay"))
1987 : {
1988 0 : GNUNET_break (0);
1989 0 : resume_pay_with_error (pc,
1990 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1991 : TALER_EC_GENERIC_DB_START_FAILED,
1992 : NULL);
1993 0 : return;
1994 : }
1995 :
1996 : {
1997 : enum GNUNET_DB_QueryStatus qs;
1998 :
1999 : /* Check if some of these coins already succeeded for _this_ contract. */
2000 0 : qs = TMH_db->lookup_deposits (TMH_db->cls,
2001 : instance_id,
2002 0 : &pc->h_contract_terms,
2003 : &check_coin_paid,
2004 : pc);
2005 0 : if (0 > qs)
2006 : {
2007 0 : TMH_db->rollback (TMH_db->cls);
2008 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
2009 : {
2010 0 : execute_pay_transaction (pc);
2011 0 : return;
2012 : }
2013 : /* Always report on hard error as well to enable diagnostics */
2014 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
2015 0 : resume_pay_with_error (pc,
2016 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2017 : TALER_EC_GENERIC_DB_FETCH_FAILED,
2018 : "lookup deposits");
2019 0 : return;
2020 : }
2021 : }
2022 :
2023 :
2024 : {
2025 : enum GNUNET_DB_QueryStatus qs;
2026 :
2027 : /* Check if we refunded some of the coins */
2028 0 : qs = TMH_db->lookup_refunds (TMH_db->cls,
2029 : instance_id,
2030 0 : &pc->h_contract_terms,
2031 : &check_coin_refunded,
2032 : pc);
2033 0 : if (0 > qs)
2034 : {
2035 0 : TMH_db->rollback (TMH_db->cls);
2036 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
2037 : {
2038 0 : execute_pay_transaction (pc);
2039 0 : return;
2040 : }
2041 : /* Always report on hard error as well to enable diagnostics */
2042 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
2043 0 : resume_pay_with_error (pc,
2044 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2045 : TALER_EC_GENERIC_DB_FETCH_FAILED,
2046 : "lookup refunds");
2047 0 : return;
2048 : }
2049 : }
2050 :
2051 : /* Check if there are coins that still need to be processed */
2052 0 : if (0 != pc->pending)
2053 : {
2054 : /* we made no DB changes, so we can just rollback */
2055 0 : TMH_db->rollback (TMH_db->cls);
2056 :
2057 : /* Ok, we need to first go to the network to process more coins.
2058 : We that interaction in *tiny* transactions (hence the rollback
2059 : above). */
2060 0 : start_batch_deposits (pc);
2061 0 : return;
2062 : }
2063 :
2064 : /* 0 == pc->pending: all coins processed, let's see if that was enough */
2065 0 : if (! check_payment_sufficient (pc))
2066 : {
2067 : /* check_payment_sufficient() will have queued an error already.
2068 : We need to still abort the transaction. */
2069 0 : TMH_db->rollback (TMH_db->cls);
2070 0 : return;
2071 : }
2072 : /* Payment succeeded, save in database */
2073 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2074 : "Order `%s' (%s) was fully paid\n",
2075 : pc->order_id,
2076 : GNUNET_h2s (&pc->h_contract_terms.hash));
2077 : {
2078 : enum GNUNET_DB_QueryStatus qs;
2079 :
2080 0 : qs = TMH_db->mark_contract_paid (TMH_db->cls,
2081 : instance_id,
2082 0 : &pc->h_contract_terms,
2083 0 : pc->session_id);
2084 0 : if (qs < 0)
2085 : {
2086 0 : TMH_db->rollback (TMH_db->cls);
2087 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
2088 : {
2089 0 : execute_pay_transaction (pc);
2090 0 : return;
2091 : }
2092 0 : GNUNET_break (0);
2093 0 : resume_pay_with_error (pc,
2094 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2095 : TALER_EC_GENERIC_DB_STORE_FAILED,
2096 : "mark contract paid");
2097 0 : return;
2098 : }
2099 : }
2100 :
2101 0 : TMH_notify_order_change (hc->instance,
2102 : TMH_OSF_CLAIMED | TMH_OSF_PAID,
2103 : pc->timestamp,
2104 : pc->order_serial);
2105 :
2106 : {
2107 : enum GNUNET_DB_QueryStatus qs;
2108 :
2109 : /* Now commit! */
2110 0 : qs = TMH_db->commit (TMH_db->cls);
2111 0 : if (0 > qs)
2112 : {
2113 : /* commit failed */
2114 0 : TMH_db->rollback (TMH_db->cls);
2115 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
2116 : {
2117 0 : execute_pay_transaction (pc);
2118 0 : return;
2119 : }
2120 0 : GNUNET_break (0);
2121 0 : resume_pay_with_error (pc,
2122 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2123 : TALER_EC_GENERIC_DB_COMMIT_FAILED,
2124 : NULL);
2125 0 : return;
2126 : }
2127 0 : trigger_payment_notification (pc);
2128 : }
2129 0 : generate_success_response (pc);
2130 : }
2131 :
2132 :
2133 : /**
2134 : * Try to parse the pay request into the given pay context.
2135 : * Schedules an error response in the connection on failure.
2136 : *
2137 : * @param[in,out] pc context we use to handle the payment
2138 : * @return #GNUNET_OK on success,
2139 : * #GNUNET_NO on failure (response was queued with MHD)
2140 : * #GNUNET_SYSERR on hard error (MHD connection must be dropped)
2141 : */
2142 : static enum GNUNET_GenericReturnValue
2143 0 : parse_pay (struct PayContext *pc)
2144 : {
2145 0 : const char *session_id = NULL;
2146 : json_t *coins;
2147 : struct GNUNET_JSON_Specification spec[] = {
2148 0 : GNUNET_JSON_spec_json ("coins",
2149 : &coins),
2150 0 : GNUNET_JSON_spec_mark_optional (
2151 : GNUNET_JSON_spec_string ("session_id",
2152 : &session_id),
2153 : NULL),
2154 0 : GNUNET_JSON_spec_end ()
2155 : };
2156 :
2157 : {
2158 : enum GNUNET_GenericReturnValue res;
2159 :
2160 0 : res = TALER_MHD_parse_json_data (pc->connection,
2161 0 : pc->hc->request_body,
2162 : spec);
2163 0 : if (GNUNET_YES != res)
2164 : {
2165 0 : GNUNET_break_op (0);
2166 0 : return res;
2167 : }
2168 : }
2169 :
2170 : /* copy session ID (if set) */
2171 0 : if (NULL != session_id)
2172 : {
2173 0 : pc->session_id = GNUNET_strdup (session_id);
2174 : }
2175 : else
2176 : {
2177 : /* use empty string as default if client didn't specify it */
2178 0 : pc->session_id = GNUNET_strdup ("");
2179 : }
2180 :
2181 0 : if (! json_is_array (coins))
2182 : {
2183 0 : GNUNET_break_op (0);
2184 0 : GNUNET_JSON_parse_free (spec);
2185 : return (MHD_YES ==
2186 0 : TALER_MHD_reply_with_error (pc->connection,
2187 : MHD_HTTP_BAD_REQUEST,
2188 : TALER_EC_GENERIC_PARAMETER_MISSING,
2189 : "'coins' must be an array"))
2190 : ? GNUNET_NO
2191 0 : : GNUNET_SYSERR;
2192 : }
2193 :
2194 0 : pc->coins_cnt = json_array_size (coins);
2195 0 : if (pc->coins_cnt > MAX_COIN_ALLOWED_COINS)
2196 : {
2197 0 : GNUNET_break_op (0);
2198 0 : GNUNET_JSON_parse_free (spec);
2199 : return (MHD_YES ==
2200 0 : TALER_MHD_reply_with_error (
2201 : pc->connection,
2202 : MHD_HTTP_BAD_REQUEST,
2203 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
2204 : "'coins' array too long"))
2205 : ? GNUNET_NO
2206 0 : : GNUNET_SYSERR;
2207 : }
2208 :
2209 : /* note: 1 coin = 1 deposit confirmation expected */
2210 0 : pc->dc = GNUNET_new_array (pc->coins_cnt,
2211 : struct DepositConfirmation);
2212 :
2213 : /* This loop populates the array 'dc' in 'pc' */
2214 : {
2215 : unsigned int coins_index;
2216 : json_t *coin;
2217 :
2218 0 : json_array_foreach (coins, coins_index, coin)
2219 : {
2220 0 : struct DepositConfirmation *dc = &pc->dc[coins_index];
2221 : const char *exchange_url;
2222 : struct GNUNET_JSON_Specification ispec[] = {
2223 0 : GNUNET_JSON_spec_fixed_auto ("coin_sig",
2224 : &dc->cdd.coin_sig),
2225 0 : GNUNET_JSON_spec_fixed_auto ("coin_pub",
2226 : &dc->cdd.coin_pub),
2227 0 : TALER_JSON_spec_denom_sig ("ub_sig",
2228 : &dc->cdd.denom_sig),
2229 0 : GNUNET_JSON_spec_fixed_auto ("h_denom",
2230 : &dc->cdd.h_denom_pub),
2231 0 : TALER_JSON_spec_amount ("contribution",
2232 : TMH_currency,
2233 : &dc->cdd.amount),
2234 0 : GNUNET_JSON_spec_string ("exchange_url",
2235 : &exchange_url),
2236 : /* if a minimum age was required, the minimum_age_sig and
2237 : * age_commitment must be provided */
2238 0 : GNUNET_JSON_spec_mark_optional (
2239 0 : GNUNET_JSON_spec_fixed_auto ("minimum_age_sig",
2240 : &dc->minimum_age_sig),
2241 : &dc->no_minimum_age_sig),
2242 0 : GNUNET_JSON_spec_mark_optional (
2243 : TALER_JSON_spec_age_commitment ("age_commitment",
2244 : &dc->age_commitment),
2245 : &dc->no_age_commitment),
2246 : /* if minimum age was not required, but coin with age restriction set
2247 : * was used, h_age_commitment must be provided. */
2248 0 : GNUNET_JSON_spec_mark_optional (
2249 0 : GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
2250 : &dc->cdd.h_age_commitment),
2251 : &dc->no_h_age_commitment),
2252 0 : GNUNET_JSON_spec_end ()
2253 : };
2254 : enum GNUNET_GenericReturnValue res;
2255 0 : bool have_eg = false;
2256 :
2257 0 : res = TALER_MHD_parse_json_data (pc->connection,
2258 : coin,
2259 : ispec);
2260 0 : if (GNUNET_YES != res)
2261 : {
2262 0 : GNUNET_break_op (0);
2263 0 : GNUNET_JSON_parse_free (spec);
2264 0 : return res;
2265 : }
2266 :
2267 0 : for (unsigned int j = 0; j<coins_index; j++)
2268 : {
2269 0 : if (0 ==
2270 0 : GNUNET_memcmp (&dc->cdd.coin_pub,
2271 : &pc->dc[j].cdd.coin_pub))
2272 : {
2273 0 : GNUNET_break_op (0);
2274 : return (MHD_YES ==
2275 0 : TALER_MHD_reply_with_error (pc->connection,
2276 : MHD_HTTP_BAD_REQUEST,
2277 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
2278 : "duplicate coin in list"))
2279 : ? GNUNET_NO
2280 0 : : GNUNET_SYSERR;
2281 : }
2282 : }
2283 :
2284 0 : dc->exchange_url = GNUNET_strdup (exchange_url);
2285 0 : dc->index = coins_index;
2286 0 : dc->pc = pc;
2287 :
2288 0 : if (0 !=
2289 0 : strcasecmp (dc->cdd.amount.currency,
2290 : TMH_currency))
2291 : {
2292 0 : GNUNET_break_op (0);
2293 0 : GNUNET_JSON_parse_free (spec);
2294 : return (MHD_YES ==
2295 0 : TALER_MHD_reply_with_error (pc->connection,
2296 : MHD_HTTP_CONFLICT,
2297 : TALER_EC_GENERIC_CURRENCY_MISMATCH,
2298 : TMH_currency))
2299 : ? GNUNET_NO
2300 0 : : GNUNET_SYSERR;
2301 : }
2302 :
2303 : /* Check the consistency of the (potential) age restriction
2304 : * information. */
2305 0 : if (dc->no_age_commitment != dc->no_minimum_age_sig)
2306 : {
2307 0 : GNUNET_break_op (0);
2308 0 : GNUNET_JSON_parse_free (spec);
2309 : return (MHD_YES ==
2310 0 : TALER_MHD_reply_with_error (
2311 : pc->connection,
2312 : MHD_HTTP_BAD_REQUEST,
2313 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
2314 : "inconsistent: 'age_commitment' vs. 'minimum_age_sig'"
2315 : )
2316 : )
2317 : ? GNUNET_NO
2318 0 : : GNUNET_SYSERR;
2319 : }
2320 :
2321 : /* Setup exchange group */
2322 0 : for (unsigned int i = 0; i<pc->num_exchanges; i++)
2323 : {
2324 0 : if (0 ==
2325 0 : strcmp (pc->egs[i]->exchange_url,
2326 0 : GNUNET_strdup (exchange_url)))
2327 : {
2328 0 : have_eg = true;
2329 0 : break;
2330 : }
2331 : }
2332 0 : if (! have_eg)
2333 : {
2334 : struct ExchangeGroup *eg;
2335 :
2336 0 : eg = GNUNET_new (struct ExchangeGroup);
2337 0 : eg->pc = pc;
2338 0 : eg->exchange_url = dc->exchange_url;
2339 0 : GNUNET_array_append (pc->egs,
2340 : pc->num_exchanges,
2341 : eg);
2342 : }
2343 : }
2344 : }
2345 0 : GNUNET_JSON_parse_free (spec);
2346 0 : return GNUNET_OK;
2347 : }
2348 :
2349 :
2350 : /**
2351 : * Function called with information about a coin that was deposited.
2352 : * Checks if this coin is in our list of deposits as well.
2353 : *
2354 : * @param cls closure with our `struct PayContext *`
2355 : * @param deposit_serial which deposit operation is this about
2356 : * @param exchange_url URL of the exchange that issued the coin
2357 : * @param amount_with_fee amount the exchange will deposit for this coin
2358 : * @param deposit_fee fee the exchange will charge for this coin
2359 : * @param h_wire hash of merchant's wire details
2360 : * @param coin_pub public key of the coin
2361 : */
2362 : static void
2363 0 : deposit_paid_check (
2364 : void *cls,
2365 : uint64_t deposit_serial,
2366 : const char *exchange_url,
2367 : const struct TALER_MerchantWireHashP *h_wire,
2368 : const struct TALER_Amount *amount_with_fee,
2369 : const struct TALER_Amount *deposit_fee,
2370 : const struct TALER_CoinSpendPublicKeyP *coin_pub)
2371 : {
2372 0 : struct PayContext *pc = cls;
2373 :
2374 0 : for (unsigned int i = 0; i<pc->coins_cnt; i++)
2375 : {
2376 0 : struct DepositConfirmation *dci = &pc->dc[i];
2377 :
2378 0 : if ( (0 ==
2379 0 : GNUNET_memcmp (&dci->cdd.coin_pub,
2380 0 : coin_pub)) &&
2381 : (0 ==
2382 0 : strcmp (dci->exchange_url,
2383 0 : exchange_url)) &&
2384 : (0 ==
2385 0 : TALER_amount_cmp (&dci->cdd.amount,
2386 : amount_with_fee)) )
2387 : {
2388 0 : dci->matched_in_db = true;
2389 0 : break;
2390 : }
2391 : }
2392 0 : }
2393 :
2394 :
2395 : /**
2396 : * Handle case where contract was already paid. Either decides
2397 : * the payment is idempotent, or refunds the excess payment.
2398 : *
2399 : * @param[in,out] pc context we use to handle the payment
2400 : * @return #GNUNET_NO if response was queued with MHD
2401 : * #GNUNET_SYSERR on hard error (MHD connection must be dropped)
2402 : */
2403 : static enum GNUNET_GenericReturnValue
2404 0 : handle_contract_paid (struct PayContext *pc)
2405 : {
2406 : enum GNUNET_DB_QueryStatus qs;
2407 0 : bool unmatched = false;
2408 : json_t *refunds;
2409 :
2410 0 : qs = TMH_db->lookup_deposits_by_order (TMH_db->cls,
2411 : pc->order_serial,
2412 : &deposit_paid_check,
2413 : pc);
2414 0 : if (qs <= 0)
2415 : {
2416 0 : GNUNET_break (0);
2417 : return (MHD_YES ==
2418 0 : TALER_MHD_reply_with_error (pc->connection,
2419 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2420 : TALER_EC_GENERIC_DB_FETCH_FAILED,
2421 : "lookup_deposits_by_order"))
2422 : ? GNUNET_NO
2423 0 : : GNUNET_SYSERR;
2424 : }
2425 0 : for (unsigned int i = 0; i<pc->coins_cnt; i++)
2426 : {
2427 0 : struct DepositConfirmation *dci = &pc->dc[i];
2428 :
2429 0 : if (! dci->matched_in_db)
2430 0 : unmatched = true;
2431 : }
2432 0 : if (! unmatched)
2433 : {
2434 : /* Everything fine, idempotent request */
2435 : struct GNUNET_CRYPTO_EddsaSignature sig;
2436 :
2437 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2438 : "Idempotent pay request for order `%s', signing again\n",
2439 : pc->order_id);
2440 0 : TALER_merchant_pay_sign (&pc->h_contract_terms,
2441 0 : &pc->hc->instance->merchant_priv,
2442 : &sig);
2443 : return (MHD_YES ==
2444 0 : TALER_MHD_REPLY_JSON_PACK (
2445 : pc->connection,
2446 : MHD_HTTP_OK,
2447 : GNUNET_JSON_pack_data_auto ("sig",
2448 : &sig)))
2449 : ? GNUNET_NO
2450 0 : : GNUNET_SYSERR;
2451 : }
2452 : /* Conflict, double-payment detected! */
2453 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2454 : "Client attempted to pay extra for already paid order `%s'\n",
2455 : pc->order_id);
2456 0 : refunds = json_array ();
2457 0 : GNUNET_assert (NULL != refunds);
2458 0 : for (unsigned int i = 0; i<pc->coins_cnt; i++)
2459 : {
2460 0 : struct DepositConfirmation *dci = &pc->dc[i];
2461 : struct TALER_MerchantSignatureP merchant_sig;
2462 :
2463 0 : if (dci->matched_in_db)
2464 0 : continue;
2465 0 : TALER_merchant_refund_sign (&dci->cdd.coin_pub,
2466 0 : &pc->h_contract_terms,
2467 : 0, /* rtransaction id */
2468 0 : &dci->cdd.amount,
2469 0 : &pc->hc->instance->merchant_priv,
2470 : &merchant_sig);
2471 0 : GNUNET_assert (
2472 : 0 ==
2473 : json_array_append_new (
2474 : refunds,
2475 : GNUNET_JSON_PACK (
2476 : GNUNET_JSON_pack_data_auto (
2477 : "coin_pub",
2478 : &dci->cdd.coin_pub),
2479 : GNUNET_JSON_pack_data_auto (
2480 : "merchant_sig",
2481 : &merchant_sig),
2482 : TALER_JSON_pack_amount ("amount",
2483 : &dci->cdd.amount),
2484 : GNUNET_JSON_pack_uint64 ("rtransaction_id",
2485 : 0))));
2486 : }
2487 : return (MHD_YES ==
2488 0 : TALER_MHD_REPLY_JSON_PACK (
2489 : pc->connection,
2490 : MHD_HTTP_CONFLICT,
2491 : TALER_MHD_PACK_EC (
2492 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_ALREADY_PAID),
2493 : GNUNET_JSON_pack_array_steal ("refunds",
2494 : refunds)))
2495 : ? GNUNET_NO
2496 0 : : GNUNET_SYSERR;
2497 : }
2498 :
2499 :
2500 : /**
2501 : * Check the database state for the given order. * Schedules an error response in the connection on failure.
2502 : *
2503 : * @param pc context we use to handle the payment
2504 : * @return #GNUNET_OK on success,
2505 : * #GNUNET_NO on failure (response was queued with MHD)
2506 : * #GNUNET_SYSERR on hard error (MHD connection must be dropped)
2507 : */
2508 : static enum GNUNET_GenericReturnValue
2509 0 : check_contract (struct PayContext *pc)
2510 : {
2511 : /* obtain contract terms */
2512 : enum GNUNET_DB_QueryStatus qs;
2513 0 : json_t *contract_terms = NULL;
2514 0 : bool paid = false;
2515 :
2516 0 : qs = TMH_db->lookup_contract_terms (TMH_db->cls,
2517 0 : pc->hc->instance->settings.id,
2518 : pc->order_id,
2519 : &contract_terms,
2520 : &pc->order_serial,
2521 : &paid,
2522 : NULL);
2523 0 : if (0 > qs)
2524 : {
2525 : /* single, read-only SQL statements should never cause
2526 : serialization problems */
2527 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
2528 : /* Always report on hard error to enable diagnostics */
2529 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
2530 : return (MHD_YES ==
2531 0 : TALER_MHD_reply_with_error (pc->connection,
2532 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2533 : TALER_EC_GENERIC_DB_FETCH_FAILED,
2534 : "contract terms"))
2535 : ? GNUNET_NO
2536 0 : : GNUNET_SYSERR;
2537 : }
2538 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2539 : {
2540 : return (MHD_YES ==
2541 0 : TALER_MHD_reply_with_error (pc->connection,
2542 : MHD_HTTP_NOT_FOUND,
2543 : TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
2544 : pc->order_id))
2545 : ? GNUNET_NO
2546 0 : : GNUNET_SYSERR;
2547 : }
2548 : /* hash contract (needed later) */
2549 0 : if (GNUNET_OK !=
2550 0 : TALER_JSON_contract_hash (contract_terms,
2551 : &pc->h_contract_terms))
2552 : {
2553 0 : GNUNET_break (0);
2554 0 : json_decref (contract_terms);
2555 : return (MHD_YES ==
2556 0 : TALER_MHD_reply_with_error (pc->connection,
2557 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2558 : TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
2559 : NULL))
2560 : ? GNUNET_NO
2561 0 : : GNUNET_SYSERR;
2562 : }
2563 0 : if (paid)
2564 : {
2565 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2566 : "Order `%s' paid, checking for double-payment\n",
2567 : pc->order_id);
2568 0 : return handle_contract_paid (pc);
2569 : }
2570 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2571 : "Handling payment for order `%s' with contract hash `%s'\n",
2572 : pc->order_id,
2573 : GNUNET_h2s (&pc->h_contract_terms.hash));
2574 :
2575 : /* basic sanity check on the contract */
2576 0 : if (NULL == json_object_get (contract_terms,
2577 : "merchant"))
2578 : {
2579 : /* invalid contract */
2580 0 : GNUNET_break (0);
2581 0 : json_decref (contract_terms);
2582 : return (MHD_YES ==
2583 0 : TALER_MHD_reply_with_error (pc->connection,
2584 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2585 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_MERCHANT_FIELD_MISSING,
2586 : NULL))
2587 : ? GNUNET_NO
2588 0 : : GNUNET_SYSERR;
2589 : }
2590 :
2591 : /* Get details from contract and check fundamentals */
2592 : {
2593 0 : const char *fulfillment_url = NULL;
2594 : struct GNUNET_JSON_Specification espec[] = {
2595 0 : TALER_JSON_spec_amount ("amount",
2596 : TMH_currency,
2597 : &pc->amount),
2598 0 : GNUNET_JSON_spec_mark_optional (
2599 : GNUNET_JSON_spec_string ("fulfillment_url",
2600 : &fulfillment_url),
2601 : NULL),
2602 0 : TALER_JSON_spec_amount ("max_fee",
2603 : TMH_currency,
2604 : &pc->max_fee),
2605 0 : TALER_JSON_spec_amount ("max_wire_fee",
2606 : TMH_currency,
2607 : &pc->max_wire_fee),
2608 0 : GNUNET_JSON_spec_uint32 ("wire_fee_amortization",
2609 : &pc->wire_fee_amortization),
2610 0 : GNUNET_JSON_spec_timestamp ("timestamp",
2611 : &pc->timestamp),
2612 0 : GNUNET_JSON_spec_timestamp ("refund_deadline",
2613 : &pc->refund_deadline),
2614 0 : GNUNET_JSON_spec_timestamp ("pay_deadline",
2615 : &pc->pay_deadline),
2616 0 : GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
2617 : &pc->wire_transfer_deadline),
2618 0 : GNUNET_JSON_spec_fixed_auto ("h_wire",
2619 : &pc->h_wire),
2620 0 : GNUNET_JSON_spec_mark_optional (
2621 : GNUNET_JSON_spec_uint32 ("minimum_age",
2622 0 : &pc->minimum_age),
2623 : NULL),
2624 0 : GNUNET_JSON_spec_end ()
2625 : };
2626 : enum GNUNET_GenericReturnValue res;
2627 :
2628 0 : pc->minimum_age = 0;
2629 0 : res = TALER_MHD_parse_internal_json_data (pc->connection,
2630 : contract_terms,
2631 : espec);
2632 0 : if (NULL != fulfillment_url)
2633 0 : pc->fulfillment_url = GNUNET_strdup (fulfillment_url);
2634 0 : json_decref (contract_terms);
2635 0 : if (GNUNET_YES != res)
2636 : {
2637 0 : GNUNET_break (0);
2638 0 : return res;
2639 : }
2640 : }
2641 :
2642 0 : if (GNUNET_TIME_timestamp_cmp (pc->wire_transfer_deadline,
2643 : <,
2644 : pc->refund_deadline))
2645 : {
2646 : /* This should already have been checked when creating the order! */
2647 0 : GNUNET_break (0);
2648 0 : return TALER_MHD_reply_with_error (pc->connection,
2649 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2650 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_REFUND_DEADLINE_PAST_WIRE_TRANSFER_DEADLINE,
2651 : NULL);
2652 : }
2653 0 : if (GNUNET_TIME_absolute_is_past (pc->pay_deadline.abs_time))
2654 : {
2655 : /* too late */
2656 : return (MHD_YES ==
2657 0 : TALER_MHD_reply_with_error (pc->connection,
2658 : MHD_HTTP_GONE,
2659 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_OFFER_EXPIRED,
2660 : NULL))
2661 : ? GNUNET_NO
2662 0 : : GNUNET_SYSERR;
2663 : }
2664 :
2665 : /* Make sure wire method (still) exists for this instance */
2666 : {
2667 : struct TMH_WireMethod *wm;
2668 :
2669 0 : wm = pc->hc->instance->wm_head;
2670 0 : while (0 != GNUNET_memcmp (&pc->h_wire,
2671 : &wm->h_wire))
2672 0 : wm = wm->next;
2673 0 : if (NULL == wm)
2674 : {
2675 0 : GNUNET_break (0);
2676 0 : return TALER_MHD_reply_with_error (pc->connection,
2677 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2678 : TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_HASH_UNKNOWN,
2679 : NULL);
2680 : }
2681 0 : pc->wm = wm;
2682 : }
2683 :
2684 0 : return GNUNET_OK;
2685 : }
2686 :
2687 :
2688 : /**
2689 : * Handle a timeout for the processing of the pay request.
2690 : *
2691 : * @param cls our `struct PayContext`
2692 : */
2693 : static void
2694 0 : handle_pay_timeout (void *cls)
2695 : {
2696 0 : struct PayContext *pc = cls;
2697 :
2698 0 : pc->timeout_task = NULL;
2699 0 : GNUNET_assert (GNUNET_YES == pc->suspended);
2700 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2701 : "Resuming pay with error after timeout\n");
2702 0 : resume_pay_with_error (pc,
2703 : MHD_HTTP_GATEWAY_TIMEOUT,
2704 : TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT,
2705 : NULL);
2706 0 : }
2707 :
2708 :
2709 : MHD_RESULT
2710 0 : TMH_post_orders_ID_pay (const struct TMH_RequestHandler *rh,
2711 : struct MHD_Connection *connection,
2712 : struct TMH_HandlerContext *hc)
2713 : {
2714 0 : struct PayContext *pc = hc->ctx;
2715 : enum GNUNET_GenericReturnValue ret;
2716 :
2717 0 : GNUNET_assert (NULL != hc->infix);
2718 0 : if (NULL == pc)
2719 : {
2720 0 : pc = GNUNET_new (struct PayContext);
2721 0 : GNUNET_CONTAINER_DLL_insert (pc_head,
2722 : pc_tail,
2723 : pc);
2724 0 : pc->connection = connection;
2725 0 : pc->hc = hc;
2726 0 : pc->order_id = hc->infix;
2727 0 : hc->ctx = pc;
2728 0 : hc->cc = &pay_context_cleanup;
2729 0 : ret = parse_pay (pc);
2730 0 : if (GNUNET_OK != ret)
2731 : return (GNUNET_NO == ret)
2732 : ? MHD_YES
2733 0 : : MHD_NO;
2734 : }
2735 0 : if (GNUNET_SYSERR == pc->suspended)
2736 0 : return MHD_NO; /* during shutdown, we don't generate any more replies */
2737 0 : GNUNET_assert (GNUNET_NO == pc->suspended);
2738 0 : if (0 != pc->response_code)
2739 : {
2740 : /* We are *done* processing the request, just queue the response (!) */
2741 0 : if (UINT_MAX == pc->response_code)
2742 : {
2743 0 : GNUNET_break (0);
2744 0 : return MHD_NO; /* hard error */
2745 : }
2746 0 : return MHD_queue_response (connection,
2747 : pc->response_code,
2748 : pc->response);
2749 : }
2750 0 : ret = check_contract (pc);
2751 0 : if (GNUNET_OK != ret)
2752 : return (GNUNET_NO == ret)
2753 : ? MHD_YES
2754 0 : : MHD_NO;
2755 :
2756 : /* Payment not finished, suspend while we interact with the exchange */
2757 0 : MHD_suspend_connection (connection);
2758 0 : pc->suspended = GNUNET_YES;
2759 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2760 : "Suspending pay handling while working with the exchange\n");
2761 0 : GNUNET_assert (NULL == pc->timeout_task);
2762 : pc->timeout_task
2763 0 : = GNUNET_SCHEDULER_add_delayed (get_pay_timeout (pc->coins_cnt),
2764 : &handle_pay_timeout,
2765 : pc);
2766 0 : GNUNET_assert (NULL != pc->wm);
2767 0 : execute_pay_transaction (pc);
2768 0 : return MHD_YES;
2769 : }
2770 :
2771 :
2772 : /* end of taler-merchant-httpd_post-orders-ID-pay.c */
|