Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2017-2024, 2026 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file src/backend/taler-merchant-httpd_get-private-orders-ORDER_ID.c
18 : * @brief implementation of GET /private/orders/ID handler
19 : * @author Florian Dold
20 : * @author Christian Grothoff
21 : * @author Bohdan Potuzhnyi
22 : * @author Iván Ávalos
23 : */
24 : #include "platform.h"
25 : #include <taler/taler_json_lib.h>
26 : #include <taler/taler_dbevents.h>
27 : #include <taler/taler_error_codes.h>
28 : #include <taler/taler_util.h>
29 : #include <gnunet/gnunet_common.h>
30 : #include <gnunet/gnunet_json_lib.h>
31 : #include "taler/taler_merchant_util.h"
32 : #include "taler-merchant-httpd_helper.h"
33 : #include "taler-merchant-httpd_get-private-orders.h"
34 : #include "taler-merchant-httpd_get-private-orders-ORDER_ID.h"
35 : #include "merchant-database/get_contract_terms_status.h"
36 : #include "merchant-database/iterate_deposits_by_order.h"
37 : #include "merchant-database/get_order.h"
38 : #include "merchant-database/get_order_by_fulfillment.h"
39 : #include "merchant-database/iterate_refunds_detailed.h"
40 : #include "merchant-database/iterate_external_refunds.h"
41 : #include "merchant-database/iterate_transfer_details_by_order.h"
42 : #include "merchant-database/update_to_contract_terms_wired.h"
43 : #include "merchant-database/preflight.h"
44 : #include "merchant-database/event_listen.h"
45 :
46 : /**
47 : * Data structure we keep for a check payment request.
48 : */
49 : struct GetOrderRequestContext;
50 :
51 :
52 : /**
53 : * Request to an exchange for details about wire transfers
54 : * in response to a coin's deposit operation.
55 : */
56 : struct TransferQuery
57 : {
58 :
59 : /**
60 : * Kept in a DLL.
61 : */
62 : struct TransferQuery *next;
63 :
64 : /**
65 : * Kept in a DLL.
66 : */
67 : struct TransferQuery *prev;
68 :
69 : /**
70 : * Base URL of the exchange.
71 : */
72 : char *exchange_url;
73 :
74 : /**
75 : * Overall request this TQ belongs with.
76 : */
77 : struct GetOrderRequestContext *gorc;
78 :
79 : /**
80 : * Hash of the merchant's bank account the transfer (presumably) went to.
81 : */
82 : struct TALER_MerchantWireHashP h_wire;
83 :
84 : /**
85 : * Value deposited (including deposit fee).
86 : */
87 : struct TALER_Amount amount_with_fee;
88 :
89 : /**
90 : * Deposit fee paid for this coin.
91 : */
92 : struct TALER_Amount deposit_fee;
93 :
94 : /**
95 : * Public key of the coin this is about.
96 : */
97 : struct TALER_CoinSpendPublicKeyP coin_pub;
98 :
99 : /**
100 : * Which deposit operation is this about?
101 : */
102 : uint64_t deposit_serial;
103 :
104 : };
105 :
106 :
107 : /**
108 : * Phases of order processing.
109 : */
110 : enum GetOrderPhase
111 : {
112 : /**
113 : * Initialization.
114 : */
115 : GOP_INIT = 0,
116 :
117 : /**
118 : * Obtain contract terms from database.
119 : */
120 : GOP_FETCH_CONTRACT = 1,
121 :
122 : /**
123 : * Parse the contract terms.
124 : */
125 : GOP_PARSE_CONTRACT = 2,
126 :
127 : /**
128 : * Check if the contract was fully paid.
129 : */
130 : GOP_CHECK_PAID = 3,
131 :
132 : /**
133 : * Check if the wallet may have purchased an equivalent
134 : * order before and we need to redirect the wallet to
135 : * an existing paid order.
136 : */
137 : GOP_CHECK_REPURCHASE = 4,
138 :
139 : /**
140 : * Terminate processing of unpaid orders, either by
141 : * suspending until payment or by returning the
142 : * unpaid order status.
143 : */
144 : GOP_UNPAID_FINISH = 5,
145 :
146 : /**
147 : * Load all deposits associated with the order.
148 : */
149 : GOP_CHECK_DEPOSITS = 6,
150 :
151 : /**
152 : * Check if the (paid) order was refunded.
153 : */
154 : GOP_CHECK_REFUNDS = 7,
155 :
156 : /**
157 : * Check local records for transfers of funds to
158 : * the merchant.
159 : */
160 : GOP_CHECK_LOCAL_TRANSFERS = 8,
161 :
162 : /**
163 : * Generate final comprehensive result.
164 : */
165 : GOP_REPLY_RESULT = 9,
166 :
167 : /**
168 : * End with the HTTP status and error code in
169 : * wire_hc and wire_ec.
170 : */
171 : GOP_ERROR = 10,
172 :
173 : /**
174 : * We are suspended awaiting payment.
175 : */
176 : GOP_SUSPENDED_ON_UNPAID = 11,
177 :
178 : /**
179 : * Processing is done, return #MHD_YES.
180 : */
181 : GOP_END_YES = 12,
182 :
183 : /**
184 : * Processing is done, return #MHD_NO.
185 : */
186 : GOP_END_NO = 13
187 :
188 : };
189 :
190 :
191 : /**
192 : * Data structure we keep for a check payment request.
193 : */
194 : struct GetOrderRequestContext
195 : {
196 :
197 : /**
198 : * Processing phase we are in.
199 : */
200 : enum GetOrderPhase phase;
201 :
202 : /**
203 : * Entry in the #resume_timeout_heap for this check payment, if we are
204 : * suspended.
205 : */
206 : struct TMH_SuspendedConnection sc;
207 :
208 : /**
209 : * Which merchant instance is this for?
210 : */
211 : struct TMH_HandlerContext *hc;
212 :
213 : /**
214 : * session of the client
215 : */
216 : const char *session_id;
217 :
218 : /**
219 : * Kept in a DLL while suspended on exchange.
220 : */
221 : struct GetOrderRequestContext *next;
222 :
223 : /**
224 : * Kept in a DLL while suspended on exchange.
225 : */
226 : struct GetOrderRequestContext *prev;
227 :
228 : /**
229 : * Head of DLL of individual queries for transfer data.
230 : */
231 : struct TransferQuery *tq_head;
232 :
233 : /**
234 : * Tail of DLL of individual queries for transfer data.
235 : */
236 : struct TransferQuery *tq_tail;
237 :
238 : /**
239 : * Timeout task while waiting on exchange.
240 : */
241 : struct GNUNET_SCHEDULER_Task *tt;
242 :
243 : /**
244 : * Database event we are waiting on to be resuming
245 : * for payment or refunds.
246 : */
247 : struct GNUNET_DB_EventHandler *eh;
248 :
249 : /**
250 : * Database event we are waiting on to be resuming
251 : * for session capture.
252 : */
253 : struct GNUNET_DB_EventHandler *session_eh;
254 :
255 : /**
256 : * Contract terms of the payment we are checking. NULL when they
257 : * are not (yet) known.
258 : */
259 : json_t *contract_terms_json;
260 :
261 : /**
262 : * Parsed contract terms, NULL when parsing failed
263 : */
264 : struct TALER_MERCHANT_Contract *contract_terms;
265 :
266 : /**
267 : * Proto-contract. Be careful: do NOT free this
268 : * if @e contract_terms is not NULL!
269 : */
270 : struct TALER_MERCHANT_ProtoContract *pc;
271 :
272 : /**
273 : * Order terms of the payment we are checking. NULL when we have
274 : * a contract.
275 : */
276 : json_t *order_json;
277 :
278 : /**
279 : * Parsed order details, NULL when parsing failed or we have a contract.
280 : */
281 : struct TALER_MERCHANT_Order *order;
282 :
283 : /**
284 : * Common terms of @e order and @e contract.
285 : */
286 : const struct TALER_MERCHANT_ContractBaseTerms *ct;
287 :
288 : /**
289 : * Timestamp of the contract or order.
290 : */
291 : struct GNUNET_TIME_Timestamp timestamp;
292 :
293 : /**
294 : * Claim token of the order.
295 : */
296 : struct TALER_ClaimTokenP claim_token;
297 :
298 : /**
299 : * Timestamp of the last payment.
300 : */
301 : struct GNUNET_TIME_Timestamp last_payment;
302 :
303 : /**
304 : * Wire details for the payment, to be returned in the reply. NULL
305 : * if not available.
306 : */
307 : json_t *wire_details;
308 :
309 : /**
310 : * Details about refunds, NULL if there are no refunds.
311 : */
312 : json_t *refund_details;
313 :
314 : /**
315 : * Details about external refunds, empty array if there are none.
316 : */
317 : json_t *refunds_external;
318 :
319 : /**
320 : * Amount of the order, unset for unpaid v1 orders.
321 : */
322 : struct TALER_Amount contract_amount;
323 :
324 : /**
325 : * Hash over the @e contract_terms.
326 : */
327 : struct TALER_PrivateContractHashP h_contract_terms;
328 :
329 : /**
330 : * Set to the Etag of a response already known to the
331 : * client. We should only return from long-polling
332 : * on timeout (with "Not Modified") or when the Etag
333 : * of the response differs from what is given here.
334 : * Only set if @a have_lp_not_etag is true.
335 : * Set from "lp_etag" query parameter.
336 : */
337 : struct GNUNET_ShortHashCode lp_not_etag;
338 :
339 : /**
340 : * Total amount the exchange deposited into our bank account
341 : * (confirmed or unconfirmed), excluding fees.
342 : */
343 : struct TALER_Amount deposits_total;
344 :
345 : /**
346 : * Total amount in deposit fees we paid for all coins.
347 : */
348 : struct TALER_Amount deposit_fees_total;
349 :
350 : /**
351 : * Total amount in deposit fees cancelled due to refunds for all coins.
352 : */
353 : struct TALER_Amount deposit_fees_refunded_total;
354 :
355 : /**
356 : * Total value of the coins that the exchange deposited into our bank
357 : * account (confirmed or unconfirmed), including deposit fees.
358 : */
359 : struct TALER_Amount value_total;
360 :
361 : /**
362 : * Serial ID of the order.
363 : */
364 : uint64_t order_serial;
365 :
366 : /**
367 : * Index of selected choice from ``choices`` array in the contract_terms.
368 : * Is -1 for orders without choices.
369 : */
370 : int16_t choice_index;
371 :
372 : /**
373 : * Total refunds granted for this payment. Only initialized
374 : * if @e refunded is set to true.
375 : */
376 : struct TALER_Amount refund_amount;
377 :
378 : /**
379 : * Exchange HTTP error code encountered while trying to determine wire transfer
380 : * details. #TALER_EC_NONE for no error encountered.
381 : */
382 : unsigned int exchange_hc;
383 :
384 : /**
385 : * Exchange error code encountered while trying to determine wire transfer
386 : * details. #TALER_EC_NONE for no error encountered.
387 : */
388 : enum TALER_ErrorCode exchange_ec;
389 :
390 : /**
391 : * Error code encountered while trying to determine wire transfer
392 : * details. #TALER_EC_NONE for no error encountered.
393 : */
394 : enum TALER_ErrorCode wire_ec;
395 :
396 : /**
397 : * Set to YES if refunded orders should be included when
398 : * doing repurchase detection.
399 : */
400 : enum TALER_EXCHANGE_YesNoAll allow_refunded_for_repurchase;
401 :
402 : /**
403 : * HTTP status to return with @e wire_ec, 0 if @e wire_ec is #TALER_EC_NONE.
404 : */
405 : unsigned int wire_hc;
406 :
407 : /**
408 : * Did we suspend @a connection and are thus in
409 : * the #gorc_head DLL (#GNUNET_YES). Set to
410 : * #GNUNET_NO if we are not suspended, and to
411 : * #GNUNET_SYSERR if we should close the connection
412 : * without a response due to shutdown.
413 : */
414 : enum GNUNET_GenericReturnValue suspended;
415 :
416 : /**
417 : * Set to true if this payment has been refunded and
418 : * @e refund_amount is initialized.
419 : */
420 : bool refunded;
421 :
422 : /**
423 : * True if @e lp_not_etag was given.
424 : */
425 : bool have_lp_not_etag;
426 :
427 : /**
428 : * True if the order was paid.
429 : */
430 : bool paid;
431 :
432 : /**
433 : * True if the paid session in the database matches
434 : * our @e session_id.
435 : */
436 : bool paid_session_matches;
437 :
438 : /**
439 : * When we install the @e session_eh listener, we already checked
440 : * the order status. We could not do it earlier, because the
441 : * fulfillment URL was unavailable. However, this leaves a chance
442 : * for a payment event to be missed if it happens before the first
443 : * order status check and us installing the event listener. Thus,
444 : * if we afterwards suspend, we should once *immediately* check the
445 : * order status a 2nd time before we really suspend. This flag is
446 : * set to true to handle this case.
447 : */
448 : bool instant_retry;
449 :
450 : /**
451 : * True if the exchange wired the money to the merchant.
452 : */
453 : bool wired;
454 :
455 : /**
456 : * True if the order remains unclaimed.
457 : */
458 : bool order_only;
459 :
460 : /**
461 : * Set to true if this payment has been refunded and
462 : * some refunds remain to be picked up by the wallet.
463 : */
464 : bool refund_pending;
465 :
466 : /**
467 : * Set to true if our database (incorrectly) has refunds
468 : * in a different currency than the currency of the
469 : * original payment for the order.
470 : */
471 : bool refund_currency_mismatch;
472 :
473 : /**
474 : * Set to true if our database (incorrectly) has deposits
475 : * in a different currency than the currency of the
476 : * original payment for the order.
477 : */
478 : bool deposit_currency_mismatch;
479 : };
480 :
481 :
482 : /**
483 : * Head of list of suspended requests waiting on the exchange.
484 : */
485 : static struct GetOrderRequestContext *gorc_head;
486 :
487 : /**
488 : * Tail of list of suspended requests waiting on the exchange.
489 : */
490 : static struct GetOrderRequestContext *gorc_tail;
491 :
492 :
493 : void
494 20 : TMH_force_gorc_resume (void)
495 : {
496 : struct GetOrderRequestContext *gorc;
497 :
498 20 : while (NULL != (gorc = gorc_head))
499 : {
500 0 : GNUNET_CONTAINER_DLL_remove (gorc_head,
501 : gorc_tail,
502 : gorc);
503 0 : GNUNET_assert (GNUNET_YES == gorc->suspended);
504 0 : gorc->suspended = GNUNET_SYSERR;
505 0 : MHD_resume_connection (gorc->sc.con);
506 : }
507 20 : }
508 :
509 :
510 : /**
511 : * We have received a trigger from the database
512 : * that we should (possibly) resume the request.
513 : *
514 : * @param cls a `struct GetOrderRequestContext` to resume
515 : * @param extra string encoding refund amount (or NULL)
516 : * @param extra_size number of bytes in @a extra
517 : */
518 : static void
519 3 : resume_by_event (void *cls,
520 : const void *extra,
521 : size_t extra_size)
522 : {
523 3 : struct GetOrderRequestContext *gorc = cls;
524 :
525 : (void) extra;
526 : (void) extra_size;
527 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
528 : "Resuming request for order %s by trigger\n",
529 : gorc->hc->infix);
530 3 : if (GNUNET_NO == gorc->suspended)
531 0 : return; /* duplicate event is possible */
532 3 : gorc->suspended = GNUNET_NO;
533 3 : gorc->phase = GOP_FETCH_CONTRACT;
534 3 : GNUNET_CONTAINER_DLL_remove (gorc_head,
535 : gorc_tail,
536 : gorc);
537 3 : MHD_resume_connection (gorc->sc.con);
538 3 : TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
539 : }
540 :
541 :
542 : /**
543 : * Clean up the session state for a GET /private/order/ID request.
544 : *
545 : * @param cls closure, must be a `struct GetOrderRequestContext *`
546 : */
547 : static void
548 75 : gorc_cleanup (void *cls)
549 : {
550 75 : struct GetOrderRequestContext *gorc = cls;
551 : struct TransferQuery *tq;
552 :
553 115 : while (NULL != (tq = gorc->tq_head))
554 : {
555 40 : GNUNET_CONTAINER_DLL_remove (gorc->tq_head,
556 : gorc->tq_tail,
557 : tq);
558 40 : GNUNET_free (tq->exchange_url);
559 40 : GNUNET_free (tq);
560 : }
561 :
562 75 : if (NULL != gorc->contract_terms_json)
563 : {
564 75 : json_decref (gorc->contract_terms_json);
565 75 : gorc->contract_terms_json = NULL;
566 : }
567 75 : if (NULL != gorc->order_json)
568 : {
569 0 : json_decref (gorc->order_json);
570 0 : gorc->order_json = NULL;
571 : }
572 75 : if (NULL != gorc->contract_terms)
573 : {
574 59 : TALER_MERCHANT_contract_free (gorc->contract_terms);
575 59 : gorc->contract_terms = NULL;
576 59 : gorc->pc = NULL; /* was an alias! */
577 : }
578 75 : if (NULL != gorc->pc)
579 : {
580 16 : TALER_MERCHANT_proto_contract_free (gorc->pc);
581 16 : gorc->pc = NULL;
582 : }
583 75 : if (NULL != gorc->order)
584 : {
585 0 : TALER_MERCHANT_order_free (gorc->order);
586 0 : gorc->order = NULL;
587 : }
588 75 : gorc->ct = NULL; /* avoid dangling pointer */
589 75 : if (NULL != gorc->wire_details)
590 75 : json_decref (gorc->wire_details);
591 75 : if (NULL != gorc->refund_details)
592 75 : json_decref (gorc->refund_details);
593 75 : if (NULL != gorc->refunds_external)
594 75 : json_decref (gorc->refunds_external);
595 75 : if (NULL != gorc->tt)
596 : {
597 0 : GNUNET_SCHEDULER_cancel (gorc->tt);
598 0 : gorc->tt = NULL;
599 : }
600 75 : if (NULL != gorc->eh)
601 : {
602 5 : TALER_MERCHANTDB_event_listen_cancel (gorc->eh);
603 5 : gorc->eh = NULL;
604 : }
605 75 : if (NULL != gorc->session_eh)
606 : {
607 14 : TALER_MERCHANTDB_event_listen_cancel (gorc->session_eh);
608 14 : gorc->session_eh = NULL;
609 : }
610 75 : GNUNET_free (gorc);
611 75 : }
612 :
613 :
614 : /**
615 : * Processing the request @a gorc is finished, set the
616 : * final return value in phase based on @a mret.
617 : *
618 : * @param[in,out] gorc order context to initialize
619 : * @param mret MHD HTTP response status to return
620 : */
621 : static void
622 75 : phase_end (struct GetOrderRequestContext *gorc,
623 : enum MHD_Result mret)
624 : {
625 75 : gorc->phase = (MHD_YES == mret)
626 : ? GOP_END_YES
627 75 : : GOP_END_NO;
628 75 : }
629 :
630 :
631 : /**
632 : * Initialize event callbacks for the order processing.
633 : *
634 : * @param[in,out] gorc order context to initialize
635 : */
636 : static void
637 75 : phase_init (struct GetOrderRequestContext *gorc)
638 : {
639 75 : struct TMH_HandlerContext *hc = gorc->hc;
640 75 : struct TMH_OrderPayEventP pay_eh = {
641 75 : .header.size = htons (sizeof (pay_eh)),
642 75 : .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_STATUS_CHANGED),
643 75 : .merchant_pub = hc->instance->merchant_pub
644 : };
645 :
646 75 : if (! GNUNET_TIME_absolute_is_future (gorc->sc.long_poll_timeout))
647 : {
648 70 : gorc->phase++;
649 70 : return;
650 : }
651 :
652 5 : GNUNET_CRYPTO_hash (hc->infix,
653 5 : strlen (hc->infix),
654 : &pay_eh.h_order_id);
655 5 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
656 : "Subscribing to payment triggers for %p\n",
657 : gorc);
658 5 : gorc->eh = TALER_MERCHANTDB_event_listen (
659 : TMH_db,
660 : &pay_eh.header,
661 : GNUNET_TIME_absolute_get_remaining (gorc->sc.long_poll_timeout),
662 : &resume_by_event,
663 : gorc);
664 5 : gorc->phase++;
665 : }
666 :
667 :
668 : /**
669 : * Obtain latest contract terms from the database.
670 : *
671 : * @param[in,out] gorc order context to update
672 : */
673 : static void
674 78 : phase_fetch_contract (struct GetOrderRequestContext *gorc)
675 : {
676 78 : struct TMH_HandlerContext *hc = gorc->hc;
677 : enum GNUNET_DB_QueryStatus qs;
678 :
679 78 : if (NULL != gorc->contract_terms_json)
680 : {
681 : /* Free memory filled with old contract terms before fetching the latest
682 : ones from the DB. Note that we cannot simply skip the database
683 : interaction as the contract terms loaded previously might be from an
684 : earlier *unclaimed* order state (which we loaded in a previous
685 : invocation of this function and we are back here due to long polling)
686 : and thus the contract terms could have changed during claiming. Thus,
687 : we need to fetch the latest contract terms from the DB again. */
688 3 : json_decref (gorc->contract_terms_json);
689 3 : gorc->contract_terms_json = NULL;
690 3 : gorc->order_only = false;
691 : }
692 78 : TALER_MERCHANTDB_preflight (TMH_db);
693 78 : qs = TALER_MERCHANTDB_get_contract_terms_status (TMH_db,
694 78 : hc->instance->settings.id,
695 78 : hc->infix,
696 : gorc->session_id,
697 : &gorc->contract_terms_json,
698 : &gorc->order_serial,
699 : &gorc->paid,
700 : &gorc->wired,
701 : &gorc->paid_session_matches,
702 : &gorc->claim_token,
703 : &gorc->choice_index);
704 78 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
705 : "get_contract_terms (%s) returned %d\n",
706 : hc->infix,
707 : (int) qs);
708 78 : if (0 > qs)
709 : {
710 : /* single, read-only SQL statements should never cause
711 : serialization problems */
712 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
713 : /* Always report on hard error as well to enable diagnostics */
714 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
715 0 : phase_end (gorc,
716 : TALER_MHD_reply_with_error (gorc->sc.con,
717 : MHD_HTTP_INTERNAL_SERVER_ERROR,
718 : TALER_EC_GENERIC_DB_FETCH_FAILED,
719 : "contract terms"));
720 0 : return;
721 : }
722 78 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
723 : {
724 61 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
725 : "Order %s is %s (%s) according to database, choice %d\n",
726 : hc->infix,
727 : gorc->paid ? "paid" : "unpaid",
728 : gorc->wired ? "wired" : "unwired",
729 : (int) gorc->choice_index);
730 61 : gorc->phase++;
731 61 : return;
732 : }
733 17 : GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
734 17 : GNUNET_assert (! gorc->paid);
735 : /* No contract, only order, fetch from orders table */
736 17 : gorc->order_only = true;
737 : {
738 : struct TALER_MerchantPostDataHashP unused;
739 :
740 : /* We need the order for two cases: Either when the contract doesn't exist yet,
741 : * or when the order is claimed but unpaid, and we need the claim token. */
742 17 : qs = TALER_MERCHANTDB_get_order (TMH_db,
743 17 : hc->instance->settings.id,
744 17 : hc->infix,
745 : &gorc->claim_token,
746 : &unused,
747 : &gorc->contract_terms_json);
748 : }
749 17 : if (0 > qs)
750 : {
751 : /* single, read-only SQL statements should never cause
752 : serialization problems */
753 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
754 : /* Always report on hard error as well to enable diagnostics */
755 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
756 0 : phase_end (gorc,
757 : TALER_MHD_reply_with_error (gorc->sc.con,
758 : MHD_HTTP_INTERNAL_SERVER_ERROR,
759 : TALER_EC_GENERIC_DB_FETCH_FAILED,
760 : "order"));
761 0 : return;
762 : }
763 17 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
764 : {
765 0 : phase_end (gorc,
766 : TALER_MHD_reply_with_error (gorc->sc.con,
767 : MHD_HTTP_NOT_FOUND,
768 : TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
769 0 : hc->infix));
770 0 : return;
771 : }
772 17 : gorc->phase++;
773 : }
774 :
775 :
776 : /**
777 : * Obtain parse contract terms of the order. Extracts the fulfillment URL,
778 : * total amount, summary and timestamp from the contract terms!
779 : *
780 : * @param[in,out] gorc order context to update
781 : */
782 : static void
783 78 : phase_parse_contract (struct GetOrderRequestContext *gorc)
784 : {
785 78 : struct TMH_HandlerContext *hc = gorc->hc;
786 :
787 78 : if ( (NULL == gorc->order) &&
788 78 : (NULL != gorc->order_json) )
789 : {
790 0 : gorc->order = TALER_MERCHANT_order_parse (
791 : gorc->order_json);
792 :
793 0 : if (NULL == gorc->order)
794 : {
795 0 : GNUNET_break (0);
796 0 : phase_end (gorc,
797 : TALER_MHD_reply_with_error (
798 : gorc->sc.con,
799 : MHD_HTTP_INTERNAL_SERVER_ERROR,
800 : TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
801 0 : hc->infix));
802 0 : return;
803 : }
804 0 : gorc->ct = gorc->order->base;
805 0 : gorc->timestamp = gorc->order->timestamp;
806 : }
807 78 : if ( (NULL == gorc->contract_terms) &&
808 76 : (NULL != gorc->contract_terms_json) )
809 : {
810 76 : if (NULL ==
811 76 : json_object_get (gorc->contract_terms_json,
812 : "nonce"))
813 : {
814 : /* only have a proto contract */
815 17 : gorc->pc = TALER_MERCHANT_proto_contract_parse (
816 : gorc->contract_terms_json);
817 :
818 17 : if (NULL == gorc->pc)
819 : {
820 0 : GNUNET_break (0);
821 0 : phase_end (gorc,
822 : TALER_MHD_reply_with_error (
823 : gorc->sc.con,
824 : MHD_HTTP_INTERNAL_SERVER_ERROR,
825 : TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
826 0 : hc->infix));
827 0 : return;
828 : }
829 17 : gorc->ct = gorc->pc->base;
830 17 : gorc->timestamp = gorc->pc->timestamp;
831 : }
832 : else
833 : {
834 59 : gorc->contract_terms = TALER_MERCHANT_contract_parse (
835 : gorc->contract_terms_json);
836 59 : if (NULL == gorc->contract_terms)
837 : {
838 0 : GNUNET_break (0);
839 0 : phase_end (gorc,
840 : TALER_MHD_reply_with_error (
841 : gorc->sc.con,
842 : MHD_HTTP_INTERNAL_SERVER_ERROR,
843 : TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
844 0 : hc->infix));
845 0 : return;
846 : }
847 59 : gorc->pc = gorc->contract_terms->pc;
848 59 : gorc->ct = gorc->pc->base;
849 59 : gorc->timestamp = gorc->contract_terms->pc->timestamp;
850 : }
851 : }
852 :
853 : /* Now that the contract terms (and thus the fulfillment URL) are
854 : available, subscribe to session-capture triggers if requested.
855 : The @e session_eh guard makes this idempotent across the phase
856 : re-runs of the long-polling loop; @e gorc->session_eh is cancelled
857 : in gorc_cleanup(). */
858 78 : if ( (NULL != gorc->session_id) &&
859 14 : (NULL != gorc->ct->fulfillment_url) &&
860 14 : (NULL == gorc->session_eh) )
861 : {
862 14 : struct TMH_SessionEventP session_eh = {
863 14 : .header.size = htons (sizeof (session_eh)),
864 14 : .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED),
865 14 : .merchant_pub = hc->instance->merchant_pub
866 : };
867 :
868 14 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
869 : "Subscribing to session triggers for %p\n",
870 : gorc);
871 14 : GNUNET_CRYPTO_hash (gorc->session_id,
872 : strlen (gorc->session_id),
873 : &session_eh.h_session_id);
874 14 : GNUNET_CRYPTO_hash (gorc->ct->fulfillment_url,
875 14 : strlen (gorc->ct->fulfillment_url),
876 : &session_eh.h_fulfillment_url);
877 : gorc->session_eh
878 14 : = TALER_MERCHANTDB_event_listen (
879 : TMH_db,
880 : &session_eh.header,
881 : GNUNET_TIME_absolute_get_remaining (gorc->sc.long_poll_timeout),
882 : &resume_by_event,
883 : gorc);
884 14 : gorc->instant_retry = true;
885 : }
886 :
887 78 : switch (gorc->ct->version)
888 : {
889 72 : case TALER_MERCHANT_CONTRACT_VERSION_0:
890 : gorc->contract_amount
891 144 : = (NULL != gorc->pc)
892 72 : ? gorc->pc->details.v0.brutto
893 72 : : gorc->order->details.v0.brutto;
894 72 : break;
895 6 : case TALER_MERCHANT_CONTRACT_VERSION_1:
896 6 : if (gorc->choice_index >= 0)
897 : {
898 6 : if (gorc->choice_index >=
899 6 : ( (NULL != gorc->pc)
900 6 : ? gorc->pc->details.v1.choices_len
901 6 : : gorc->order->details.v1.choices_len) )
902 : {
903 0 : GNUNET_break (0);
904 0 : phase_end (gorc,
905 : TALER_MHD_reply_with_error (
906 : gorc->sc.con,
907 : MHD_HTTP_INTERNAL_SERVER_ERROR,
908 : TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
909 : NULL));
910 0 : return;
911 : }
912 6 : gorc->contract_amount =
913 6 : (NULL != gorc->pc)
914 6 : ? gorc->pc->details.v1.choices[gorc->choice_index].amount
915 6 : : gorc->order->details.v1.choices[gorc->choice_index].amount;
916 : }
917 : else
918 : {
919 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
920 : "Choice index %i is invalid",
921 : gorc->choice_index);
922 : }
923 6 : break;
924 0 : default:
925 : {
926 0 : GNUNET_break (0);
927 0 : phase_end (gorc,
928 : TALER_MHD_reply_with_error (
929 : gorc->sc.con,
930 : MHD_HTTP_INTERNAL_SERVER_ERROR,
931 : TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_VERSION,
932 : NULL));
933 0 : return;
934 : }
935 : }
936 :
937 139 : if ( (NULL != gorc->contract_terms) &&
938 : (GNUNET_OK !=
939 61 : TALER_JSON_contract_hash (gorc->contract_terms_json,
940 : &gorc->h_contract_terms)) )
941 : {
942 0 : GNUNET_break (0);
943 0 : phase_end (gorc,
944 : TALER_MHD_reply_with_error (gorc->sc.con,
945 : MHD_HTTP_INTERNAL_SERVER_ERROR,
946 : TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
947 : NULL));
948 0 : return;
949 : }
950 78 : GNUNET_assert ( (NULL != gorc->contract_terms_json) ||
951 : (NULL != gorc->order_json) );
952 78 : GNUNET_assert ( (NULL != gorc->pc) ||
953 : (NULL != gorc->order) );
954 78 : gorc->phase++;
955 : }
956 :
957 :
958 : /**
959 : * Check payment status of the order.
960 : *
961 : * @param[in,out] gorc order context to update
962 : */
963 : static void
964 78 : phase_check_paid (struct GetOrderRequestContext *gorc)
965 : {
966 78 : struct TMH_HandlerContext *hc = gorc->hc;
967 :
968 78 : if (gorc->order_only)
969 : {
970 17 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
971 : "Order %s unclaimed, no need to lookup payment status\n",
972 : hc->infix);
973 17 : GNUNET_assert (! gorc->paid);
974 17 : GNUNET_assert (! gorc->wired);
975 17 : gorc->phase++;
976 17 : return;
977 : }
978 61 : if (NULL == gorc->session_id)
979 : {
980 50 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
981 : "No session ID, do not need to lookup session-ID specific payment status (%s/%s)\n",
982 : gorc->paid ? "paid" : "unpaid",
983 : gorc->wired ? "wired" : "unwired");
984 50 : gorc->phase++;
985 50 : return;
986 : }
987 11 : if (! gorc->paid_session_matches)
988 : {
989 8 : gorc->paid = false;
990 8 : gorc->wired = false;
991 : }
992 11 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
993 : "Order %s %s for session %s (%s)\n",
994 : hc->infix,
995 : gorc->paid ? "paid" : "unpaid",
996 : gorc->session_id,
997 : gorc->wired ? "wired" : "unwired");
998 11 : gorc->phase++;
999 : }
1000 :
1001 :
1002 : /**
1003 : * Check if the @a reply satisfies the long-poll not_etag
1004 : * constraint. If so, return it as a response for @a gorc,
1005 : * otherwise suspend and wait for a change.
1006 : *
1007 : * @param[in,out] gorc request to handle
1008 : * @param reply body for JSON response (#MHD_HTTP_OK)
1009 : */
1010 : static void
1011 76 : check_reply (struct GetOrderRequestContext *gorc,
1012 : const json_t *reply)
1013 : {
1014 : struct GNUNET_ShortHashCode sh;
1015 : unsigned int http_response_code;
1016 : bool not_modified;
1017 : struct MHD_Response *response;
1018 : char *can;
1019 :
1020 76 : can = TALER_JSON_canonicalize (reply);
1021 76 : GNUNET_assert (GNUNET_YES ==
1022 : GNUNET_CRYPTO_hkdf_gnunet (&sh,
1023 : sizeof (sh),
1024 : "GOR-SALT",
1025 : strlen ("GOR-SALT"),
1026 : can,
1027 : strlen (can)));
1028 78 : not_modified = gorc->have_lp_not_etag &&
1029 2 : (0 == GNUNET_memcmp (&sh,
1030 : &gorc->lp_not_etag));
1031 :
1032 76 : if (not_modified &&
1033 1 : (! GNUNET_TIME_absolute_is_past (gorc->sc.long_poll_timeout)) )
1034 : {
1035 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1036 : "Status unchanged, not returning response yet\n");
1037 1 : GNUNET_assert (GNUNET_NO == gorc->suspended);
1038 1 : if (gorc->instant_retry)
1039 : {
1040 0 : gorc->instant_retry = false;
1041 0 : gorc->phase = GOP_FETCH_CONTRACT;
1042 0 : GNUNET_free (can);
1043 1 : return;
1044 : }
1045 : /* note: not necessarily actually unpaid ... */
1046 1 : GNUNET_CONTAINER_DLL_insert (gorc_head,
1047 : gorc_tail,
1048 : gorc);
1049 1 : gorc->phase = GOP_SUSPENDED_ON_UNPAID;
1050 1 : gorc->suspended = GNUNET_YES;
1051 1 : MHD_suspend_connection (gorc->sc.con);
1052 1 : GNUNET_free (can);
1053 1 : return;
1054 : }
1055 : {
1056 : const char *inm;
1057 :
1058 75 : inm = MHD_lookup_connection_value (gorc->sc.con,
1059 : MHD_HEADER_KIND,
1060 : MHD_HTTP_HEADER_IF_NONE_MATCH);
1061 75 : if ( (NULL == inm) ||
1062 1 : ('"' != inm[0]) ||
1063 1 : ('"' != inm[strlen (inm) - 1]) ||
1064 1 : (0 != strncmp (inm + 1,
1065 : can,
1066 : strlen (can))) )
1067 75 : not_modified = false; /* must return full response */
1068 : }
1069 75 : GNUNET_free (can);
1070 75 : http_response_code = not_modified
1071 : ? MHD_HTTP_NOT_MODIFIED
1072 75 : : MHD_HTTP_OK;
1073 75 : response = TALER_MHD_make_json (reply);
1074 : {
1075 : char *etag;
1076 : char *qetag;
1077 :
1078 75 : etag = GNUNET_STRINGS_data_to_string_alloc (&sh,
1079 : sizeof (sh));
1080 75 : GNUNET_asprintf (&qetag,
1081 : "\"%s\"",
1082 : etag);
1083 75 : GNUNET_break (MHD_YES ==
1084 : MHD_add_response_header (response,
1085 : MHD_HTTP_HEADER_ETAG,
1086 : qetag));
1087 75 : GNUNET_free (qetag);
1088 75 : GNUNET_free (etag);
1089 : }
1090 :
1091 : {
1092 : enum MHD_Result ret;
1093 :
1094 75 : ret = MHD_queue_response (gorc->sc.con,
1095 : http_response_code,
1096 : response);
1097 75 : MHD_destroy_response (response);
1098 75 : phase_end (gorc,
1099 : ret);
1100 : }
1101 : }
1102 :
1103 :
1104 : /**
1105 : * Check if re-purchase detection applies to the order.
1106 : *
1107 : * @param[in,out] gorc order context to update
1108 : */
1109 : static void
1110 78 : phase_check_repurchase (struct GetOrderRequestContext *gorc)
1111 : {
1112 78 : struct TMH_HandlerContext *hc = gorc->hc;
1113 78 : char *already_paid_order_id = NULL;
1114 : enum GNUNET_DB_QueryStatus qs;
1115 : char *taler_pay_uri;
1116 : char *order_status_url;
1117 : json_t *reply;
1118 :
1119 78 : if ( (gorc->paid) ||
1120 34 : (NULL == gorc->ct->fulfillment_url) ||
1121 20 : (NULL == gorc->session_id) )
1122 : {
1123 : /* Repurchase cannot apply */
1124 67 : gorc->phase++;
1125 74 : return;
1126 : }
1127 11 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1128 : "Running re-purchase detection for %s/%s\n",
1129 : gorc->session_id,
1130 : gorc->ct->fulfillment_url);
1131 11 : qs = TALER_MERCHANTDB_get_order_by_fulfillment (
1132 : TMH_db,
1133 11 : hc->instance->settings.id,
1134 11 : gorc->ct->fulfillment_url,
1135 : gorc->session_id,
1136 : TALER_EXCHANGE_YNA_NO !=
1137 11 : gorc->allow_refunded_for_repurchase,
1138 : &already_paid_order_id);
1139 11 : if (0 > qs)
1140 : {
1141 : /* single, read-only SQL statements should never cause
1142 : serialization problems, and the entry should exist as per above */
1143 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
1144 0 : phase_end (gorc,
1145 : TALER_MHD_reply_with_error (gorc->sc.con,
1146 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1147 : TALER_EC_GENERIC_DB_FETCH_FAILED,
1148 : "order by fulfillment"));
1149 0 : return;
1150 : }
1151 11 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1152 : {
1153 7 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1154 : "No already paid order for %s/%s\n",
1155 : gorc->session_id,
1156 : gorc->ct->fulfillment_url);
1157 7 : gorc->phase++;
1158 7 : return;
1159 : }
1160 :
1161 : /* User did pay for this order, but under a different session; ask wallet to
1162 : switch order ID */
1163 4 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1164 : "Found already paid order %s\n",
1165 : already_paid_order_id);
1166 4 : taler_pay_uri = TMH_make_taler_pay_uri (gorc->sc.con,
1167 4 : hc->infix,
1168 : gorc->session_id,
1169 4 : hc->instance->settings.id,
1170 : &gorc->claim_token);
1171 4 : order_status_url = TMH_make_order_status_url (gorc->sc.con,
1172 4 : hc->infix,
1173 : gorc->session_id,
1174 4 : hc->instance->settings.id,
1175 : &gorc->claim_token,
1176 : NULL);
1177 4 : if ( (NULL == taler_pay_uri) ||
1178 : (NULL == order_status_url) )
1179 : {
1180 0 : GNUNET_break_op (0);
1181 0 : GNUNET_free (taler_pay_uri);
1182 0 : GNUNET_free (order_status_url);
1183 0 : phase_end (gorc,
1184 : TALER_MHD_reply_with_error (gorc->sc.con,
1185 : MHD_HTTP_BAD_REQUEST,
1186 : TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
1187 : "host"));
1188 0 : return;
1189 : }
1190 4 : reply = GNUNET_JSON_PACK (
1191 : GNUNET_JSON_pack_string ("taler_pay_uri",
1192 : taler_pay_uri),
1193 : GNUNET_JSON_pack_string ("order_status_url",
1194 : order_status_url),
1195 : GNUNET_JSON_pack_string ("order_status",
1196 : "unpaid"),
1197 : GNUNET_JSON_pack_string ("already_paid_order_id",
1198 : already_paid_order_id),
1199 : GNUNET_JSON_pack_string ("already_paid_fulfillment_url",
1200 : gorc->ct->fulfillment_url),
1201 : /* undefined for unpaid v1 contracts */
1202 : GNUNET_JSON_pack_allow_null (
1203 : TALER_JSON_pack_amount ("total_amount",
1204 : TALER_amount_is_valid (&gorc->contract_amount)
1205 : ? &gorc->contract_amount
1206 : : NULL)),
1207 : GNUNET_JSON_pack_object_incref ("proto_contract_terms",
1208 : gorc->contract_terms_json),
1209 : GNUNET_JSON_pack_string ("summary",
1210 : gorc->ct->summary),
1211 : GNUNET_JSON_pack_timestamp ("pay_deadline",
1212 : NULL != gorc->pc
1213 : ? gorc->pc->pay_deadline
1214 : : gorc->order->pay_deadline),
1215 : GNUNET_JSON_pack_timestamp ("creation_time",
1216 : gorc->timestamp));
1217 :
1218 4 : GNUNET_free (order_status_url);
1219 4 : GNUNET_free (taler_pay_uri);
1220 4 : GNUNET_free (already_paid_order_id);
1221 4 : check_reply (gorc,
1222 : reply);
1223 4 : json_decref (reply);
1224 : }
1225 :
1226 :
1227 : /**
1228 : * Check if we should suspend until the order is paid.
1229 : *
1230 : * @param[in,out] gorc order context to update
1231 : */
1232 : static void
1233 74 : phase_unpaid_finish (struct GetOrderRequestContext *gorc)
1234 : {
1235 74 : struct TMH_HandlerContext *hc = gorc->hc;
1236 : char *order_status_url;
1237 :
1238 74 : if (gorc->paid)
1239 : {
1240 44 : gorc->phase++;
1241 44 : return;
1242 : }
1243 : /* User never paid for this order, suspend waiting
1244 : on payment or return details. */
1245 30 : if (GNUNET_TIME_absolute_is_future (gorc->sc.long_poll_timeout) &&
1246 4 : (! gorc->have_lp_not_etag) )
1247 : {
1248 2 : if (gorc->instant_retry)
1249 : {
1250 0 : gorc->instant_retry = false;
1251 0 : gorc->phase = GOP_FETCH_CONTRACT;
1252 0 : return;
1253 : }
1254 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1255 : "Suspending GET /private/orders/%s\n",
1256 : hc->infix);
1257 2 : GNUNET_CONTAINER_DLL_insert (gorc_head,
1258 : gorc_tail,
1259 : gorc);
1260 2 : gorc->phase = GOP_SUSPENDED_ON_UNPAID;
1261 2 : gorc->suspended = GNUNET_YES;
1262 2 : MHD_suspend_connection (gorc->sc.con);
1263 2 : return;
1264 : }
1265 28 : order_status_url = TMH_make_order_status_url (gorc->sc.con,
1266 28 : hc->infix,
1267 : gorc->session_id,
1268 28 : hc->instance->settings.id,
1269 : &gorc->claim_token,
1270 : NULL);
1271 28 : if (! gorc->order_only)
1272 : {
1273 : json_t *reply;
1274 :
1275 11 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1276 : "Order %s claimed but not paid yet\n",
1277 : hc->infix);
1278 11 : reply = GNUNET_JSON_PACK (
1279 : GNUNET_JSON_pack_string ("order_status_url",
1280 : order_status_url),
1281 : GNUNET_JSON_pack_object_incref ("contract_terms",
1282 : gorc->contract_terms_json),
1283 : GNUNET_JSON_pack_string ("order_status",
1284 : "claimed"));
1285 11 : GNUNET_free (order_status_url);
1286 11 : check_reply (gorc,
1287 : reply);
1288 11 : json_decref (reply);
1289 11 : return;
1290 : }
1291 : {
1292 : char *taler_pay_uri;
1293 : json_t *reply;
1294 :
1295 17 : taler_pay_uri = TMH_make_taler_pay_uri (gorc->sc.con,
1296 17 : hc->infix,
1297 : gorc->session_id,
1298 17 : hc->instance->settings.id,
1299 : &gorc->claim_token);
1300 17 : reply = GNUNET_JSON_PACK (
1301 : GNUNET_JSON_pack_string ("taler_pay_uri",
1302 : taler_pay_uri),
1303 : GNUNET_JSON_pack_string ("order_status_url",
1304 : order_status_url),
1305 : GNUNET_JSON_pack_string ("order_status",
1306 : "unpaid"),
1307 : GNUNET_JSON_pack_object_incref ("proto_contract_terms",
1308 : gorc->contract_terms_json),
1309 : /* undefined for unpaid v1 contracts */
1310 : GNUNET_JSON_pack_allow_null (
1311 : TALER_JSON_pack_amount ("total_amount",
1312 : &gorc->contract_amount)),
1313 : GNUNET_JSON_pack_string ("summary",
1314 : gorc->ct->summary),
1315 : GNUNET_JSON_pack_timestamp ("creation_time",
1316 : gorc->timestamp));
1317 17 : check_reply (gorc,
1318 : reply);
1319 17 : json_decref (reply);
1320 17 : GNUNET_free (taler_pay_uri);
1321 : }
1322 17 : GNUNET_free (order_status_url);
1323 : }
1324 :
1325 :
1326 : /**
1327 : * Function called with each @a coin_pub that was deposited into the
1328 : * @a h_wire account of the merchant for the @a deposit_serial as part
1329 : * of the payment for the order identified by @a cls.
1330 : *
1331 : * Queries the exchange for the payment status associated with the
1332 : * given coin.
1333 : *
1334 : * @param cls a `struct GetOrderRequestContext`
1335 : * @param deposit_serial identifies the deposit operation
1336 : * @param exchange_url URL of the exchange that issued @a coin_pub
1337 : * @param h_wire hash of the merchant's wire account into which the deposit was made
1338 : * @param deposit_timestamp when was the deposit made
1339 : * @param amount_with_fee amount the exchange will deposit for this coin
1340 : * @param deposit_fee fee the exchange will charge for this coin
1341 : * @param coin_pub public key of the deposited coin
1342 : */
1343 : static void
1344 40 : deposit_cb (
1345 : void *cls,
1346 : uint64_t deposit_serial,
1347 : const char *exchange_url,
1348 : const struct TALER_MerchantWireHashP *h_wire,
1349 : struct GNUNET_TIME_Timestamp deposit_timestamp,
1350 : const struct TALER_Amount *amount_with_fee,
1351 : const struct TALER_Amount *deposit_fee,
1352 : const struct TALER_CoinSpendPublicKeyP *coin_pub)
1353 : {
1354 40 : struct GetOrderRequestContext *gorc = cls;
1355 : struct TransferQuery *tq;
1356 :
1357 40 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1358 : "Checking deposit status for coin %s (over %s)\n",
1359 : TALER_B2S (coin_pub),
1360 : TALER_amount2s (amount_with_fee));
1361 : gorc->last_payment
1362 40 : = GNUNET_TIME_timestamp_max (gorc->last_payment,
1363 : deposit_timestamp);
1364 40 : tq = GNUNET_new (struct TransferQuery);
1365 40 : tq->gorc = gorc;
1366 40 : tq->exchange_url = GNUNET_strdup (exchange_url);
1367 40 : tq->deposit_serial = deposit_serial;
1368 40 : GNUNET_CONTAINER_DLL_insert (gorc->tq_head,
1369 : gorc->tq_tail,
1370 : tq);
1371 40 : tq->coin_pub = *coin_pub;
1372 40 : tq->h_wire = *h_wire;
1373 40 : tq->amount_with_fee = *amount_with_fee;
1374 40 : tq->deposit_fee = *deposit_fee;
1375 40 : }
1376 :
1377 :
1378 : /**
1379 : * Check wire transfer status for the order at the exchange.
1380 : *
1381 : * @param[in,out] gorc order context to update
1382 : */
1383 : static void
1384 44 : phase_check_deposits (struct GetOrderRequestContext *gorc)
1385 : {
1386 44 : GNUNET_assert (! gorc->order_only);
1387 44 : GNUNET_assert (gorc->paid);
1388 :
1389 : /* amount must be always valid for paid orders */
1390 44 : GNUNET_assert (GNUNET_OK ==
1391 : TALER_amount_is_valid (&gorc->contract_amount));
1392 :
1393 44 : GNUNET_assert (GNUNET_OK ==
1394 : TALER_amount_set_zero (gorc->contract_amount.currency,
1395 : &gorc->deposits_total));
1396 44 : GNUNET_assert (GNUNET_OK ==
1397 : TALER_amount_set_zero (gorc->contract_amount.currency,
1398 : &gorc->deposit_fees_total));
1399 44 : GNUNET_assert (GNUNET_OK ==
1400 : TALER_amount_set_zero (gorc->contract_amount.currency,
1401 : &gorc->deposit_fees_refunded_total));
1402 44 : TALER_MERCHANTDB_iterate_deposits_by_order (TMH_db,
1403 : gorc->order_serial,
1404 : &deposit_cb,
1405 : gorc);
1406 44 : gorc->phase++;
1407 44 : }
1408 :
1409 :
1410 : /**
1411 : * Function called with information about a refund.
1412 : * It is responsible for summing up the refund amount.
1413 : *
1414 : * @param cls closure
1415 : * @param refund_serial unique serial number of the refund
1416 : * @param timestamp time of the refund (for grouping of refunds in the wallet UI)
1417 : * @param coin_pub public coin from which the refund comes from
1418 : * @param exchange_url URL of the exchange that issued @a coin_pub
1419 : * @param rtransaction_id identificator of the refund
1420 : * @param reason human-readable explanation of the refund
1421 : * @param refund_amount refund amount which is being taken from @a coin_pub
1422 : * @param pending true if the this refund was not yet processed by the wallet/exchange
1423 : */
1424 : static void
1425 8 : process_refunds_cb (
1426 : void *cls,
1427 : uint64_t refund_serial,
1428 : struct GNUNET_TIME_Timestamp timestamp,
1429 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
1430 : const char *exchange_url,
1431 : uint64_t rtransaction_id,
1432 : const char *reason,
1433 : const struct TALER_Amount *refund_amount,
1434 : bool pending)
1435 : {
1436 8 : struct GetOrderRequestContext *gorc = cls;
1437 :
1438 8 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1439 : "Found refund %llu over %s for reason %s\n",
1440 : (unsigned long long) rtransaction_id,
1441 : TALER_amount2s (refund_amount),
1442 : reason);
1443 8 : GNUNET_assert (
1444 : 0 ==
1445 : json_array_append_new (
1446 : gorc->refund_details,
1447 : GNUNET_JSON_PACK (
1448 : TALER_JSON_pack_amount ("amount",
1449 : refund_amount),
1450 : GNUNET_JSON_pack_bool ("pending",
1451 : pending),
1452 : GNUNET_JSON_pack_timestamp ("timestamp",
1453 : timestamp),
1454 : GNUNET_JSON_pack_string ("reason",
1455 : reason))));
1456 : /* For refunded coins, we are not charged deposit fees, so subtract those
1457 : again */
1458 8 : for (struct TransferQuery *tq = gorc->tq_head;
1459 16 : NULL != tq;
1460 8 : tq = tq->next)
1461 : {
1462 8 : if (0 !=
1463 8 : strcmp (exchange_url,
1464 8 : tq->exchange_url))
1465 0 : continue;
1466 8 : if (0 !=
1467 8 : GNUNET_memcmp (&tq->coin_pub,
1468 : coin_pub))
1469 0 : continue;
1470 8 : if (GNUNET_OK !=
1471 8 : TALER_amount_cmp_currency (
1472 8 : &gorc->deposit_fees_total,
1473 8 : &tq->deposit_fee))
1474 : {
1475 0 : gorc->refund_currency_mismatch = true;
1476 0 : return;
1477 : }
1478 8 : GNUNET_assert (
1479 : 0 <=
1480 : TALER_amount_add (&gorc->deposit_fees_refunded_total,
1481 : &gorc->deposit_fees_refunded_total,
1482 : &tq->deposit_fee));
1483 : }
1484 8 : if (GNUNET_OK !=
1485 8 : TALER_amount_cmp_currency (
1486 8 : &gorc->refund_amount,
1487 : refund_amount))
1488 : {
1489 0 : gorc->refund_currency_mismatch = true;
1490 0 : return;
1491 : }
1492 8 : GNUNET_assert (0 <=
1493 : TALER_amount_add (&gorc->refund_amount,
1494 : &gorc->refund_amount,
1495 : refund_amount));
1496 8 : gorc->refunded = true;
1497 8 : gorc->refund_pending |= pending;
1498 : }
1499 :
1500 :
1501 : /**
1502 : * Function called with information about an external refund.
1503 : * Appends the refund entry to the "refunds_external" array.
1504 : *
1505 : * @param cls a `struct GetOrderRequestContext`
1506 : * @param refund_id identifier of the refund within the order
1507 : * @param refund_timestamp when was the refund recorded
1508 : * @param method external payment method used for the refund
1509 : * @param payment_id id of the original external payment entry
1510 : * @param amount amount returned to the customer
1511 : * @param reason human-readable refund justification
1512 : */
1513 : static void
1514 4 : process_external_refunds_cb (void *cls,
1515 : const char *refund_id,
1516 : struct GNUNET_TIME_Timestamp refund_timestamp,
1517 : const char *method,
1518 : const char *payment_id,
1519 : const struct TALER_Amount *amount,
1520 : const char *reason)
1521 : {
1522 4 : struct GetOrderRequestContext *gorc = cls;
1523 :
1524 4 : GNUNET_assert (
1525 : 0 ==
1526 : json_array_append_new (
1527 : gorc->refunds_external,
1528 : GNUNET_JSON_PACK (
1529 : GNUNET_JSON_pack_string ("id",
1530 : refund_id),
1531 : GNUNET_JSON_pack_string ("method",
1532 : method),
1533 : GNUNET_JSON_pack_allow_null (
1534 : GNUNET_JSON_pack_string ("payment_id",
1535 : payment_id)),
1536 : TALER_JSON_pack_amount ("amount",
1537 : amount),
1538 : GNUNET_JSON_pack_string ("reason",
1539 : reason),
1540 : GNUNET_JSON_pack_timestamp ("timestamp",
1541 : refund_timestamp))));
1542 4 : }
1543 :
1544 :
1545 : /**
1546 : * Check refund status for the order.
1547 : *
1548 : * @param[in,out] gorc order context to update
1549 : */
1550 : static void
1551 44 : phase_check_refunds (struct GetOrderRequestContext *gorc)
1552 : {
1553 44 : struct TMH_HandlerContext *hc = gorc->hc;
1554 : enum GNUNET_DB_QueryStatus qs;
1555 :
1556 44 : GNUNET_assert (! gorc->order_only);
1557 44 : GNUNET_assert (gorc->paid);
1558 :
1559 : /* Accumulate refunds, if any. */
1560 44 : GNUNET_assert (GNUNET_OK ==
1561 : TALER_amount_set_zero (gorc->contract_amount.currency,
1562 : &gorc->refund_amount));
1563 44 : json_array_clear (gorc->refund_details);
1564 44 : qs = TALER_MERCHANTDB_iterate_refunds_detailed (
1565 : TMH_db,
1566 44 : hc->instance->settings.id,
1567 44 : &gorc->h_contract_terms,
1568 : &process_refunds_cb,
1569 : gorc);
1570 44 : if (0 > qs)
1571 : {
1572 0 : GNUNET_break (0);
1573 0 : phase_end (gorc,
1574 : TALER_MHD_reply_with_error (
1575 : gorc->sc.con,
1576 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1577 : TALER_EC_GENERIC_DB_FETCH_FAILED,
1578 : "detailed refunds"));
1579 0 : return;
1580 : }
1581 44 : if (gorc->refund_currency_mismatch)
1582 : {
1583 0 : GNUNET_break (0);
1584 0 : phase_end (gorc,
1585 : TALER_MHD_reply_with_error (
1586 : gorc->sc.con,
1587 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1588 : TALER_EC_GENERIC_DB_FETCH_FAILED,
1589 : "refunds in different currency than original order price"));
1590 0 : return;
1591 : }
1592 44 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1593 : "Total refunds are %s\n",
1594 : TALER_amount2s (&gorc->refund_amount));
1595 44 : json_array_clear (gorc->refunds_external);
1596 44 : qs = TALER_MERCHANTDB_iterate_external_refunds (
1597 : TMH_db,
1598 44 : hc->instance->settings.id,
1599 44 : hc->infix,
1600 : &process_external_refunds_cb,
1601 : gorc);
1602 44 : if (0 > qs)
1603 : {
1604 0 : GNUNET_break (0);
1605 0 : phase_end (gorc,
1606 : TALER_MHD_reply_with_error (
1607 : gorc->sc.con,
1608 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1609 : TALER_EC_GENERIC_DB_FETCH_FAILED,
1610 : "external refunds"));
1611 0 : return;
1612 : }
1613 44 : gorc->phase++;
1614 : }
1615 :
1616 :
1617 : /**
1618 : * Function called with available wire details, to be added to
1619 : * the response.
1620 : *
1621 : * @param cls a `struct GetOrderRequestContext`
1622 : * @param wtid wire transfer subject of the wire transfer for the coin
1623 : * @param exchange_url base URL of the exchange that made the payment
1624 : * @param execution_time when was the payment made
1625 : * @param deposit_value contribution of the coin to the total wire transfer value
1626 : * @param deposit_fee deposit fee charged by the exchange for the coin
1627 : * @param transfer_confirmed did the merchant confirm that a wire transfer with
1628 : * @a wtid over the total amount happened?
1629 : * @param expected_credit_serial row for the expected wire transfer this
1630 : * entry references
1631 : */
1632 : static void
1633 14 : process_transfer_details (
1634 : void *cls,
1635 : const struct TALER_WireTransferIdentifierRawP *wtid,
1636 : const char *exchange_url,
1637 : struct GNUNET_TIME_Timestamp execution_time,
1638 : const struct TALER_Amount *deposit_value,
1639 : const struct TALER_Amount *deposit_fee,
1640 : bool transfer_confirmed,
1641 : uint64_t expected_credit_serial)
1642 : {
1643 14 : struct GetOrderRequestContext *gorc = cls;
1644 14 : json_t *wire_details = gorc->wire_details;
1645 : struct TALER_Amount wired;
1646 :
1647 14 : if ( (GNUNET_OK !=
1648 14 : TALER_amount_cmp_currency (&gorc->deposits_total,
1649 14 : deposit_value)) ||
1650 : (GNUNET_OK !=
1651 14 : TALER_amount_cmp_currency (&gorc->deposit_fees_total,
1652 : deposit_fee)) )
1653 : {
1654 0 : GNUNET_break (0);
1655 0 : gorc->deposit_currency_mismatch = true;
1656 0 : return;
1657 : }
1658 :
1659 : /* Compute total amount *wired* */
1660 14 : GNUNET_assert (0 <=
1661 : TALER_amount_add (&gorc->deposits_total,
1662 : &gorc->deposits_total,
1663 : deposit_value));
1664 14 : GNUNET_assert (0 <=
1665 : TALER_amount_add (&gorc->deposit_fees_total,
1666 : &gorc->deposit_fees_total,
1667 : deposit_fee));
1668 14 : GNUNET_assert (0 <= TALER_amount_subtract (&wired,
1669 : deposit_value,
1670 : deposit_fee));
1671 14 : GNUNET_assert (0 ==
1672 : json_array_append_new (
1673 : wire_details,
1674 : GNUNET_JSON_PACK (
1675 : GNUNET_JSON_pack_data_auto ("wtid",
1676 : wtid),
1677 : GNUNET_JSON_pack_string ("exchange_url",
1678 : exchange_url),
1679 : TALER_JSON_pack_amount ("amount",
1680 : &wired),
1681 : TALER_JSON_pack_amount ("deposit_fee",
1682 : deposit_fee),
1683 : GNUNET_JSON_pack_timestamp ("execution_time",
1684 : execution_time),
1685 : GNUNET_JSON_pack_bool ("confirmed",
1686 : transfer_confirmed),
1687 : GNUNET_JSON_pack_uint64 ("expected_transfer_serial_id",
1688 : expected_credit_serial))));
1689 : }
1690 :
1691 :
1692 : /**
1693 : * Check transfer status in local database.
1694 : *
1695 : * @param[in,out] gorc order context to update
1696 : */
1697 : static void
1698 44 : phase_check_local_transfers (struct GetOrderRequestContext *gorc)
1699 : {
1700 44 : struct TMH_HandlerContext *hc = gorc->hc;
1701 : enum GNUNET_DB_QueryStatus qs;
1702 :
1703 44 : GNUNET_assert (gorc->paid);
1704 44 : GNUNET_assert (! gorc->order_only);
1705 :
1706 44 : GNUNET_assert (GNUNET_OK ==
1707 : TALER_amount_set_zero (gorc->contract_amount.currency,
1708 : &gorc->deposits_total));
1709 44 : GNUNET_assert (GNUNET_OK ==
1710 : TALER_amount_set_zero (gorc->contract_amount.currency,
1711 : &gorc->deposit_fees_total));
1712 44 : GNUNET_assert (NULL != gorc->wire_details);
1713 : /* We may be running again due to long-polling, clear state first */
1714 44 : json_array_clear (gorc->wire_details);
1715 44 : qs = TALER_MERCHANTDB_iterate_transfer_details_by_order (TMH_db,
1716 : gorc->order_serial,
1717 : &process_transfer_details,
1718 : gorc);
1719 44 : if (0 > qs)
1720 : {
1721 0 : GNUNET_break (0);
1722 0 : phase_end (gorc,
1723 : TALER_MHD_reply_with_error (gorc->sc.con,
1724 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1725 : TALER_EC_GENERIC_DB_FETCH_FAILED,
1726 : "transfer details"));
1727 0 : return;
1728 : }
1729 44 : if (gorc->deposit_currency_mismatch)
1730 : {
1731 0 : GNUNET_break (0);
1732 0 : phase_end (gorc,
1733 : TALER_MHD_reply_with_error (gorc->sc.con,
1734 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1735 : TALER_EC_GENERIC_DB_FETCH_FAILED,
1736 : "deposits in different currency than original order price"));
1737 0 : return;
1738 : }
1739 :
1740 44 : if (! gorc->wired)
1741 : {
1742 : /* we believe(d) the wire transfer did not happen yet, check if maybe
1743 : in light of new evidence it did */
1744 : struct TALER_Amount expect_total;
1745 :
1746 30 : if (0 >
1747 30 : TALER_amount_subtract (&expect_total,
1748 30 : &gorc->contract_amount,
1749 30 : &gorc->refund_amount))
1750 : {
1751 0 : GNUNET_break (0);
1752 0 : phase_end (gorc,
1753 : TALER_MHD_reply_with_error (
1754 : gorc->sc.con,
1755 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1756 : TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
1757 : "refund exceeds contract value"));
1758 0 : return;
1759 : }
1760 30 : GNUNET_assert (
1761 : 0 <=
1762 : TALER_amount_add (&expect_total,
1763 : &expect_total,
1764 : &gorc->deposit_fees_refunded_total));
1765 :
1766 30 : if (0 >
1767 30 : TALER_amount_subtract (&expect_total,
1768 : &expect_total,
1769 30 : &gorc->deposit_fees_total))
1770 : {
1771 0 : GNUNET_break (0);
1772 0 : phase_end (gorc,
1773 : TALER_MHD_reply_with_error (
1774 : gorc->sc.con,
1775 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1776 : TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
1777 : "deposit fees exceed total minus refunds"));
1778 0 : return;
1779 : }
1780 30 : if (0 >=
1781 30 : TALER_amount_cmp (&expect_total,
1782 30 : &gorc->deposits_total))
1783 : {
1784 : /* expect_total <= gorc->deposits_total: good: we got the wire transfer */
1785 4 : gorc->wired = true;
1786 4 : qs = TALER_MERCHANTDB_update_to_contract_terms_wired (TMH_db,
1787 : gorc->order_serial);
1788 4 : GNUNET_break (qs >= 0); /* just warn if transaction failed */
1789 4 : TMH_notify_order_change (hc->instance,
1790 : TMH_OSF_PAID
1791 : | TMH_OSF_WIRED,
1792 : gorc->timestamp,
1793 : gorc->order_serial);
1794 : }
1795 : }
1796 44 : gorc->phase++;
1797 : }
1798 :
1799 :
1800 : /**
1801 : * Generate final result for the status request.
1802 : *
1803 : * @param[in,out] gorc order context to update
1804 : */
1805 : static void
1806 44 : phase_reply_result (struct GetOrderRequestContext *gorc)
1807 : {
1808 44 : struct TMH_HandlerContext *hc = gorc->hc;
1809 : char *order_status_url;
1810 :
1811 44 : GNUNET_assert (gorc->paid);
1812 44 : GNUNET_assert (! gorc->order_only);
1813 :
1814 : {
1815 44 : struct TALER_PrivateContractHashP *h_contract = NULL;
1816 :
1817 : /* In a session-bound payment, allow the browser to check the order
1818 : * status page (e.g. to get a refund).
1819 : *
1820 : * Note that we don't allow this outside of session-based payment, as
1821 : * otherwise this becomes an oracle to convert order_id to h_contract.
1822 : */
1823 44 : if (NULL != gorc->session_id)
1824 3 : h_contract = &gorc->h_contract_terms;
1825 :
1826 : order_status_url =
1827 44 : TMH_make_order_status_url (gorc->sc.con,
1828 44 : hc->infix,
1829 : gorc->session_id,
1830 44 : hc->instance->settings.id,
1831 : &gorc->claim_token,
1832 : h_contract);
1833 : }
1834 44 : if (GNUNET_TIME_absolute_is_zero (gorc->last_payment.abs_time))
1835 : {
1836 4 : GNUNET_break (GNUNET_YES ==
1837 : TALER_amount_is_zero (&gorc->contract_amount));
1838 4 : gorc->last_payment = gorc->timestamp;
1839 : }
1840 : {
1841 : json_t *reply;
1842 :
1843 44 : reply = GNUNET_JSON_PACK (
1844 : // Deprecated in protocol v6!
1845 : GNUNET_JSON_pack_array_steal ("wire_reports",
1846 : json_array ()),
1847 : GNUNET_JSON_pack_uint64 ("exchange_code",
1848 : gorc->exchange_ec),
1849 : GNUNET_JSON_pack_uint64 ("exchange_http_status",
1850 : gorc->exchange_hc),
1851 : /* legacy: */
1852 : GNUNET_JSON_pack_uint64 ("exchange_ec",
1853 : gorc->exchange_ec),
1854 : /* legacy: */
1855 : GNUNET_JSON_pack_uint64 ("exchange_hc",
1856 : gorc->exchange_hc),
1857 : TALER_JSON_pack_amount ("deposit_total",
1858 : &gorc->deposits_total),
1859 : GNUNET_JSON_pack_object_incref ("contract_terms",
1860 : gorc->contract_terms_json),
1861 : GNUNET_JSON_pack_string ("order_status",
1862 : "paid"),
1863 : GNUNET_JSON_pack_timestamp ("last_payment",
1864 : gorc->last_payment),
1865 : GNUNET_JSON_pack_bool ("refunded",
1866 : gorc->refunded),
1867 : GNUNET_JSON_pack_bool ("wired",
1868 : gorc->wired),
1869 : GNUNET_JSON_pack_bool ("refund_pending",
1870 : gorc->refund_pending),
1871 : GNUNET_JSON_pack_allow_null (
1872 : TALER_JSON_pack_amount ("refund_amount",
1873 : &gorc->refund_amount)),
1874 : GNUNET_JSON_pack_array_incref ("wire_details",
1875 : gorc->wire_details),
1876 : GNUNET_JSON_pack_array_incref ("refund_details",
1877 : gorc->refund_details),
1878 : GNUNET_JSON_pack_array_incref ("refunds_external",
1879 : gorc->refunds_external),
1880 : GNUNET_JSON_pack_string ("order_status_url",
1881 : order_status_url),
1882 : (gorc->choice_index >= 0)
1883 : ? GNUNET_JSON_pack_int64 ("choice_index",
1884 : gorc->choice_index)
1885 : : GNUNET_JSON_pack_end_ ());
1886 44 : check_reply (gorc,
1887 : reply);
1888 44 : json_decref (reply);
1889 : }
1890 44 : GNUNET_free (order_status_url);
1891 44 : }
1892 :
1893 :
1894 : /**
1895 : * End with error status in wire_hc and wire_ec.
1896 : *
1897 : * @param[in,out] gorc order context to update
1898 : */
1899 : static void
1900 0 : phase_error (struct GetOrderRequestContext *gorc)
1901 : {
1902 0 : GNUNET_assert (TALER_EC_NONE != gorc->wire_ec);
1903 0 : phase_end (gorc,
1904 : TALER_MHD_reply_with_error (gorc->sc.con,
1905 : gorc->wire_hc,
1906 : gorc->wire_ec,
1907 : NULL));
1908 0 : }
1909 :
1910 :
1911 : enum MHD_Result
1912 78 : TMH_private_get_orders_ID (
1913 : const struct TMH_RequestHandler *rh,
1914 : struct MHD_Connection *connection,
1915 : struct TMH_HandlerContext *hc)
1916 : {
1917 78 : struct GetOrderRequestContext *gorc = hc->ctx;
1918 :
1919 78 : if (NULL == gorc)
1920 : {
1921 : /* First time here, parse request and check order is known */
1922 75 : GNUNET_assert (NULL != hc->infix);
1923 75 : gorc = GNUNET_new (struct GetOrderRequestContext);
1924 75 : hc->cc = &gorc_cleanup;
1925 75 : hc->ctx = gorc;
1926 75 : gorc->sc.con = connection;
1927 75 : gorc->hc = hc;
1928 75 : gorc->wire_details = json_array ();
1929 75 : GNUNET_assert (NULL != gorc->wire_details);
1930 75 : gorc->refund_details = json_array ();
1931 75 : GNUNET_assert (NULL != gorc->refund_details);
1932 75 : gorc->refunds_external = json_array ();
1933 75 : GNUNET_assert (NULL != gorc->refunds_external);
1934 75 : gorc->session_id = MHD_lookup_connection_value (connection,
1935 : MHD_GET_ARGUMENT_KIND,
1936 : "session_id");
1937 75 : if (! (TALER_MHD_arg_to_yna (connection,
1938 : "allow_refunded_for_repurchase",
1939 : TALER_EXCHANGE_YNA_NO,
1940 : &gorc->allow_refunded_for_repurchase)) )
1941 0 : return TALER_MHD_reply_with_error (connection,
1942 : MHD_HTTP_BAD_REQUEST,
1943 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
1944 : "allow_refunded_for_repurchase");
1945 75 : TALER_MHD_parse_request_timeout (connection,
1946 : &gorc->sc.long_poll_timeout);
1947 75 : TALER_MHD_parse_request_arg_auto (connection,
1948 : "lp_not_etag",
1949 : &gorc->lp_not_etag,
1950 : gorc->have_lp_not_etag);
1951 75 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1952 : "Starting GET /private/orders/%s processing with timeout %s\n",
1953 : hc->infix,
1954 : GNUNET_STRINGS_absolute_time_to_string (
1955 : gorc->sc.long_poll_timeout));
1956 : }
1957 78 : if (GNUNET_SYSERR == gorc->suspended)
1958 0 : return MHD_NO; /* we are in shutdown */
1959 : while (1)
1960 : {
1961 1352 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1962 : "Processing order %s in phase %d\n",
1963 : hc->infix,
1964 : (int) gorc->phase);
1965 715 : switch (gorc->phase)
1966 : {
1967 75 : case GOP_INIT:
1968 75 : phase_init (gorc);
1969 75 : break;
1970 78 : case GOP_FETCH_CONTRACT:
1971 78 : phase_fetch_contract (gorc);
1972 78 : break;
1973 78 : case GOP_PARSE_CONTRACT:
1974 78 : phase_parse_contract (gorc);
1975 78 : break;
1976 78 : case GOP_CHECK_PAID:
1977 78 : phase_check_paid (gorc);
1978 78 : break;
1979 78 : case GOP_CHECK_REPURCHASE:
1980 78 : phase_check_repurchase (gorc);
1981 78 : break;
1982 74 : case GOP_UNPAID_FINISH:
1983 74 : phase_unpaid_finish (gorc);
1984 74 : break;
1985 44 : case GOP_CHECK_DEPOSITS:
1986 44 : phase_check_deposits (gorc);
1987 44 : break;
1988 44 : case GOP_CHECK_REFUNDS:
1989 44 : phase_check_refunds (gorc);
1990 44 : break;
1991 44 : case GOP_CHECK_LOCAL_TRANSFERS:
1992 44 : phase_check_local_transfers (gorc);
1993 44 : break;
1994 44 : case GOP_REPLY_RESULT:
1995 44 : phase_reply_result (gorc);
1996 44 : break;
1997 0 : case GOP_ERROR:
1998 0 : phase_error (gorc);
1999 0 : break;
2000 3 : case GOP_SUSPENDED_ON_UNPAID:
2001 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2002 : "Suspending order request awaiting payment\n");
2003 3 : return MHD_YES;
2004 75 : case GOP_END_YES:
2005 75 : return MHD_YES;
2006 0 : case GOP_END_NO:
2007 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2008 : "Closing connection, no response generated\n");
2009 0 : return MHD_NO;
2010 : }
2011 : } /* end first-time per-request initialization */
2012 : }
|