LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-get-orders-ID.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 73.1 % 439 321
Test Date: 2025-11-06 19:31:41 Functions: 94.7 % 19 18

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2017-2024 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 taler-merchant-httpd_private-get-orders-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_merchant_util.h"
      32              : #include "taler-merchant-httpd_helper.h"
      33              : #include "taler-merchant-httpd_private-get-orders.h"
      34              : #include "taler-merchant-httpd_private-get-orders-ID.h"
      35              : 
      36              : /**
      37              :  * Data structure we keep for a check payment request.
      38              :  */
      39              : struct GetOrderRequestContext;
      40              : 
      41              : 
      42              : /**
      43              :  * Request to an exchange for details about wire transfers
      44              :  * in response to a coin's deposit operation.
      45              :  */
      46              : struct TransferQuery
      47              : {
      48              : 
      49              :   /**
      50              :    * Kept in a DLL.
      51              :    */
      52              :   struct TransferQuery *next;
      53              : 
      54              :   /**
      55              :    * Kept in a DLL.
      56              :    */
      57              :   struct TransferQuery *prev;
      58              : 
      59              :   /**
      60              :    * Base URL of the exchange.
      61              :    */
      62              :   char *exchange_url;
      63              : 
      64              :   /**
      65              :    * Overall request this TQ belongs with.
      66              :    */
      67              :   struct GetOrderRequestContext *gorc;
      68              : 
      69              :   /**
      70              :    * Hash of the merchant's bank account the transfer (presumably) went to.
      71              :    */
      72              :   struct TALER_MerchantWireHashP h_wire;
      73              : 
      74              :   /**
      75              :    * Value deposited (including deposit fee).
      76              :    */
      77              :   struct TALER_Amount amount_with_fee;
      78              : 
      79              :   /**
      80              :    * Deposit fee paid for this coin.
      81              :    */
      82              :   struct TALER_Amount deposit_fee;
      83              : 
      84              :   /**
      85              :    * Public key of the coin this is about.
      86              :    */
      87              :   struct TALER_CoinSpendPublicKeyP coin_pub;
      88              : 
      89              :   /**
      90              :    * Which deposit operation is this about?
      91              :    */
      92              :   uint64_t deposit_serial;
      93              : 
      94              : };
      95              : 
      96              : 
      97              : /**
      98              :  * Phases of order processing.
      99              :  */
     100              : enum GetOrderPhase
     101              : {
     102              :   /**
     103              :    * Initialization.
     104              :    */
     105              :   GOP_INIT = 0,
     106              : 
     107              :   /**
     108              :    * Obtain contract terms from database.
     109              :    */
     110              :   GOP_FETCH_CONTRACT = 1,
     111              : 
     112              :   /**
     113              :    * Parse the contract terms.
     114              :    */
     115              :   GOP_PARSE_CONTRACT = 2,
     116              : 
     117              :   /**
     118              :    * Check if the contract was fully paid.
     119              :    */
     120              :   GOP_CHECK_PAID = 3,
     121              : 
     122              :   /**
     123              :    * Check if the wallet may have purchased an equivalent
     124              :    * order before and we need to redirect the wallet to
     125              :    * an existing paid order.
     126              :    */
     127              :   GOP_CHECK_REPURCHASE = 4,
     128              : 
     129              :   /**
     130              :    * Terminate processing of unpaid orders, either by
     131              :    * suspending until payment or by returning the
     132              :    * unpaid order status.
     133              :    */
     134              :   GOP_UNPAID_FINISH = 5,
     135              : 
     136              :   /**
     137              :    * Check if the (paid) order was refunded.
     138              :    */
     139              :   GOP_CHECK_REFUNDS = 6,
     140              : 
     141              :   /**
     142              :    * Load all deposits associated with the order.
     143              :    */
     144              :   GOP_CHECK_DEPOSITS = 7,
     145              : 
     146              :   /**
     147              :    * Check local records for transfers of funds to
     148              :    * the merchant.
     149              :    */
     150              :   GOP_CHECK_LOCAL_TRANSFERS = 8,
     151              : 
     152              :   /**
     153              :    * Generate final comprehensive result.
     154              :    */
     155              :   GOP_REPLY_RESULT = 9,
     156              : 
     157              :   /**
     158              :    * End with the HTTP status and error code in
     159              :    * wire_hc and wire_ec.
     160              :    */
     161              :   GOP_ERROR = 10,
     162              : 
     163              :   /**
     164              :    * We are suspended awaiting payment.
     165              :    */
     166              :   GOP_SUSPENDED_ON_UNPAID = 11,
     167              : 
     168              :   /**
     169              :    * Processing is done, return #MHD_YES.
     170              :    */
     171              :   GOP_END_YES = 12,
     172              : 
     173              :   /**
     174              :    * Processing is done, return #MHD_NO.
     175              :    */
     176              :   GOP_END_NO = 13
     177              : 
     178              : };
     179              : 
     180              : 
     181              : /**
     182              :  * Data structure we keep for a check payment request.
     183              :  */
     184              : struct GetOrderRequestContext
     185              : {
     186              : 
     187              :   /**
     188              :    * Processing phase we are in.
     189              :    */
     190              :   enum GetOrderPhase phase;
     191              : 
     192              :   /**
     193              :    * Entry in the #resume_timeout_heap for this check payment, if we are
     194              :    * suspended.
     195              :    */
     196              :   struct TMH_SuspendedConnection sc;
     197              : 
     198              :   /**
     199              :    * Which merchant instance is this for?
     200              :    */
     201              :   struct TMH_HandlerContext *hc;
     202              : 
     203              :   /**
     204              :    * session of the client
     205              :    */
     206              :   const char *session_id;
     207              : 
     208              :   /**
     209              :    * Kept in a DLL while suspended on exchange.
     210              :    */
     211              :   struct GetOrderRequestContext *next;
     212              : 
     213              :   /**
     214              :    * Kept in a DLL while suspended on exchange.
     215              :    */
     216              :   struct GetOrderRequestContext *prev;
     217              : 
     218              :   /**
     219              :    * Head of DLL of individual queries for transfer data.
     220              :    */
     221              :   struct TransferQuery *tq_head;
     222              : 
     223              :   /**
     224              :    * Tail of DLL of individual queries for transfer data.
     225              :    */
     226              :   struct TransferQuery *tq_tail;
     227              : 
     228              :   /**
     229              :    * Timeout task while waiting on exchange.
     230              :    */
     231              :   struct GNUNET_SCHEDULER_Task *tt;
     232              : 
     233              :   /**
     234              :    * Database event we are waiting on to be resuming
     235              :    * for payment or refunds.
     236              :    */
     237              :   struct GNUNET_DB_EventHandler *eh;
     238              : 
     239              :   /**
     240              :    * Database event we are waiting on to be resuming
     241              :    * for session capture.
     242              :    */
     243              :   struct GNUNET_DB_EventHandler *session_eh;
     244              : 
     245              :   /**
     246              :    * Contract terms of the payment we are checking. NULL when they
     247              :    * are not (yet) known.
     248              :    */
     249              :   json_t *contract_terms_json;
     250              : 
     251              :   /**
     252              :    * Parsed contract terms, NULL when parsing failed
     253              :    */
     254              :   struct TALER_MERCHANT_Contract *contract_terms;
     255              : 
     256              :   /**
     257              :    * Claim token of the order.
     258              :    */
     259              :   struct TALER_ClaimTokenP claim_token;
     260              : 
     261              :   /**
     262              :    * Timestamp of the last payment.
     263              :    */
     264              :   struct GNUNET_TIME_Timestamp last_payment;
     265              : 
     266              :   /**
     267              :    * Wire details for the payment, to be returned in the reply. NULL
     268              :    * if not available.
     269              :    */
     270              :   json_t *wire_details;
     271              : 
     272              :   /**
     273              :    * Details about refunds, NULL if there are no refunds.
     274              :    */
     275              :   json_t *refund_details;
     276              : 
     277              :   /**
     278              :    * Amount of the order, unset for unpaid v1 orders.
     279              :    */
     280              :   struct TALER_Amount contract_amount;
     281              : 
     282              :   /**
     283              :    * Hash over the @e contract_terms.
     284              :    */
     285              :   struct TALER_PrivateContractHashP h_contract_terms;
     286              : 
     287              :   /**
     288              :    * Total amount the exchange deposited into our bank account
     289              :    * (confirmed or unconfirmed), excluding fees.
     290              :    */
     291              :   struct TALER_Amount deposits_total;
     292              : 
     293              :   /**
     294              :    * Total amount in deposit fees we paid for all coins.
     295              :    */
     296              :   struct TALER_Amount deposit_fees_total;
     297              : 
     298              :   /**
     299              :    * Total value of the coins that the exchange deposited into our bank
     300              :    * account (confirmed or unconfirmed), including deposit fees.
     301              :    */
     302              :   struct TALER_Amount value_total;
     303              : 
     304              :   /**
     305              :    * Serial ID of the order.
     306              :    */
     307              :   uint64_t order_serial;
     308              : 
     309              :   /**
     310              :    * Index of selected choice from ``choices`` array in the contract_terms.
     311              :    * Is -1 for orders without choices.
     312              :    */
     313              :   int16_t choice_index;
     314              : 
     315              :   /**
     316              :    * Total refunds granted for this payment. Only initialized
     317              :    * if @e refunded is set to true.
     318              :    */
     319              :   struct TALER_Amount refund_amount;
     320              : 
     321              :   /**
     322              :    * Exchange HTTP error code encountered while trying to determine wire transfer
     323              :    * details. #TALER_EC_NONE for no error encountered.
     324              :    */
     325              :   unsigned int exchange_hc;
     326              : 
     327              :   /**
     328              :    * Exchange error code encountered while trying to determine wire transfer
     329              :    * details. #TALER_EC_NONE for no error encountered.
     330              :    */
     331              :   enum TALER_ErrorCode exchange_ec;
     332              : 
     333              :   /**
     334              :    * Error code encountered while trying to determine wire transfer
     335              :    * details. #TALER_EC_NONE for no error encountered.
     336              :    */
     337              :   enum TALER_ErrorCode wire_ec;
     338              : 
     339              :   /**
     340              :    * Set to YES if refunded orders should be included when
     341              :    * doing repurchase detection.
     342              :    */
     343              :   enum TALER_EXCHANGE_YesNoAll allow_refunded_for_repurchase;
     344              : 
     345              :   /**
     346              :    * HTTP status to return with @e wire_ec, 0 if @e wire_ec is #TALER_EC_NONE.
     347              :    */
     348              :   unsigned int wire_hc;
     349              : 
     350              :   /**
     351              :    * Did we suspend @a connection and are thus in
     352              :    * the #gorc_head DLL (#GNUNET_YES). Set to
     353              :    * #GNUNET_NO if we are not suspended, and to
     354              :    * #GNUNET_SYSERR if we should close the connection
     355              :    * without a response due to shutdown.
     356              :    */
     357              :   enum GNUNET_GenericReturnValue suspended;
     358              : 
     359              :   /**
     360              :    * Set to true if this payment has been refunded and
     361              :    * @e refund_amount is initialized.
     362              :    */
     363              :   bool refunded;
     364              : 
     365              :   /**
     366              :    * True if the order was paid.
     367              :    */
     368              :   bool paid;
     369              : 
     370              :   /**
     371              :    * True if the paid session in the database matches
     372              :    * our @e session_id.
     373              :    */
     374              :   bool paid_session_matches;
     375              : 
     376              :   /**
     377              :    * True if the exchange wired the money to the merchant.
     378              :    */
     379              :   bool wired;
     380              : 
     381              :   /**
     382              :    * True if the order remains unclaimed.
     383              :    */
     384              :   bool order_only;
     385              : 
     386              :   /**
     387              :    * Set to true if this payment has been refunded and
     388              :    * some refunds remain to be picked up by the wallet.
     389              :    */
     390              :   bool refund_pending;
     391              : 
     392              :   /**
     393              :    * Set to true if our database (incorrectly) has refunds
     394              :    * in a different currency than the currency of the
     395              :    * original payment for the order.
     396              :    */
     397              :   bool refund_currency_mismatch;
     398              : 
     399              :   /**
     400              :    * Set to true if our database (incorrectly) has deposits
     401              :    * in a different currency than the currency of the
     402              :    * original payment for the order.
     403              :    */
     404              :   bool deposit_currency_mismatch;
     405              : };
     406              : 
     407              : 
     408              : /**
     409              :  * Head of list of suspended requests waiting on the exchange.
     410              :  */
     411              : static struct GetOrderRequestContext *gorc_head;
     412              : 
     413              : /**
     414              :  * Tail of list of suspended requests waiting on the exchange.
     415              :  */
     416              : static struct GetOrderRequestContext *gorc_tail;
     417              : 
     418              : 
     419              : void
     420           15 : TMH_force_gorc_resume (void)
     421              : {
     422              :   struct GetOrderRequestContext *gorc;
     423              : 
     424           15 :   while (NULL != (gorc = gorc_head))
     425              :   {
     426            0 :     GNUNET_CONTAINER_DLL_remove (gorc_head,
     427              :                                  gorc_tail,
     428              :                                  gorc);
     429            0 :     GNUNET_assert (GNUNET_YES == gorc->suspended);
     430            0 :     gorc->suspended = GNUNET_SYSERR;
     431            0 :     MHD_resume_connection (gorc->sc.con);
     432              :   }
     433           15 : }
     434              : 
     435              : 
     436              : /**
     437              :  * We have received a trigger from the database
     438              :  * that we should (possibly) resume the request.
     439              :  *
     440              :  * @param cls a `struct GetOrderRequestContext` to resume
     441              :  * @param extra string encoding refund amount (or NULL)
     442              :  * @param extra_size number of bytes in @a extra
     443              :  */
     444              : static void
     445            2 : resume_by_event (void *cls,
     446              :                  const void *extra,
     447              :                  size_t extra_size)
     448              : {
     449            2 :   struct GetOrderRequestContext *gorc = cls;
     450              : 
     451              :   (void) extra;
     452              :   (void) extra_size;
     453            2 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     454              :               "Resuming request for order %s by trigger\n",
     455              :               gorc->hc->infix);
     456            2 :   if (GNUNET_NO == gorc->suspended)
     457            0 :     return; /* duplicate event is possible */
     458            2 :   gorc->suspended = GNUNET_NO;
     459            2 :   gorc->phase = GOP_FETCH_CONTRACT;
     460            2 :   GNUNET_CONTAINER_DLL_remove (gorc_head,
     461              :                                gorc_tail,
     462              :                                gorc);
     463            2 :   MHD_resume_connection (gorc->sc.con);
     464            2 :   TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     465              : }
     466              : 
     467              : 
     468              : /**
     469              :  * Clean up the session state for a GET /private/order/ID request.
     470              :  *
     471              :  * @param cls closure, must be a `struct GetOrderRequestContext *`
     472              :  */
     473              : static void
     474           62 : gorc_cleanup (void *cls)
     475              : {
     476           62 :   struct GetOrderRequestContext *gorc = cls;
     477              :   struct TransferQuery *tq;
     478              : 
     479           98 :   while (NULL != (tq = gorc->tq_head))
     480              :   {
     481           36 :     GNUNET_CONTAINER_DLL_remove (gorc->tq_head,
     482              :                                  gorc->tq_tail,
     483              :                                  tq);
     484           36 :     GNUNET_free (tq->exchange_url);
     485           36 :     GNUNET_free (tq);
     486              :   }
     487              : 
     488           62 :   if (NULL != gorc->contract_terms_json)
     489           62 :     json_decref (gorc->contract_terms_json);
     490           62 :   if (NULL != gorc->contract_terms)
     491              :   {
     492           62 :     TALER_MERCHANT_contract_free (gorc->contract_terms);
     493           62 :     gorc->contract_terms = NULL;
     494              :   }
     495           62 :   if (NULL != gorc->wire_details)
     496           26 :     json_decref (gorc->wire_details);
     497           62 :   if (NULL != gorc->refund_details)
     498           26 :     json_decref (gorc->refund_details);
     499           62 :   if (NULL != gorc->tt)
     500              :   {
     501            0 :     GNUNET_SCHEDULER_cancel (gorc->tt);
     502            0 :     gorc->tt = NULL;
     503              :   }
     504           62 :   if (NULL != gorc->eh)
     505              :   {
     506            4 :     TMH_db->event_listen_cancel (gorc->eh);
     507            4 :     gorc->eh = NULL;
     508              :   }
     509           62 :   if (NULL != gorc->session_eh)
     510              :   {
     511            0 :     TMH_db->event_listen_cancel (gorc->session_eh);
     512            0 :     gorc->session_eh = NULL;
     513              :   }
     514           62 :   GNUNET_free (gorc);
     515           62 : }
     516              : 
     517              : 
     518              : /**
     519              :  * Processing the request @a gorc is finished, set the
     520              :  * final return value in phase based on @a mret.
     521              :  *
     522              :  * @param[in,out] gorc order context to initialize
     523              :  * @param mret MHD HTTP response status to return
     524              :  */
     525              : static void
     526           62 : phase_end (struct GetOrderRequestContext *gorc,
     527              :            MHD_RESULT mret)
     528              : {
     529           62 :   gorc->phase = (MHD_YES == mret)
     530              :     ? GOP_END_YES
     531           62 :     : GOP_END_NO;
     532           62 : }
     533              : 
     534              : 
     535              : /**
     536              :  * Initialize event callbacks for the order processing.
     537              :  *
     538              :  * @param[in,out] gorc order context to initialize
     539              :  */
     540              : static void
     541           62 : phase_init (struct GetOrderRequestContext *gorc)
     542              : {
     543           62 :   struct TMH_HandlerContext *hc = gorc->hc;
     544           62 :   struct TMH_OrderPayEventP pay_eh = {
     545           62 :     .header.size = htons (sizeof (pay_eh)),
     546           62 :     .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_PAID),
     547           62 :     .merchant_pub = hc->instance->merchant_pub
     548              :   };
     549              : 
     550           62 :   if (! GNUNET_TIME_absolute_is_future (gorc->sc.long_poll_timeout))
     551              :   {
     552           58 :     gorc->phase++;
     553           58 :     return;
     554              :   }
     555              : 
     556            4 :   GNUNET_CRYPTO_hash (hc->infix,
     557            4 :                       strlen (hc->infix),
     558              :                       &pay_eh.h_order_id);
     559            4 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     560              :               "Subscribing to payment triggers for %p\n",
     561              :               gorc);
     562            4 :   gorc->eh = TMH_db->event_listen (
     563            4 :     TMH_db->cls,
     564              :     &pay_eh.header,
     565              :     GNUNET_TIME_absolute_get_remaining (gorc->sc.long_poll_timeout),
     566              :     &resume_by_event,
     567              :     gorc);
     568            4 :   if ( (NULL != gorc->session_id) &&
     569            0 :        (NULL != gorc->contract_terms->fulfillment_url) )
     570              :   {
     571            0 :     struct TMH_SessionEventP session_eh = {
     572            0 :       .header.size = htons (sizeof (session_eh)),
     573            0 :       .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED),
     574            0 :       .merchant_pub = hc->instance->merchant_pub
     575              :     };
     576              : 
     577            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     578              :                 "Subscribing to session triggers for %p\n",
     579              :                 gorc);
     580            0 :     GNUNET_CRYPTO_hash (gorc->session_id,
     581              :                         strlen (gorc->session_id),
     582              :                         &session_eh.h_session_id);
     583            0 :     GNUNET_CRYPTO_hash (gorc->contract_terms->fulfillment_url,
     584            0 :                         strlen (gorc->contract_terms->fulfillment_url),
     585              :                         &session_eh.h_fulfillment_url);
     586              :     gorc->session_eh
     587            0 :       = TMH_db->event_listen (
     588            0 :           TMH_db->cls,
     589              :           &session_eh.header,
     590              :           GNUNET_TIME_absolute_get_remaining (gorc->sc.long_poll_timeout),
     591              :           &resume_by_event,
     592              :           gorc);
     593              :   }
     594            4 :   gorc->phase++;
     595              : }
     596              : 
     597              : 
     598              : /**
     599              :  * Obtain latest contract terms from the database.
     600              :  *
     601              :  * @param[in,out] gorc order context to update
     602              :  */
     603              : static void
     604           64 : phase_fetch_contract (struct GetOrderRequestContext *gorc)
     605              : {
     606           64 :   struct TMH_HandlerContext *hc = gorc->hc;
     607              :   enum GNUNET_DB_QueryStatus qs;
     608              : 
     609           64 :   if (NULL != gorc->contract_terms_json)
     610              :   {
     611              :     /* Free memory filled with old contract terms before fetching the latest
     612              :        ones from the DB.  Note that we cannot simply skip the database
     613              :        interaction as the contract terms loaded previously might be from an
     614              :        earlier *unclaimed* order state (which we loaded in a previous
     615              :        invocation of this function and we are back here due to long polling)
     616              :        and thus the contract terms could have changed during claiming. Thus,
     617              :        we need to fetch the latest contract terms from the DB again. */
     618            2 :     json_decref (gorc->contract_terms_json);
     619            2 :     gorc->contract_terms_json = NULL;
     620            2 :     gorc->order_only = false;
     621              :   }
     622           64 :   TMH_db->preflight (TMH_db->cls);
     623           64 :   qs = TMH_db->lookup_contract_terms3 (TMH_db->cls,
     624           64 :                                        hc->instance->settings.id,
     625           64 :                                        hc->infix,
     626              :                                        gorc->session_id,
     627              :                                        &gorc->contract_terms_json,
     628              :                                        &gorc->order_serial,
     629              :                                        &gorc->paid,
     630              :                                        &gorc->wired,
     631              :                                        &gorc->paid_session_matches,
     632              :                                        &gorc->claim_token,
     633              :                                        &gorc->choice_index);
     634           64 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     635              :               "lookup_contract_terms (%s) returned %d\n",
     636              :               hc->infix,
     637              :               (int) qs);
     638           64 :   if (0 > qs)
     639              :   {
     640              :     /* single, read-only SQL statements should never cause
     641              :        serialization problems */
     642            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
     643              :     /* Always report on hard error as well to enable diagnostics */
     644            0 :     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
     645            0 :     phase_end (gorc,
     646              :                TALER_MHD_reply_with_error (gorc->sc.con,
     647              :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     648              :                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
     649              :                                            "contract terms"));
     650            0 :     return;
     651              :   }
     652           64 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     653              :   {
     654           52 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     655              :                 "Order %s is %s (%s) according to database\n",
     656              :                 hc->infix,
     657              :                 gorc->paid ? "paid" : "unpaid",
     658              :                 gorc->wired ? "wired" : "unwired");
     659           52 :     gorc->phase++;
     660           52 :     return;
     661              :   }
     662           12 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
     663           12 :   GNUNET_assert (! gorc->paid);
     664              :   /* No contract, only order, fetch from orders table */
     665           12 :   gorc->order_only = true;
     666              :   {
     667              :     struct TALER_MerchantPostDataHashP unused;
     668              : 
     669              :     /* We need the order for two cases:  Either when the contract doesn't exist yet,
     670              :      * or when the order is claimed but unpaid, and we need the claim token. */
     671           12 :     qs = TMH_db->lookup_order (TMH_db->cls,
     672           12 :                                hc->instance->settings.id,
     673           12 :                                hc->infix,
     674              :                                &gorc->claim_token,
     675              :                                &unused,
     676              :                                &gorc->contract_terms_json);
     677              :   }
     678           12 :   if (0 > qs)
     679              :   {
     680              :     /* single, read-only SQL statements should never cause
     681              :        serialization problems */
     682            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
     683              :     /* Always report on hard error as well to enable diagnostics */
     684            0 :     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
     685            0 :     phase_end (gorc,
     686              :                TALER_MHD_reply_with_error (gorc->sc.con,
     687              :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     688              :                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
     689              :                                            "order"));
     690            0 :     return;
     691              :   }
     692           12 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     693              :   {
     694            0 :     phase_end (gorc,
     695              :                TALER_MHD_reply_with_error (gorc->sc.con,
     696              :                                            MHD_HTTP_NOT_FOUND,
     697              :                                            TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
     698            0 :                                            hc->infix));
     699            0 :     return;
     700              :   }
     701           12 :   gorc->phase++;
     702              : }
     703              : 
     704              : 
     705              : /**
     706              :  * Obtain parse contract terms of the order.  Extracts the fulfillment URL,
     707              :  * total amount, summary and timestamp from the contract terms!
     708              :  *
     709              :  * @param[in,out] gorc order context to update
     710              :  */
     711              : static void
     712           64 : phase_parse_contract (struct GetOrderRequestContext *gorc)
     713              : {
     714           64 :   struct TMH_HandlerContext *hc = gorc->hc;
     715              : 
     716           64 :   if (NULL == gorc->contract_terms)
     717              :   {
     718           62 :     gorc->contract_terms = TALER_MERCHANT_contract_parse (
     719              :       gorc->contract_terms_json,
     720              :       true);
     721              : 
     722           62 :     if (NULL == gorc->contract_terms)
     723              :     {
     724            0 :       GNUNET_break (0);
     725            0 :       phase_end (gorc,
     726              :                  TALER_MHD_reply_with_error (
     727              :                    gorc->sc.con,
     728              :                    MHD_HTTP_INTERNAL_SERVER_ERROR,
     729              :                    TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
     730            0 :                    hc->infix));
     731            0 :       return;
     732              :     }
     733              :   }
     734              : 
     735           64 :   switch (gorc->contract_terms->version)
     736              :   {
     737           64 :   case TALER_MERCHANT_CONTRACT_VERSION_0:
     738           64 :     gorc->contract_amount = gorc->contract_terms->details.v0.brutto;
     739           64 :     break;
     740            0 :   case TALER_MERCHANT_CONTRACT_VERSION_1:
     741            0 :     if (gorc->choice_index >= 0)
     742              :     {
     743            0 :       if (gorc->choice_index >=
     744            0 :           gorc->contract_terms->details.v1.choices_len)
     745              :       {
     746            0 :         GNUNET_break (0);
     747            0 :         phase_end (gorc,
     748              :                    TALER_MHD_reply_with_error (
     749              :                      gorc->sc.con, MHD_HTTP_INTERNAL_SERVER_ERROR,
     750              :                      TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
     751              :                      NULL));
     752            0 :         return;
     753              :       }
     754              : 
     755            0 :       gorc->contract_amount =
     756            0 :         gorc->contract_terms->details.v1.choices[gorc->choice_index].amount;
     757              :     }
     758              :     else
     759              :     {
     760            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     761              :                   "choice index %i for order %s is invalid or not yet available",
     762              :                   gorc->choice_index,
     763              :                   gorc->contract_terms->order_id);
     764              :     }
     765            0 :     break;
     766            0 :   default:
     767              :     {
     768            0 :       GNUNET_break (0);
     769            0 :       phase_end (gorc,
     770              :                  TALER_MHD_reply_with_error (
     771              :                    gorc->sc.con,
     772              :                    MHD_HTTP_INTERNAL_SERVER_ERROR,
     773              :                    TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_VERSION,
     774              :                    NULL));
     775            0 :       return;
     776              :     }
     777              :   }
     778              : 
     779          116 :   if (! gorc->order_only && GNUNET_OK !=
     780           52 :       TALER_JSON_contract_hash (gorc->contract_terms_json,
     781              :                                 &gorc->h_contract_terms))
     782              :   {
     783              :     {
     784            0 :       GNUNET_break (0);
     785            0 :       phase_end (gorc,
     786              :                  TALER_MHD_reply_with_error (gorc->sc.con,
     787              :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     788              :                                              TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
     789              :                                              NULL));
     790            0 :       return;
     791              :     }
     792              :   }
     793           64 :   GNUNET_assert (NULL != gorc->contract_terms_json);
     794           64 :   GNUNET_assert (NULL != gorc->contract_terms);
     795           64 :   gorc->phase++;
     796              : }
     797              : 
     798              : 
     799              : /**
     800              :  * Check payment status of the order.
     801              :  *
     802              :  * @param[in,out] gorc order context to update
     803              :  */
     804              : static void
     805           64 : phase_check_paid (struct GetOrderRequestContext *gorc)
     806              : {
     807           64 :   struct TMH_HandlerContext *hc = gorc->hc;
     808              : 
     809           64 :   if (gorc->order_only)
     810              :   {
     811           12 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     812              :                 "Order %s unclaimed, no need to lookup payment status\n",
     813              :                 hc->infix);
     814           12 :     GNUNET_assert (! gorc->paid);
     815           12 :     GNUNET_assert (! gorc->wired);
     816           12 :     gorc->phase++;
     817           12 :     return;
     818              :   }
     819           52 :   if (NULL == gorc->session_id)
     820              :   {
     821           42 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     822              :                 "No session ID, do not need to lookup session-ID specific payment status (%s/%s)\n",
     823              :                 gorc->paid ? "paid" : "unpaid",
     824              :                 gorc->wired ? "wired" : "unwired");
     825           42 :     gorc->phase++;
     826           42 :     return;
     827              :   }
     828           10 :   if (! gorc->paid_session_matches)
     829              :   {
     830            8 :     gorc->paid = false;
     831            8 :     gorc->wired = false;
     832              :   }
     833           10 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     834              :               "Order %s %s for session %s (%s)\n",
     835              :               hc->infix,
     836              :               gorc->paid ? "paid" : "unpaid",
     837              :               gorc->session_id,
     838              :               gorc->wired ? "wired" : "unwired");
     839           10 :   gorc->phase++;
     840              : }
     841              : 
     842              : 
     843              : /**
     844              :  * Check if re-purchase detection applies to the order.
     845              :  *
     846              :  * @param[in,out] gorc order context to update
     847              :  */
     848              : static void
     849           64 : phase_check_repurchase (struct GetOrderRequestContext *gorc)
     850              : {
     851           64 :   struct TMH_HandlerContext *hc = gorc->hc;
     852           64 :   char *already_paid_order_id = NULL;
     853              :   enum GNUNET_DB_QueryStatus qs;
     854              :   char *taler_pay_uri;
     855              :   char *order_status_url;
     856              :   MHD_RESULT ret;
     857              : 
     858           64 :   if ( (gorc->paid) ||
     859           28 :        (NULL == gorc->contract_terms->fulfillment_url) ||
     860           17 :        (NULL == gorc->session_id) )
     861              :   {
     862              :     /* Repurchase cannot apply */
     863           56 :     gorc->phase++;
     864           60 :     return;
     865              :   }
     866            8 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     867              :               "Running re-purchase detection for %s/%s\n",
     868              :               gorc->session_id,
     869              :               gorc->contract_terms->fulfillment_url);
     870            8 :   qs = TMH_db->lookup_order_by_fulfillment (TMH_db->cls,
     871            8 :                                             hc->instance->settings.id,
     872            8 :                                             gorc->contract_terms->
     873              :                                             fulfillment_url,
     874              :                                             gorc->session_id,
     875              :                                             TALER_EXCHANGE_YNA_NO !=
     876            8 :                                             gorc->allow_refunded_for_repurchase,
     877              :                                             &already_paid_order_id);
     878            8 :   if (0 > qs)
     879              :   {
     880              :     /* single, read-only SQL statements should never cause
     881              :        serialization problems, and the entry should exist as per above */
     882            0 :     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
     883            0 :     phase_end (gorc,
     884              :                TALER_MHD_reply_with_error (gorc->sc.con,
     885              :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     886              :                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
     887              :                                            "order by fulfillment"));
     888            0 :     return;
     889              :   }
     890            8 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     891              :   {
     892            4 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     893              :                 "No already paid order for %s/%s\n",
     894              :                 gorc->session_id,
     895              :                 gorc->contract_terms->fulfillment_url);
     896            4 :     gorc->phase++;
     897            4 :     return;
     898              :   }
     899              : 
     900              :   /* User did pay for this order, but under a different session; ask wallet to
     901              :      switch order ID */
     902            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     903              :               "Found already paid order %s\n",
     904              :               already_paid_order_id);
     905            4 :   taler_pay_uri = TMH_make_taler_pay_uri (gorc->sc.con,
     906            4 :                                           hc->infix,
     907              :                                           gorc->session_id,
     908            4 :                                           hc->instance->settings.id,
     909              :                                           &gorc->claim_token);
     910            4 :   order_status_url = TMH_make_order_status_url (gorc->sc.con,
     911            4 :                                                 hc->infix,
     912              :                                                 gorc->session_id,
     913            4 :                                                 hc->instance->settings.id,
     914              :                                                 &gorc->claim_token,
     915              :                                                 NULL);
     916            4 :   if ( (NULL == taler_pay_uri) ||
     917              :        (NULL == order_status_url) )
     918              :   {
     919            0 :     GNUNET_break_op (0);
     920            0 :     GNUNET_free (taler_pay_uri);
     921            0 :     GNUNET_free (order_status_url);
     922            0 :     phase_end (gorc,
     923              :                TALER_MHD_reply_with_error (gorc->sc.con,
     924              :                                            MHD_HTTP_BAD_REQUEST,
     925              :                                            TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
     926              :                                            "host"));
     927            0 :     return;
     928              :   }
     929            4 :   ret = TALER_MHD_REPLY_JSON_PACK (
     930              :     gorc->sc.con,
     931              :     MHD_HTTP_OK,
     932              :     GNUNET_JSON_pack_string ("taler_pay_uri",
     933              :                              taler_pay_uri),
     934              :     GNUNET_JSON_pack_string ("order_status_url",
     935              :                              order_status_url),
     936              :     GNUNET_JSON_pack_string ("order_status",
     937              :                              "unpaid"),
     938              :     GNUNET_JSON_pack_string ("already_paid_order_id",
     939              :                              already_paid_order_id),
     940              :     GNUNET_JSON_pack_string ("already_paid_fulfillment_url",
     941              :                              gorc->contract_terms->fulfillment_url),
     942              :     /* undefined for unpaid v1 contracts */
     943              :     GNUNET_JSON_pack_allow_null (
     944              :       TALER_JSON_pack_amount ("total_amount",
     945              :                               &gorc->contract_amount)),
     946              :     GNUNET_JSON_pack_string ("summary",
     947              :                              gorc->contract_terms->summary),
     948              :     GNUNET_JSON_pack_timestamp ("pay_deadline",
     949              :                                 gorc->contract_terms->pay_deadline),
     950              :     GNUNET_JSON_pack_timestamp ("creation_time",
     951              :                                 gorc->contract_terms->timestamp));
     952            4 :   GNUNET_free (order_status_url);
     953            4 :   GNUNET_free (taler_pay_uri);
     954            4 :   GNUNET_free (already_paid_order_id);
     955            4 :   phase_end (gorc,
     956              :              ret);
     957              : }
     958              : 
     959              : 
     960              : /**
     961              :  * Check if we should suspend until the order is paid.
     962              :  *
     963              :  * @param[in,out] gorc order context to update
     964              :  */
     965              : static void
     966           60 : phase_unpaid_finish (struct GetOrderRequestContext *gorc)
     967              : {
     968           60 :   struct TMH_HandlerContext *hc = gorc->hc;
     969              :   char *taler_pay_uri;
     970              :   char *order_status_url;
     971              :   MHD_RESULT ret;
     972              : 
     973           60 :   if (gorc->paid)
     974              :   {
     975           36 :     gorc->phase++;
     976           48 :     return;
     977              :   }
     978              :   /* User never paid for this order, suspend waiting
     979              :      on payment or return details. */
     980           24 :   if (GNUNET_TIME_absolute_is_future (gorc->sc.long_poll_timeout))
     981              :   {
     982            2 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     983              :                 "Suspending GET /private/orders/%s\n",
     984              :                 hc->infix);
     985            2 :     GNUNET_CONTAINER_DLL_insert (gorc_head,
     986              :                                  gorc_tail,
     987              :                                  gorc);
     988            2 :     gorc->phase = GOP_SUSPENDED_ON_UNPAID;
     989            2 :     gorc->suspended = GNUNET_YES;
     990            2 :     MHD_suspend_connection (gorc->sc.con);
     991            2 :     return;
     992              :   }
     993           22 :   order_status_url = TMH_make_order_status_url (gorc->sc.con,
     994           22 :                                                 hc->infix,
     995              :                                                 gorc->session_id,
     996           22 :                                                 hc->instance->settings.id,
     997              :                                                 &gorc->claim_token,
     998              :                                                 NULL);
     999           22 :   if (! gorc->order_only)
    1000              :   {
    1001           10 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1002              :                 "Order %s claimed but not paid yet\n",
    1003              :                 hc->infix);
    1004           10 :     phase_end (gorc,
    1005           10 :                TALER_MHD_REPLY_JSON_PACK (
    1006              :                  gorc->sc.con,
    1007              :                  MHD_HTTP_OK,
    1008              :                  GNUNET_JSON_pack_string ("order_status_url",
    1009              :                                           order_status_url),
    1010              :                  GNUNET_JSON_pack_object_incref ("contract_terms",
    1011              :                                                  gorc->contract_terms_json),
    1012              :                  GNUNET_JSON_pack_string ("order_status",
    1013              :                                           "claimed")));
    1014           10 :     GNUNET_free (order_status_url);
    1015           10 :     return;
    1016              :   }
    1017           12 :   taler_pay_uri = TMH_make_taler_pay_uri (gorc->sc.con,
    1018           12 :                                           hc->infix,
    1019              :                                           gorc->session_id,
    1020           12 :                                           hc->instance->settings.id,
    1021              :                                           &gorc->claim_token);
    1022           12 :   ret = TALER_MHD_REPLY_JSON_PACK (
    1023              :     gorc->sc.con,
    1024              :     MHD_HTTP_OK,
    1025              :     GNUNET_JSON_pack_string ("taler_pay_uri",
    1026              :                              taler_pay_uri),
    1027              :     GNUNET_JSON_pack_string ("order_status_url",
    1028              :                              order_status_url),
    1029              :     GNUNET_JSON_pack_string ("order_status",
    1030              :                              "unpaid"),
    1031              :     /* undefined for unpaid v1 contracts */
    1032              :     GNUNET_JSON_pack_allow_null (
    1033              :       TALER_JSON_pack_amount ("total_amount",
    1034              :                               &gorc->contract_amount)),
    1035              :     GNUNET_JSON_pack_string ("summary",
    1036              :                              gorc->contract_terms->summary),
    1037              :     GNUNET_JSON_pack_timestamp ("creation_time",
    1038              :                                 gorc->contract_terms->timestamp));
    1039           12 :   GNUNET_free (taler_pay_uri);
    1040           12 :   GNUNET_free (order_status_url);
    1041           12 :   phase_end (gorc,
    1042              :              ret);
    1043              : 
    1044              : }
    1045              : 
    1046              : 
    1047              : /**
    1048              :  * Function called with information about a refund.
    1049              :  * It is responsible for summing up the refund amount.
    1050              :  *
    1051              :  * @param cls closure
    1052              :  * @param refund_serial unique serial number of the refund
    1053              :  * @param timestamp time of the refund (for grouping of refunds in the wallet UI)
    1054              :  * @param coin_pub public coin from which the refund comes from
    1055              :  * @param exchange_url URL of the exchange that issued @a coin_pub
    1056              :  * @param rtransaction_id identificator of the refund
    1057              :  * @param reason human-readable explanation of the refund
    1058              :  * @param refund_amount refund amount which is being taken from @a coin_pub
    1059              :  * @param pending true if the this refund was not yet processed by the wallet/exchange
    1060              :  */
    1061              : static void
    1062            8 : process_refunds_cb (
    1063              :   void *cls,
    1064              :   uint64_t refund_serial,
    1065              :   struct GNUNET_TIME_Timestamp timestamp,
    1066              :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1067              :   const char *exchange_url,
    1068              :   uint64_t rtransaction_id,
    1069              :   const char *reason,
    1070              :   const struct TALER_Amount *refund_amount,
    1071              :   bool pending)
    1072              : {
    1073            8 :   struct GetOrderRequestContext *gorc = cls;
    1074              : 
    1075            8 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1076              :               "Found refund %llu over %s for reason %s\n",
    1077              :               (unsigned long long) rtransaction_id,
    1078              :               TALER_amount2s (refund_amount),
    1079              :               reason);
    1080            8 :   GNUNET_assert (
    1081              :     0 ==
    1082              :     json_array_append_new (
    1083              :       gorc->refund_details,
    1084              :       GNUNET_JSON_PACK (
    1085              :         TALER_JSON_pack_amount ("amount",
    1086              :                                 refund_amount),
    1087              :         GNUNET_JSON_pack_bool ("pending",
    1088              :                                pending),
    1089              :         GNUNET_JSON_pack_timestamp ("timestamp",
    1090              :                                     timestamp),
    1091              :         GNUNET_JSON_pack_string ("reason",
    1092              :                                  reason))));
    1093              :   /* For refunded coins, we are not charged deposit fees, so subtract those
    1094              :      again */
    1095            8 :   for (struct TransferQuery *tq = gorc->tq_head;
    1096            8 :        NULL != tq;
    1097            0 :        tq = tq->next)
    1098              :   {
    1099            0 :     if (0 !=
    1100            0 :         strcmp (exchange_url,
    1101            0 :                 tq->exchange_url))
    1102            0 :       continue;
    1103            0 :     if (0 !=
    1104            0 :         GNUNET_memcmp (&tq->coin_pub,
    1105              :                        coin_pub))
    1106            0 :       continue;
    1107            0 :     if (GNUNET_OK !=
    1108            0 :         TALER_amount_cmp_currency (
    1109            0 :           &gorc->deposit_fees_total,
    1110            0 :           &tq->deposit_fee))
    1111              :     {
    1112            0 :       gorc->refund_currency_mismatch = true;
    1113            0 :       return;
    1114              :     }
    1115            0 :     GNUNET_assert (
    1116              :       0 <=
    1117              :       TALER_amount_subtract (&gorc->deposit_fees_total,
    1118              :                              &gorc->deposit_fees_total,
    1119              :                              &tq->deposit_fee));
    1120              :   }
    1121            8 :   if (GNUNET_OK !=
    1122            8 :       TALER_amount_cmp_currency (
    1123            8 :         &gorc->refund_amount,
    1124              :         refund_amount))
    1125              :   {
    1126            0 :     gorc->refund_currency_mismatch = true;
    1127            0 :     return;
    1128              :   }
    1129            8 :   GNUNET_assert (0 <=
    1130              :                  TALER_amount_add (&gorc->refund_amount,
    1131              :                                    &gorc->refund_amount,
    1132              :                                    refund_amount));
    1133            8 :   gorc->refunded = true;
    1134            8 :   gorc->refund_pending |= pending;
    1135              : }
    1136              : 
    1137              : 
    1138              : /**
    1139              :  * Check refund status for the order.
    1140              :  *
    1141              :  * @param[in,out] gorc order context to update
    1142              :  */
    1143              : static void
    1144           36 : phase_check_refunds (struct GetOrderRequestContext *gorc)
    1145              : {
    1146           36 :   struct TMH_HandlerContext *hc = gorc->hc;
    1147              :   enum GNUNET_DB_QueryStatus qs;
    1148              : 
    1149           36 :   GNUNET_assert (! gorc->order_only);
    1150           36 :   GNUNET_assert (gorc->paid);
    1151              : 
    1152              :   /* Accumulate refunds, if any. */
    1153           36 :   GNUNET_assert (GNUNET_OK ==
    1154              :                  TALER_amount_set_zero (gorc->contract_amount.currency,
    1155              :                                         &gorc->refund_amount));
    1156              : 
    1157           36 :   qs = TMH_db->lookup_refunds_detailed (
    1158           36 :     TMH_db->cls,
    1159           36 :     hc->instance->settings.id,
    1160           36 :     &gorc->h_contract_terms,
    1161              :     &process_refunds_cb,
    1162              :     gorc);
    1163           36 :   if (0 > qs)
    1164              :   {
    1165            0 :     GNUNET_break (0);
    1166            0 :     phase_end (gorc,
    1167              :                TALER_MHD_reply_with_error (
    1168              :                  gorc->sc.con,
    1169              :                  MHD_HTTP_INTERNAL_SERVER_ERROR,
    1170              :                  TALER_EC_GENERIC_DB_FETCH_FAILED,
    1171              :                  "detailed refunds"));
    1172            0 :     return;
    1173              :   }
    1174           36 :   if (gorc->refund_currency_mismatch)
    1175              :   {
    1176            0 :     GNUNET_break (0);
    1177            0 :     phase_end (gorc,
    1178              :                TALER_MHD_reply_with_error (
    1179              :                  gorc->sc.con,
    1180              :                  MHD_HTTP_INTERNAL_SERVER_ERROR,
    1181              :                  TALER_EC_GENERIC_DB_FETCH_FAILED,
    1182              :                  "refunds in different currency than original order price"));
    1183            0 :     return;
    1184              :   }
    1185           36 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1186              :               "Total refunds are %s\n",
    1187              :               TALER_amount2s (&gorc->refund_amount));
    1188           36 :   gorc->phase++;
    1189              : }
    1190              : 
    1191              : 
    1192              : /**
    1193              :  * Function called with each @a coin_pub that was deposited into the
    1194              :  * @a h_wire account of the merchant for the @a deposit_serial as part
    1195              :  * of the payment for the order identified by @a cls.
    1196              :  *
    1197              :  * Queries the exchange for the payment status associated with the
    1198              :  * given coin.
    1199              :  *
    1200              :  * @param cls a `struct GetOrderRequestContext`
    1201              :  * @param deposit_serial identifies the deposit operation
    1202              :  * @param exchange_url URL of the exchange that issued @a coin_pub
    1203              :  * @param h_wire hash of the merchant's wire account into which the deposit was made
    1204              :  * @param deposit_timestamp when was the deposit made
    1205              :  * @param amount_with_fee amount the exchange will deposit for this coin
    1206              :  * @param deposit_fee fee the exchange will charge for this coin
    1207              :  * @param coin_pub public key of the deposited coin
    1208              :  */
    1209              : static void
    1210           36 : deposit_cb (
    1211              :   void *cls,
    1212              :   uint64_t deposit_serial,
    1213              :   const char *exchange_url,
    1214              :   const struct TALER_MerchantWireHashP *h_wire,
    1215              :   struct GNUNET_TIME_Timestamp deposit_timestamp,
    1216              :   const struct TALER_Amount *amount_with_fee,
    1217              :   const struct TALER_Amount *deposit_fee,
    1218              :   const struct TALER_CoinSpendPublicKeyP *coin_pub)
    1219              : {
    1220           36 :   struct GetOrderRequestContext *gorc = cls;
    1221              :   struct TransferQuery *tq;
    1222              : 
    1223           36 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1224              :               "Checking deposit status for coin %s (over %s)\n",
    1225              :               TALER_B2S (coin_pub),
    1226              :               TALER_amount2s (amount_with_fee));
    1227              :   gorc->last_payment
    1228           36 :     = GNUNET_TIME_timestamp_max (gorc->last_payment,
    1229              :                                  deposit_timestamp);
    1230           36 :   tq = GNUNET_new (struct TransferQuery);
    1231           36 :   tq->gorc = gorc;
    1232           36 :   tq->exchange_url = GNUNET_strdup (exchange_url);
    1233           36 :   tq->deposit_serial = deposit_serial;
    1234           36 :   GNUNET_CONTAINER_DLL_insert (gorc->tq_head,
    1235              :                                gorc->tq_tail,
    1236              :                                tq);
    1237           36 :   tq->coin_pub = *coin_pub;
    1238           36 :   tq->h_wire = *h_wire;
    1239           36 :   tq->amount_with_fee = *amount_with_fee;
    1240           36 :   tq->deposit_fee = *deposit_fee;
    1241           36 : }
    1242              : 
    1243              : 
    1244              : /**
    1245              :  * Check wire transfer status for the order at the exchange.
    1246              :  *
    1247              :  * @param[in,out] gorc order context to update
    1248              :  */
    1249              : static void
    1250           36 : phase_check_deposits (struct GetOrderRequestContext *gorc)
    1251              : {
    1252           36 :   GNUNET_assert (! gorc->order_only);
    1253           36 :   GNUNET_assert (gorc->paid);
    1254              : 
    1255              :   /* amount must be always valid for paid orders */
    1256           36 :   GNUNET_assert (GNUNET_OK ==
    1257              :                  TALER_amount_is_valid (&gorc->contract_amount));
    1258              : 
    1259           36 :   GNUNET_assert (GNUNET_OK ==
    1260              :                  TALER_amount_set_zero (gorc->contract_amount.currency,
    1261              :                                         &gorc->deposits_total));
    1262           36 :   GNUNET_assert (GNUNET_OK ==
    1263              :                  TALER_amount_set_zero (gorc->contract_amount.currency,
    1264              :                                         &gorc->deposit_fees_total));
    1265           36 :   TMH_db->lookup_deposits_by_order (TMH_db->cls,
    1266              :                                     gorc->order_serial,
    1267              :                                     &deposit_cb,
    1268              :                                     gorc);
    1269           36 :   gorc->phase++;
    1270           36 : }
    1271              : 
    1272              : 
    1273              : /**
    1274              :  * Function called with available wire details, to be added to
    1275              :  * the response.
    1276              :  *
    1277              :  * @param cls a `struct GetOrderRequestContext`
    1278              :  * @param wtid wire transfer subject of the wire transfer for the coin
    1279              :  * @param exchange_url base URL of the exchange that made the payment
    1280              :  * @param execution_time when was the payment made
    1281              :  * @param deposit_value contribution of the coin to the total wire transfer value
    1282              :  * @param deposit_fee deposit fee charged by the exchange for the coin
    1283              :  * @param transfer_confirmed did the merchant confirm that a wire transfer with
    1284              :  *        @a wtid over the total amount happened?
    1285              :  */
    1286              : static void
    1287           14 : process_transfer_details (
    1288              :   void *cls,
    1289              :   const struct TALER_WireTransferIdentifierRawP *wtid,
    1290              :   const char *exchange_url,
    1291              :   struct GNUNET_TIME_Timestamp execution_time,
    1292              :   const struct TALER_Amount *deposit_value,
    1293              :   const struct TALER_Amount *deposit_fee,
    1294              :   bool transfer_confirmed)
    1295              : {
    1296           14 :   struct GetOrderRequestContext *gorc = cls;
    1297           14 :   json_t *wire_details = gorc->wire_details;
    1298              :   struct TALER_Amount wired;
    1299              : 
    1300           14 :   if ( (GNUNET_OK !=
    1301           14 :         TALER_amount_cmp_currency (&gorc->deposits_total,
    1302           14 :                                    deposit_value)) ||
    1303              :        (GNUNET_OK !=
    1304           14 :         TALER_amount_cmp_currency (&gorc->deposit_fees_total,
    1305              :                                    deposit_fee)) )
    1306              :   {
    1307            0 :     GNUNET_break (0);
    1308            0 :     gorc->deposit_currency_mismatch = true;
    1309            0 :     return;
    1310              :   }
    1311              : 
    1312              :   /* Compute total amount *wired* */
    1313           14 :   GNUNET_assert (0 <=
    1314              :                  TALER_amount_add (&gorc->deposits_total,
    1315              :                                    &gorc->deposits_total,
    1316              :                                    deposit_value));
    1317           14 :   GNUNET_assert (0 <=
    1318              :                  TALER_amount_add (&gorc->deposit_fees_total,
    1319              :                                    &gorc->deposit_fees_total,
    1320              :                                    deposit_fee));
    1321           14 :   GNUNET_assert (0 <= TALER_amount_subtract (&wired,
    1322              :                                              deposit_value,
    1323              :                                              deposit_fee));
    1324           14 :   GNUNET_assert (0 ==
    1325              :                  json_array_append_new (
    1326              :                    wire_details,
    1327              :                    GNUNET_JSON_PACK (
    1328              :                      GNUNET_JSON_pack_data_auto ("wtid",
    1329              :                                                  wtid),
    1330              :                      GNUNET_JSON_pack_string ("exchange_url",
    1331              :                                               exchange_url),
    1332              :                      TALER_JSON_pack_amount ("amount",
    1333              :                                              &wired),
    1334              :                      GNUNET_JSON_pack_timestamp ("execution_time",
    1335              :                                                  execution_time),
    1336              :                      GNUNET_JSON_pack_bool ("confirmed",
    1337              :                                             transfer_confirmed))));
    1338              : }
    1339              : 
    1340              : 
    1341              : /**
    1342              :  * Check transfer status in local database.
    1343              :  *
    1344              :  * @param[in,out] gorc order context to update
    1345              :  */
    1346              : static void
    1347           36 : phase_check_local_transfers (struct GetOrderRequestContext *gorc)
    1348              : {
    1349           36 :   struct TMH_HandlerContext *hc = gorc->hc;
    1350              :   enum GNUNET_DB_QueryStatus qs;
    1351              : 
    1352           36 :   GNUNET_assert (gorc->paid);
    1353           36 :   GNUNET_assert (! gorc->order_only);
    1354              : 
    1355           36 :   GNUNET_assert (GNUNET_OK ==
    1356              :                  TALER_amount_set_zero (gorc->contract_amount.currency,
    1357              :                                         &gorc->deposits_total));
    1358           36 :   GNUNET_assert (GNUNET_OK ==
    1359              :                  TALER_amount_set_zero (gorc->contract_amount.currency,
    1360              :                                         &gorc->deposit_fees_total));
    1361              : 
    1362           36 :   qs = TMH_db->lookup_transfer_details_by_order (TMH_db->cls,
    1363              :                                                  gorc->order_serial,
    1364              :                                                  &process_transfer_details,
    1365              :                                                  gorc);
    1366           36 :   if (0 > qs)
    1367              :   {
    1368            0 :     GNUNET_break (0);
    1369            0 :     phase_end (gorc,
    1370              :                TALER_MHD_reply_with_error (gorc->sc.con,
    1371              :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    1372              :                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
    1373              :                                            "transfer details"));
    1374            0 :     return;
    1375              :   }
    1376           36 :   if (gorc->deposit_currency_mismatch)
    1377              :   {
    1378            0 :     GNUNET_break (0);
    1379            0 :     phase_end (gorc,
    1380              :                TALER_MHD_reply_with_error (gorc->sc.con,
    1381              :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    1382              :                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
    1383              :                                            "deposits in different currency than original order price"));
    1384            0 :     return;
    1385              :   }
    1386              : 
    1387           36 :   if (! gorc->wired)
    1388              :   {
    1389              :     /* we believe(d) the wire transfer did not happen yet, check if maybe
    1390              :        in light of new evidence it did */
    1391              :     struct TALER_Amount expect_total;
    1392              : 
    1393           22 :     if (0 >
    1394           22 :         TALER_amount_subtract (&expect_total,
    1395           22 :                                &gorc->contract_amount,
    1396           22 :                                &gorc->refund_amount))
    1397              :     {
    1398            0 :       GNUNET_break (0);
    1399            0 :       phase_end (gorc,
    1400              :                  TALER_MHD_reply_with_error (
    1401              :                    gorc->sc.con,
    1402              :                    MHD_HTTP_INTERNAL_SERVER_ERROR,
    1403              :                    TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
    1404              :                    "refund exceeds contract value"));
    1405            0 :       return;
    1406              :     }
    1407           22 :     if (0 >
    1408           22 :         TALER_amount_subtract (&expect_total,
    1409              :                                &expect_total,
    1410           22 :                                &gorc->deposit_fees_total))
    1411              :     {
    1412            0 :       GNUNET_break (0);
    1413            0 :       phase_end (gorc,
    1414              :                  TALER_MHD_reply_with_error (
    1415              :                    gorc->sc.con,
    1416              :                    MHD_HTTP_INTERNAL_SERVER_ERROR,
    1417              :                    TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
    1418              :                    "deposit fees exceed total minus refunds"));
    1419            0 :       return;
    1420              :     }
    1421           22 :     if (0 >=
    1422           22 :         TALER_amount_cmp (&expect_total,
    1423           22 :                           &gorc->deposits_total))
    1424              :     {
    1425              :       /* expect_total <= gorc->deposits_total: good: we got the wire transfer */
    1426            0 :       gorc->wired = true;
    1427            0 :       qs = TMH_db->mark_order_wired (TMH_db->cls,
    1428              :                                      gorc->order_serial);
    1429            0 :       GNUNET_break (qs >= 0);   /* just warn if transaction failed */
    1430            0 :       TMH_notify_order_change (hc->instance,
    1431              :                                TMH_OSF_PAID
    1432              :                                | TMH_OSF_WIRED,
    1433            0 :                                gorc->contract_terms->timestamp,
    1434              :                                gorc->order_serial);
    1435              :     }
    1436              :   }
    1437           36 :   gorc->phase++;
    1438              : }
    1439              : 
    1440              : 
    1441              : /**
    1442              :  * Generate final result for the status request.
    1443              :  *
    1444              :  * @param[in,out] gorc order context to update
    1445              :  */
    1446              : static void
    1447           36 : phase_reply_result (struct GetOrderRequestContext *gorc)
    1448              : {
    1449           36 :   struct TMH_HandlerContext *hc = gorc->hc;
    1450              :   MHD_RESULT ret;
    1451              :   char *order_status_url;
    1452              : 
    1453           36 :   GNUNET_assert (gorc->paid);
    1454           36 :   GNUNET_assert (! gorc->order_only);
    1455              : 
    1456              :   {
    1457           36 :     struct TALER_PrivateContractHashP *h_contract = NULL;
    1458              : 
    1459              :     /* In a session-bound payment, allow the browser to check the order
    1460              :      * status page (e.g. to get a refund).
    1461              :      *
    1462              :      * Note that we don't allow this outside of session-based payment, as
    1463              :      * otherwise this becomes an oracle to convert order_id to h_contract.
    1464              :      */
    1465           36 :     if (NULL != gorc->session_id)
    1466            2 :       h_contract = &gorc->h_contract_terms;
    1467              : 
    1468              :     order_status_url =
    1469           36 :       TMH_make_order_status_url (gorc->sc.con,
    1470           36 :                                  hc->infix,
    1471              :                                  gorc->session_id,
    1472           36 :                                  hc->instance->settings.id,
    1473              :                                  &gorc->claim_token,
    1474              :                                  h_contract);
    1475              :   }
    1476           36 :   if (GNUNET_TIME_absolute_is_zero (gorc->last_payment.abs_time))
    1477              :   {
    1478            0 :     GNUNET_break (GNUNET_YES ==
    1479              :                   TALER_amount_is_zero (&gorc->contract_amount));
    1480            0 :     gorc->last_payment = gorc->contract_terms->timestamp;
    1481              :   }
    1482           36 :   ret = TALER_MHD_REPLY_JSON_PACK (
    1483              :     gorc->sc.con,
    1484              :     MHD_HTTP_OK,
    1485              :     // Deprecated in protocol v6.
    1486              :     GNUNET_JSON_pack_array_steal ("wire_reports",
    1487              :                                   json_array ()),
    1488              :     GNUNET_JSON_pack_uint64 ("exchange_code",
    1489              :                              gorc->exchange_ec),
    1490              :     GNUNET_JSON_pack_uint64 ("exchange_http_status",
    1491              :                              gorc->exchange_hc),
    1492              :     /* legacy: */
    1493              :     GNUNET_JSON_pack_uint64 ("exchange_ec",
    1494              :                              gorc->exchange_ec),
    1495              :     /* legacy: */
    1496              :     GNUNET_JSON_pack_uint64 ("exchange_hc",
    1497              :                              gorc->exchange_hc),
    1498              :     TALER_JSON_pack_amount ("deposit_total",
    1499              :                             &gorc->deposits_total),
    1500              :     GNUNET_JSON_pack_object_incref ("contract_terms",
    1501              :                                     gorc->contract_terms_json),
    1502              :     GNUNET_JSON_pack_string ("order_status",
    1503              :                              "paid"),
    1504              :     GNUNET_JSON_pack_timestamp ("last_payment",
    1505              :                                 gorc->last_payment),
    1506              :     GNUNET_JSON_pack_bool ("refunded",
    1507              :                            gorc->refunded),
    1508              :     GNUNET_JSON_pack_bool ("wired",
    1509              :                            gorc->wired),
    1510              :     GNUNET_JSON_pack_bool ("refund_pending",
    1511              :                            gorc->refund_pending),
    1512              :     GNUNET_JSON_pack_allow_null (
    1513              :       TALER_JSON_pack_amount ("refund_amount",
    1514              :                               &gorc->refund_amount)),
    1515              :     GNUNET_JSON_pack_array_steal ("wire_details",
    1516              :                                   gorc->wire_details),
    1517              :     GNUNET_JSON_pack_array_steal ("refund_details",
    1518              :                                   gorc->refund_details),
    1519              :     GNUNET_JSON_pack_string ("order_status_url",
    1520              :                              order_status_url),
    1521              :     (gorc->choice_index >= 0)
    1522              :       ? GNUNET_JSON_pack_int64 ("choice_index",
    1523              :                                 gorc->choice_index)
    1524              :       : GNUNET_JSON_pack_end_ ());
    1525           36 :   GNUNET_free (order_status_url);
    1526           36 :   gorc->wire_details = NULL;
    1527           36 :   gorc->refund_details = NULL;
    1528           36 :   phase_end (gorc,
    1529              :              ret);
    1530           36 : }
    1531              : 
    1532              : 
    1533              : /**
    1534              :  * End with error status in wire_hc and wire_ec.
    1535              :  *
    1536              :  * @param[in,out] gorc order context to update
    1537              :  */
    1538              : static void
    1539            0 : phase_error (struct GetOrderRequestContext *gorc)
    1540              : {
    1541            0 :   GNUNET_assert (TALER_EC_NONE != gorc->wire_ec);
    1542            0 :   phase_end (gorc,
    1543              :              TALER_MHD_reply_with_error (gorc->sc.con,
    1544              :                                          gorc->wire_hc,
    1545              :                                          gorc->wire_ec,
    1546              :                                          NULL));
    1547            0 : }
    1548              : 
    1549              : 
    1550              : MHD_RESULT
    1551           64 : TMH_private_get_orders_ID (
    1552              :   const struct TMH_RequestHandler *rh,
    1553              :   struct MHD_Connection *connection,
    1554              :   struct TMH_HandlerContext *hc)
    1555              : {
    1556           64 :   struct GetOrderRequestContext *gorc = hc->ctx;
    1557              : 
    1558           64 :   if (NULL == gorc)
    1559              :   {
    1560              :     /* First time here, parse request and check order is known */
    1561           62 :     GNUNET_assert (NULL != hc->infix);
    1562           62 :     gorc = GNUNET_new (struct GetOrderRequestContext);
    1563           62 :     hc->cc = &gorc_cleanup;
    1564           62 :     hc->ctx = gorc;
    1565           62 :     gorc->sc.con = connection;
    1566           62 :     gorc->hc = hc;
    1567           62 :     gorc->wire_details = json_array ();
    1568           62 :     GNUNET_assert (NULL != gorc->wire_details);
    1569           62 :     gorc->refund_details = json_array ();
    1570           62 :     GNUNET_assert (NULL != gorc->refund_details);
    1571           62 :     gorc->session_id = MHD_lookup_connection_value (connection,
    1572              :                                                     MHD_GET_ARGUMENT_KIND,
    1573              :                                                     "session_id");
    1574           62 :     if (! (TALER_MHD_arg_to_yna (connection,
    1575              :                                  "allow_refunded_for_repurchase",
    1576              :                                  TALER_EXCHANGE_YNA_NO,
    1577              :                                  &gorc->allow_refunded_for_repurchase)) )
    1578            0 :       return TALER_MHD_reply_with_error (connection,
    1579              :                                          MHD_HTTP_BAD_REQUEST,
    1580              :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    1581              :                                          "allow_refunded_for_repurchase");
    1582           62 :     TALER_MHD_parse_request_timeout (connection,
    1583              :                                      &gorc->sc.long_poll_timeout);
    1584           62 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1585              :                 "Starting GET /private/orders/%s processing with timeout %s\n",
    1586              :                 hc->infix,
    1587              :                 GNUNET_STRINGS_absolute_time_to_string (
    1588              :                   gorc->sc.long_poll_timeout));
    1589              :   }
    1590           64 :   if (GNUNET_SYSERR == gorc->suspended)
    1591            0 :     return MHD_NO; /* we are in shutdown */
    1592              :   while (1)
    1593              :   {
    1594         1108 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1595              :                 "Processing order %s in phase %d\n",
    1596              :                 hc->infix,
    1597              :                 (int) gorc->phase);
    1598          586 :     switch (gorc->phase)
    1599              :     {
    1600           62 :     case GOP_INIT:
    1601           62 :       phase_init (gorc);
    1602           62 :       break;
    1603           64 :     case GOP_FETCH_CONTRACT:
    1604           64 :       phase_fetch_contract (gorc);
    1605           64 :       break;
    1606           64 :     case GOP_PARSE_CONTRACT:
    1607           64 :       phase_parse_contract (gorc);
    1608           64 :       break;
    1609           64 :     case GOP_CHECK_PAID:
    1610           64 :       phase_check_paid (gorc);
    1611           64 :       break;
    1612           64 :     case GOP_CHECK_REPURCHASE:
    1613           64 :       phase_check_repurchase (gorc);
    1614           64 :       break;
    1615           60 :     case GOP_UNPAID_FINISH:
    1616           60 :       phase_unpaid_finish (gorc);
    1617           60 :       break;
    1618           36 :     case GOP_CHECK_REFUNDS:
    1619           36 :       phase_check_refunds (gorc);
    1620           36 :       break;
    1621           36 :     case GOP_CHECK_DEPOSITS:
    1622           36 :       phase_check_deposits (gorc);
    1623           36 :       break;
    1624           36 :     case GOP_CHECK_LOCAL_TRANSFERS:
    1625           36 :       phase_check_local_transfers (gorc);
    1626           36 :       break;
    1627           36 :     case GOP_REPLY_RESULT:
    1628           36 :       phase_reply_result (gorc);
    1629           36 :       break;
    1630            0 :     case GOP_ERROR:
    1631            0 :       phase_error (gorc);
    1632            0 :       break;
    1633            2 :     case GOP_SUSPENDED_ON_UNPAID:
    1634            2 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1635              :                   "Suspending order request awaiting payment\n");
    1636            2 :       return MHD_YES;
    1637           62 :     case GOP_END_YES:
    1638           62 :       return MHD_YES;
    1639            0 :     case GOP_END_NO:
    1640            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1641              :                   "Closing connection, no response generated\n");
    1642            0 :       return MHD_NO;
    1643              :     }
    1644              :   } /* end first-time per-request initialization */
    1645              : }
        

Generated by: LCOV version 2.0-1