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.0 % 441 322
Test Date: 2025-12-26 17:55:29 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           64 : gorc_cleanup (void *cls)
     475              : {
     476           64 :   struct GetOrderRequestContext *gorc = cls;
     477              :   struct TransferQuery *tq;
     478              : 
     479          102 :   while (NULL != (tq = gorc->tq_head))
     480              :   {
     481           38 :     GNUNET_CONTAINER_DLL_remove (gorc->tq_head,
     482              :                                  gorc->tq_tail,
     483              :                                  tq);
     484           38 :     GNUNET_free (tq->exchange_url);
     485           38 :     GNUNET_free (tq);
     486              :   }
     487              : 
     488           64 :   if (NULL != gorc->contract_terms_json)
     489           64 :     json_decref (gorc->contract_terms_json);
     490           64 :   if (NULL != gorc->contract_terms)
     491              :   {
     492           64 :     TALER_MERCHANT_contract_free (gorc->contract_terms);
     493           64 :     gorc->contract_terms = NULL;
     494              :   }
     495           64 :   if (NULL != gorc->wire_details)
     496           26 :     json_decref (gorc->wire_details);
     497           64 :   if (NULL != gorc->refund_details)
     498           26 :     json_decref (gorc->refund_details);
     499           64 :   if (NULL != gorc->tt)
     500              :   {
     501            0 :     GNUNET_SCHEDULER_cancel (gorc->tt);
     502            0 :     gorc->tt = NULL;
     503              :   }
     504           64 :   if (NULL != gorc->eh)
     505              :   {
     506            4 :     TMH_db->event_listen_cancel (gorc->eh);
     507            4 :     gorc->eh = NULL;
     508              :   }
     509           64 :   if (NULL != gorc->session_eh)
     510              :   {
     511            0 :     TMH_db->event_listen_cancel (gorc->session_eh);
     512            0 :     gorc->session_eh = NULL;
     513              :   }
     514           64 :   GNUNET_free (gorc);
     515           64 : }
     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           64 : phase_end (struct GetOrderRequestContext *gorc,
     527              :            MHD_RESULT mret)
     528              : {
     529           64 :   gorc->phase = (MHD_YES == mret)
     530              :     ? GOP_END_YES
     531           64 :     : GOP_END_NO;
     532           64 : }
     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           64 : phase_init (struct GetOrderRequestContext *gorc)
     542              : {
     543           64 :   struct TMH_HandlerContext *hc = gorc->hc;
     544           64 :   struct TMH_OrderPayEventP pay_eh = {
     545           64 :     .header.size = htons (sizeof (pay_eh)),
     546           64 :     .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_PAID),
     547           64 :     .merchant_pub = hc->instance->merchant_pub
     548              :   };
     549              : 
     550           64 :   if (! GNUNET_TIME_absolute_is_future (gorc->sc.long_poll_timeout))
     551              :   {
     552           60 :     gorc->phase++;
     553           60 :     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           66 : phase_fetch_contract (struct GetOrderRequestContext *gorc)
     605              : {
     606           66 :   struct TMH_HandlerContext *hc = gorc->hc;
     607              :   enum GNUNET_DB_QueryStatus qs;
     608              : 
     609           66 :   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           66 :   TMH_db->preflight (TMH_db->cls);
     623           66 :   qs = TMH_db->lookup_contract_terms3 (TMH_db->cls,
     624           66 :                                        hc->instance->settings.id,
     625           66 :                                        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           66 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     635              :               "lookup_contract_terms (%s) returned %d\n",
     636              :               hc->infix,
     637              :               (int) qs);
     638           66 :   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           66 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     653              :   {
     654           54 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     655              :                 "Order %s is %s (%s) according to database, choice %d\n",
     656              :                 hc->infix,
     657              :                 gorc->paid ? "paid" : "unpaid",
     658              :                 gorc->wired ? "wired" : "unwired",
     659              :                 (int) gorc->choice_index);
     660           54 :     gorc->phase++;
     661           54 :     return;
     662              :   }
     663           12 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
     664           12 :   GNUNET_assert (! gorc->paid);
     665              :   /* No contract, only order, fetch from orders table */
     666           12 :   gorc->order_only = true;
     667              :   {
     668              :     struct TALER_MerchantPostDataHashP unused;
     669              : 
     670              :     /* We need the order for two cases:  Either when the contract doesn't exist yet,
     671              :      * or when the order is claimed but unpaid, and we need the claim token. */
     672           12 :     qs = TMH_db->lookup_order (TMH_db->cls,
     673           12 :                                hc->instance->settings.id,
     674           12 :                                hc->infix,
     675              :                                &gorc->claim_token,
     676              :                                &unused,
     677              :                                &gorc->contract_terms_json);
     678              :   }
     679           12 :   if (0 > qs)
     680              :   {
     681              :     /* single, read-only SQL statements should never cause
     682              :        serialization problems */
     683            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
     684              :     /* Always report on hard error as well to enable diagnostics */
     685            0 :     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
     686            0 :     phase_end (gorc,
     687              :                TALER_MHD_reply_with_error (gorc->sc.con,
     688              :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     689              :                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
     690              :                                            "order"));
     691            0 :     return;
     692              :   }
     693           12 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     694              :   {
     695            0 :     phase_end (gorc,
     696              :                TALER_MHD_reply_with_error (gorc->sc.con,
     697              :                                            MHD_HTTP_NOT_FOUND,
     698              :                                            TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
     699            0 :                                            hc->infix));
     700            0 :     return;
     701              :   }
     702           12 :   gorc->phase++;
     703              : }
     704              : 
     705              : 
     706              : /**
     707              :  * Obtain parse contract terms of the order.  Extracts the fulfillment URL,
     708              :  * total amount, summary and timestamp from the contract terms!
     709              :  *
     710              :  * @param[in,out] gorc order context to update
     711              :  */
     712              : static void
     713           66 : phase_parse_contract (struct GetOrderRequestContext *gorc)
     714              : {
     715           66 :   struct TMH_HandlerContext *hc = gorc->hc;
     716              : 
     717           66 :   if (NULL == gorc->contract_terms)
     718              :   {
     719           64 :     gorc->contract_terms = TALER_MERCHANT_contract_parse (
     720              :       gorc->contract_terms_json,
     721              :       true);
     722              : 
     723           64 :     if (NULL == gorc->contract_terms)
     724              :     {
     725            0 :       GNUNET_break (0);
     726            0 :       phase_end (gorc,
     727              :                  TALER_MHD_reply_with_error (
     728              :                    gorc->sc.con,
     729              :                    MHD_HTTP_INTERNAL_SERVER_ERROR,
     730              :                    TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
     731            0 :                    hc->infix));
     732            0 :       return;
     733              :     }
     734              :   }
     735              : 
     736           66 :   switch (gorc->contract_terms->version)
     737              :   {
     738           66 :   case TALER_MERCHANT_CONTRACT_VERSION_0:
     739           66 :     gorc->contract_amount = gorc->contract_terms->details.v0.brutto;
     740           66 :     break;
     741            0 :   case TALER_MERCHANT_CONTRACT_VERSION_1:
     742            0 :     if (gorc->choice_index >= 0)
     743              :     {
     744            0 :       if (gorc->choice_index >=
     745            0 :           gorc->contract_terms->details.v1.choices_len)
     746              :       {
     747            0 :         GNUNET_break (0);
     748            0 :         phase_end (gorc,
     749              :                    TALER_MHD_reply_with_error (
     750              :                      gorc->sc.con,
     751              :                      MHD_HTTP_INTERNAL_SERVER_ERROR,
     752              :                      TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
     753              :                      NULL));
     754            0 :         return;
     755              :       }
     756              : 
     757            0 :       gorc->contract_amount =
     758            0 :         gorc->contract_terms->details.v1.choices[gorc->choice_index].amount;
     759              :     }
     760              :     else
     761              :     {
     762            0 :       GNUNET_break (gorc->order_only);
     763            0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     764              :                   "Choice index %i for order %s is invalid or not yet available",
     765              :                   gorc->choice_index,
     766              :                   gorc->contract_terms->order_id);
     767              :     }
     768            0 :     break;
     769            0 :   default:
     770              :     {
     771            0 :       GNUNET_break (0);
     772            0 :       phase_end (gorc,
     773              :                  TALER_MHD_reply_with_error (
     774              :                    gorc->sc.con,
     775              :                    MHD_HTTP_INTERNAL_SERVER_ERROR,
     776              :                    TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_VERSION,
     777              :                    NULL));
     778            0 :       return;
     779              :     }
     780              :   }
     781              : 
     782          120 :   if ( (! gorc->order_only) &&
     783              :        (GNUNET_OK !=
     784           54 :         TALER_JSON_contract_hash (gorc->contract_terms_json,
     785              :                                   &gorc->h_contract_terms)) )
     786              :   {
     787            0 :     GNUNET_break (0);
     788            0 :     phase_end (gorc,
     789              :                TALER_MHD_reply_with_error (gorc->sc.con,
     790              :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     791              :                                            TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
     792              :                                            NULL));
     793            0 :     return;
     794              :   }
     795           66 :   GNUNET_assert (NULL != gorc->contract_terms_json);
     796           66 :   GNUNET_assert (NULL != gorc->contract_terms);
     797           66 :   gorc->phase++;
     798              : }
     799              : 
     800              : 
     801              : /**
     802              :  * Check payment status of the order.
     803              :  *
     804              :  * @param[in,out] gorc order context to update
     805              :  */
     806              : static void
     807           66 : phase_check_paid (struct GetOrderRequestContext *gorc)
     808              : {
     809           66 :   struct TMH_HandlerContext *hc = gorc->hc;
     810              : 
     811           66 :   if (gorc->order_only)
     812              :   {
     813           12 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     814              :                 "Order %s unclaimed, no need to lookup payment status\n",
     815              :                 hc->infix);
     816           12 :     GNUNET_assert (! gorc->paid);
     817           12 :     GNUNET_assert (! gorc->wired);
     818           12 :     gorc->phase++;
     819           12 :     return;
     820              :   }
     821           54 :   if (NULL == gorc->session_id)
     822              :   {
     823           44 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     824              :                 "No session ID, do not need to lookup session-ID specific payment status (%s/%s)\n",
     825              :                 gorc->paid ? "paid" : "unpaid",
     826              :                 gorc->wired ? "wired" : "unwired");
     827           44 :     gorc->phase++;
     828           44 :     return;
     829              :   }
     830           10 :   if (! gorc->paid_session_matches)
     831              :   {
     832            8 :     gorc->paid = false;
     833            8 :     gorc->wired = false;
     834              :   }
     835           10 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     836              :               "Order %s %s for session %s (%s)\n",
     837              :               hc->infix,
     838              :               gorc->paid ? "paid" : "unpaid",
     839              :               gorc->session_id,
     840              :               gorc->wired ? "wired" : "unwired");
     841           10 :   gorc->phase++;
     842              : }
     843              : 
     844              : 
     845              : /**
     846              :  * Check if re-purchase detection applies to the order.
     847              :  *
     848              :  * @param[in,out] gorc order context to update
     849              :  */
     850              : static void
     851           66 : phase_check_repurchase (struct GetOrderRequestContext *gorc)
     852              : {
     853           66 :   struct TMH_HandlerContext *hc = gorc->hc;
     854           66 :   char *already_paid_order_id = NULL;
     855              :   enum GNUNET_DB_QueryStatus qs;
     856              :   char *taler_pay_uri;
     857              :   char *order_status_url;
     858              :   MHD_RESULT ret;
     859              : 
     860           66 :   if ( (gorc->paid) ||
     861           28 :        (NULL == gorc->contract_terms->fulfillment_url) ||
     862           17 :        (NULL == gorc->session_id) )
     863              :   {
     864              :     /* Repurchase cannot apply */
     865           58 :     gorc->phase++;
     866           62 :     return;
     867              :   }
     868            8 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     869              :               "Running re-purchase detection for %s/%s\n",
     870              :               gorc->session_id,
     871              :               gorc->contract_terms->fulfillment_url);
     872            8 :   qs = TMH_db->lookup_order_by_fulfillment (
     873            8 :     TMH_db->cls,
     874            8 :     hc->instance->settings.id,
     875            8 :     gorc->contract_terms->fulfillment_url,
     876              :     gorc->session_id,
     877              :     TALER_EXCHANGE_YNA_NO !=
     878            8 :     gorc->allow_refunded_for_repurchase,
     879              :     &already_paid_order_id);
     880            8 :   if (0 > qs)
     881              :   {
     882              :     /* single, read-only SQL statements should never cause
     883              :        serialization problems, and the entry should exist as per above */
     884            0 :     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
     885            0 :     phase_end (gorc,
     886              :                TALER_MHD_reply_with_error (gorc->sc.con,
     887              :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     888              :                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
     889              :                                            "order by fulfillment"));
     890            0 :     return;
     891              :   }
     892            8 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     893              :   {
     894            4 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     895              :                 "No already paid order for %s/%s\n",
     896              :                 gorc->session_id,
     897              :                 gorc->contract_terms->fulfillment_url);
     898            4 :     gorc->phase++;
     899            4 :     return;
     900              :   }
     901              : 
     902              :   /* User did pay for this order, but under a different session; ask wallet to
     903              :      switch order ID */
     904            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     905              :               "Found already paid order %s\n",
     906              :               already_paid_order_id);
     907            4 :   taler_pay_uri = TMH_make_taler_pay_uri (gorc->sc.con,
     908            4 :                                           hc->infix,
     909              :                                           gorc->session_id,
     910            4 :                                           hc->instance->settings.id,
     911              :                                           &gorc->claim_token);
     912            4 :   order_status_url = TMH_make_order_status_url (gorc->sc.con,
     913            4 :                                                 hc->infix,
     914              :                                                 gorc->session_id,
     915            4 :                                                 hc->instance->settings.id,
     916              :                                                 &gorc->claim_token,
     917              :                                                 NULL);
     918            4 :   if ( (NULL == taler_pay_uri) ||
     919              :        (NULL == order_status_url) )
     920              :   {
     921            0 :     GNUNET_break_op (0);
     922            0 :     GNUNET_free (taler_pay_uri);
     923            0 :     GNUNET_free (order_status_url);
     924            0 :     phase_end (gorc,
     925              :                TALER_MHD_reply_with_error (gorc->sc.con,
     926              :                                            MHD_HTTP_BAD_REQUEST,
     927              :                                            TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
     928              :                                            "host"));
     929            0 :     return;
     930              :   }
     931            4 :   ret = TALER_MHD_REPLY_JSON_PACK (
     932              :     gorc->sc.con,
     933              :     MHD_HTTP_OK,
     934              :     GNUNET_JSON_pack_string ("taler_pay_uri",
     935              :                              taler_pay_uri),
     936              :     GNUNET_JSON_pack_string ("order_status_url",
     937              :                              order_status_url),
     938              :     GNUNET_JSON_pack_string ("order_status",
     939              :                              "unpaid"),
     940              :     GNUNET_JSON_pack_string ("already_paid_order_id",
     941              :                              already_paid_order_id),
     942              :     GNUNET_JSON_pack_string ("already_paid_fulfillment_url",
     943              :                              gorc->contract_terms->fulfillment_url),
     944              :     /* undefined for unpaid v1 contracts */
     945              :     GNUNET_JSON_pack_allow_null (
     946              :       TALER_JSON_pack_amount ("total_amount",
     947              :                               TALER_amount_is_valid (&gorc->contract_amount)
     948              :                               ? &gorc->contract_amount
     949              :                               : NULL)),
     950              :     GNUNET_JSON_pack_string ("summary",
     951              :                              gorc->contract_terms->summary),
     952              :     GNUNET_JSON_pack_timestamp ("pay_deadline",
     953              :                                 gorc->contract_terms->pay_deadline),
     954              :     GNUNET_JSON_pack_timestamp ("creation_time",
     955              :                                 gorc->contract_terms->timestamp));
     956            4 :   GNUNET_free (order_status_url);
     957            4 :   GNUNET_free (taler_pay_uri);
     958            4 :   GNUNET_free (already_paid_order_id);
     959            4 :   phase_end (gorc,
     960              :              ret);
     961              : }
     962              : 
     963              : 
     964              : /**
     965              :  * Check if we should suspend until the order is paid.
     966              :  *
     967              :  * @param[in,out] gorc order context to update
     968              :  */
     969              : static void
     970           62 : phase_unpaid_finish (struct GetOrderRequestContext *gorc)
     971              : {
     972           62 :   struct TMH_HandlerContext *hc = gorc->hc;
     973              :   char *taler_pay_uri;
     974              :   char *order_status_url;
     975              :   MHD_RESULT ret;
     976              : 
     977           62 :   if (gorc->paid)
     978              :   {
     979           38 :     gorc->phase++;
     980           50 :     return;
     981              :   }
     982              :   /* User never paid for this order, suspend waiting
     983              :      on payment or return details. */
     984           24 :   if (GNUNET_TIME_absolute_is_future (gorc->sc.long_poll_timeout))
     985              :   {
     986            2 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     987              :                 "Suspending GET /private/orders/%s\n",
     988              :                 hc->infix);
     989            2 :     GNUNET_CONTAINER_DLL_insert (gorc_head,
     990              :                                  gorc_tail,
     991              :                                  gorc);
     992            2 :     gorc->phase = GOP_SUSPENDED_ON_UNPAID;
     993            2 :     gorc->suspended = GNUNET_YES;
     994            2 :     MHD_suspend_connection (gorc->sc.con);
     995            2 :     return;
     996              :   }
     997           22 :   order_status_url = TMH_make_order_status_url (gorc->sc.con,
     998           22 :                                                 hc->infix,
     999              :                                                 gorc->session_id,
    1000           22 :                                                 hc->instance->settings.id,
    1001              :                                                 &gorc->claim_token,
    1002              :                                                 NULL);
    1003           22 :   if (! gorc->order_only)
    1004              :   {
    1005           10 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1006              :                 "Order %s claimed but not paid yet\n",
    1007              :                 hc->infix);
    1008           10 :     phase_end (gorc,
    1009           10 :                TALER_MHD_REPLY_JSON_PACK (
    1010              :                  gorc->sc.con,
    1011              :                  MHD_HTTP_OK,
    1012              :                  GNUNET_JSON_pack_string ("order_status_url",
    1013              :                                           order_status_url),
    1014              :                  GNUNET_JSON_pack_object_incref ("contract_terms",
    1015              :                                                  gorc->contract_terms_json),
    1016              :                  GNUNET_JSON_pack_string ("order_status",
    1017              :                                           "claimed")));
    1018           10 :     GNUNET_free (order_status_url);
    1019           10 :     return;
    1020              :   }
    1021           12 :   taler_pay_uri = TMH_make_taler_pay_uri (gorc->sc.con,
    1022           12 :                                           hc->infix,
    1023              :                                           gorc->session_id,
    1024           12 :                                           hc->instance->settings.id,
    1025              :                                           &gorc->claim_token);
    1026           12 :   ret = TALER_MHD_REPLY_JSON_PACK (
    1027              :     gorc->sc.con,
    1028              :     MHD_HTTP_OK,
    1029              :     GNUNET_JSON_pack_string ("taler_pay_uri",
    1030              :                              taler_pay_uri),
    1031              :     GNUNET_JSON_pack_string ("order_status_url",
    1032              :                              order_status_url),
    1033              :     GNUNET_JSON_pack_string ("order_status",
    1034              :                              "unpaid"),
    1035              :     /* undefined for unpaid v1 contracts */
    1036              :     GNUNET_JSON_pack_allow_null (
    1037              :       TALER_JSON_pack_amount ("total_amount",
    1038              :                               &gorc->contract_amount)),
    1039              :     GNUNET_JSON_pack_string ("summary",
    1040              :                              gorc->contract_terms->summary),
    1041              :     GNUNET_JSON_pack_timestamp ("creation_time",
    1042              :                                 gorc->contract_terms->timestamp));
    1043           12 :   GNUNET_free (taler_pay_uri);
    1044           12 :   GNUNET_free (order_status_url);
    1045           12 :   phase_end (gorc,
    1046              :              ret);
    1047              : 
    1048              : }
    1049              : 
    1050              : 
    1051              : /**
    1052              :  * Function called with information about a refund.
    1053              :  * It is responsible for summing up the refund amount.
    1054              :  *
    1055              :  * @param cls closure
    1056              :  * @param refund_serial unique serial number of the refund
    1057              :  * @param timestamp time of the refund (for grouping of refunds in the wallet UI)
    1058              :  * @param coin_pub public coin from which the refund comes from
    1059              :  * @param exchange_url URL of the exchange that issued @a coin_pub
    1060              :  * @param rtransaction_id identificator of the refund
    1061              :  * @param reason human-readable explanation of the refund
    1062              :  * @param refund_amount refund amount which is being taken from @a coin_pub
    1063              :  * @param pending true if the this refund was not yet processed by the wallet/exchange
    1064              :  */
    1065              : static void
    1066            8 : process_refunds_cb (
    1067              :   void *cls,
    1068              :   uint64_t refund_serial,
    1069              :   struct GNUNET_TIME_Timestamp timestamp,
    1070              :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1071              :   const char *exchange_url,
    1072              :   uint64_t rtransaction_id,
    1073              :   const char *reason,
    1074              :   const struct TALER_Amount *refund_amount,
    1075              :   bool pending)
    1076              : {
    1077            8 :   struct GetOrderRequestContext *gorc = cls;
    1078              : 
    1079            8 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1080              :               "Found refund %llu over %s for reason %s\n",
    1081              :               (unsigned long long) rtransaction_id,
    1082              :               TALER_amount2s (refund_amount),
    1083              :               reason);
    1084            8 :   GNUNET_assert (
    1085              :     0 ==
    1086              :     json_array_append_new (
    1087              :       gorc->refund_details,
    1088              :       GNUNET_JSON_PACK (
    1089              :         TALER_JSON_pack_amount ("amount",
    1090              :                                 refund_amount),
    1091              :         GNUNET_JSON_pack_bool ("pending",
    1092              :                                pending),
    1093              :         GNUNET_JSON_pack_timestamp ("timestamp",
    1094              :                                     timestamp),
    1095              :         GNUNET_JSON_pack_string ("reason",
    1096              :                                  reason))));
    1097              :   /* For refunded coins, we are not charged deposit fees, so subtract those
    1098              :      again */
    1099            8 :   for (struct TransferQuery *tq = gorc->tq_head;
    1100            8 :        NULL != tq;
    1101            0 :        tq = tq->next)
    1102              :   {
    1103            0 :     if (0 !=
    1104            0 :         strcmp (exchange_url,
    1105            0 :                 tq->exchange_url))
    1106            0 :       continue;
    1107            0 :     if (0 !=
    1108            0 :         GNUNET_memcmp (&tq->coin_pub,
    1109              :                        coin_pub))
    1110            0 :       continue;
    1111            0 :     if (GNUNET_OK !=
    1112            0 :         TALER_amount_cmp_currency (
    1113            0 :           &gorc->deposit_fees_total,
    1114            0 :           &tq->deposit_fee))
    1115              :     {
    1116            0 :       gorc->refund_currency_mismatch = true;
    1117            0 :       return;
    1118              :     }
    1119            0 :     GNUNET_assert (
    1120              :       0 <=
    1121              :       TALER_amount_subtract (&gorc->deposit_fees_total,
    1122              :                              &gorc->deposit_fees_total,
    1123              :                              &tq->deposit_fee));
    1124              :   }
    1125            8 :   if (GNUNET_OK !=
    1126            8 :       TALER_amount_cmp_currency (
    1127            8 :         &gorc->refund_amount,
    1128              :         refund_amount))
    1129              :   {
    1130            0 :     gorc->refund_currency_mismatch = true;
    1131            0 :     return;
    1132              :   }
    1133            8 :   GNUNET_assert (0 <=
    1134              :                  TALER_amount_add (&gorc->refund_amount,
    1135              :                                    &gorc->refund_amount,
    1136              :                                    refund_amount));
    1137            8 :   gorc->refunded = true;
    1138            8 :   gorc->refund_pending |= pending;
    1139              : }
    1140              : 
    1141              : 
    1142              : /**
    1143              :  * Check refund status for the order.
    1144              :  *
    1145              :  * @param[in,out] gorc order context to update
    1146              :  */
    1147              : static void
    1148           38 : phase_check_refunds (struct GetOrderRequestContext *gorc)
    1149              : {
    1150           38 :   struct TMH_HandlerContext *hc = gorc->hc;
    1151              :   enum GNUNET_DB_QueryStatus qs;
    1152              : 
    1153           38 :   GNUNET_assert (! gorc->order_only);
    1154           38 :   GNUNET_assert (gorc->paid);
    1155              : 
    1156              :   /* Accumulate refunds, if any. */
    1157           38 :   GNUNET_assert (GNUNET_OK ==
    1158              :                  TALER_amount_set_zero (gorc->contract_amount.currency,
    1159              :                                         &gorc->refund_amount));
    1160              : 
    1161           38 :   qs = TMH_db->lookup_refunds_detailed (
    1162           38 :     TMH_db->cls,
    1163           38 :     hc->instance->settings.id,
    1164           38 :     &gorc->h_contract_terms,
    1165              :     &process_refunds_cb,
    1166              :     gorc);
    1167           38 :   if (0 > qs)
    1168              :   {
    1169            0 :     GNUNET_break (0);
    1170            0 :     phase_end (gorc,
    1171              :                TALER_MHD_reply_with_error (
    1172              :                  gorc->sc.con,
    1173              :                  MHD_HTTP_INTERNAL_SERVER_ERROR,
    1174              :                  TALER_EC_GENERIC_DB_FETCH_FAILED,
    1175              :                  "detailed refunds"));
    1176            0 :     return;
    1177              :   }
    1178           38 :   if (gorc->refund_currency_mismatch)
    1179              :   {
    1180            0 :     GNUNET_break (0);
    1181            0 :     phase_end (gorc,
    1182              :                TALER_MHD_reply_with_error (
    1183              :                  gorc->sc.con,
    1184              :                  MHD_HTTP_INTERNAL_SERVER_ERROR,
    1185              :                  TALER_EC_GENERIC_DB_FETCH_FAILED,
    1186              :                  "refunds in different currency than original order price"));
    1187            0 :     return;
    1188              :   }
    1189           38 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1190              :               "Total refunds are %s\n",
    1191              :               TALER_amount2s (&gorc->refund_amount));
    1192           38 :   gorc->phase++;
    1193              : }
    1194              : 
    1195              : 
    1196              : /**
    1197              :  * Function called with each @a coin_pub that was deposited into the
    1198              :  * @a h_wire account of the merchant for the @a deposit_serial as part
    1199              :  * of the payment for the order identified by @a cls.
    1200              :  *
    1201              :  * Queries the exchange for the payment status associated with the
    1202              :  * given coin.
    1203              :  *
    1204              :  * @param cls a `struct GetOrderRequestContext`
    1205              :  * @param deposit_serial identifies the deposit operation
    1206              :  * @param exchange_url URL of the exchange that issued @a coin_pub
    1207              :  * @param h_wire hash of the merchant's wire account into which the deposit was made
    1208              :  * @param deposit_timestamp when was the deposit made
    1209              :  * @param amount_with_fee amount the exchange will deposit for this coin
    1210              :  * @param deposit_fee fee the exchange will charge for this coin
    1211              :  * @param coin_pub public key of the deposited coin
    1212              :  */
    1213              : static void
    1214           38 : deposit_cb (
    1215              :   void *cls,
    1216              :   uint64_t deposit_serial,
    1217              :   const char *exchange_url,
    1218              :   const struct TALER_MerchantWireHashP *h_wire,
    1219              :   struct GNUNET_TIME_Timestamp deposit_timestamp,
    1220              :   const struct TALER_Amount *amount_with_fee,
    1221              :   const struct TALER_Amount *deposit_fee,
    1222              :   const struct TALER_CoinSpendPublicKeyP *coin_pub)
    1223              : {
    1224           38 :   struct GetOrderRequestContext *gorc = cls;
    1225              :   struct TransferQuery *tq;
    1226              : 
    1227           38 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1228              :               "Checking deposit status for coin %s (over %s)\n",
    1229              :               TALER_B2S (coin_pub),
    1230              :               TALER_amount2s (amount_with_fee));
    1231              :   gorc->last_payment
    1232           38 :     = GNUNET_TIME_timestamp_max (gorc->last_payment,
    1233              :                                  deposit_timestamp);
    1234           38 :   tq = GNUNET_new (struct TransferQuery);
    1235           38 :   tq->gorc = gorc;
    1236           38 :   tq->exchange_url = GNUNET_strdup (exchange_url);
    1237           38 :   tq->deposit_serial = deposit_serial;
    1238           38 :   GNUNET_CONTAINER_DLL_insert (gorc->tq_head,
    1239              :                                gorc->tq_tail,
    1240              :                                tq);
    1241           38 :   tq->coin_pub = *coin_pub;
    1242           38 :   tq->h_wire = *h_wire;
    1243           38 :   tq->amount_with_fee = *amount_with_fee;
    1244           38 :   tq->deposit_fee = *deposit_fee;
    1245           38 : }
    1246              : 
    1247              : 
    1248              : /**
    1249              :  * Check wire transfer status for the order at the exchange.
    1250              :  *
    1251              :  * @param[in,out] gorc order context to update
    1252              :  */
    1253              : static void
    1254           38 : phase_check_deposits (struct GetOrderRequestContext *gorc)
    1255              : {
    1256           38 :   GNUNET_assert (! gorc->order_only);
    1257           38 :   GNUNET_assert (gorc->paid);
    1258              : 
    1259              :   /* amount must be always valid for paid orders */
    1260           38 :   GNUNET_assert (GNUNET_OK ==
    1261              :                  TALER_amount_is_valid (&gorc->contract_amount));
    1262              : 
    1263           38 :   GNUNET_assert (GNUNET_OK ==
    1264              :                  TALER_amount_set_zero (gorc->contract_amount.currency,
    1265              :                                         &gorc->deposits_total));
    1266           38 :   GNUNET_assert (GNUNET_OK ==
    1267              :                  TALER_amount_set_zero (gorc->contract_amount.currency,
    1268              :                                         &gorc->deposit_fees_total));
    1269           38 :   TMH_db->lookup_deposits_by_order (TMH_db->cls,
    1270              :                                     gorc->order_serial,
    1271              :                                     &deposit_cb,
    1272              :                                     gorc);
    1273           38 :   gorc->phase++;
    1274           38 : }
    1275              : 
    1276              : 
    1277              : /**
    1278              :  * Function called with available wire details, to be added to
    1279              :  * the response.
    1280              :  *
    1281              :  * @param cls a `struct GetOrderRequestContext`
    1282              :  * @param wtid wire transfer subject of the wire transfer for the coin
    1283              :  * @param exchange_url base URL of the exchange that made the payment
    1284              :  * @param execution_time when was the payment made
    1285              :  * @param deposit_value contribution of the coin to the total wire transfer value
    1286              :  * @param deposit_fee deposit fee charged by the exchange for the coin
    1287              :  * @param transfer_confirmed did the merchant confirm that a wire transfer with
    1288              :  *        @a wtid over the total amount happened?
    1289              :  */
    1290              : static void
    1291           14 : process_transfer_details (
    1292              :   void *cls,
    1293              :   const struct TALER_WireTransferIdentifierRawP *wtid,
    1294              :   const char *exchange_url,
    1295              :   struct GNUNET_TIME_Timestamp execution_time,
    1296              :   const struct TALER_Amount *deposit_value,
    1297              :   const struct TALER_Amount *deposit_fee,
    1298              :   bool transfer_confirmed)
    1299              : {
    1300           14 :   struct GetOrderRequestContext *gorc = cls;
    1301           14 :   json_t *wire_details = gorc->wire_details;
    1302              :   struct TALER_Amount wired;
    1303              : 
    1304           14 :   if ( (GNUNET_OK !=
    1305           14 :         TALER_amount_cmp_currency (&gorc->deposits_total,
    1306           14 :                                    deposit_value)) ||
    1307              :        (GNUNET_OK !=
    1308           14 :         TALER_amount_cmp_currency (&gorc->deposit_fees_total,
    1309              :                                    deposit_fee)) )
    1310              :   {
    1311            0 :     GNUNET_break (0);
    1312            0 :     gorc->deposit_currency_mismatch = true;
    1313            0 :     return;
    1314              :   }
    1315              : 
    1316              :   /* Compute total amount *wired* */
    1317           14 :   GNUNET_assert (0 <=
    1318              :                  TALER_amount_add (&gorc->deposits_total,
    1319              :                                    &gorc->deposits_total,
    1320              :                                    deposit_value));
    1321           14 :   GNUNET_assert (0 <=
    1322              :                  TALER_amount_add (&gorc->deposit_fees_total,
    1323              :                                    &gorc->deposit_fees_total,
    1324              :                                    deposit_fee));
    1325           14 :   GNUNET_assert (0 <= TALER_amount_subtract (&wired,
    1326              :                                              deposit_value,
    1327              :                                              deposit_fee));
    1328           14 :   GNUNET_assert (0 ==
    1329              :                  json_array_append_new (
    1330              :                    wire_details,
    1331              :                    GNUNET_JSON_PACK (
    1332              :                      GNUNET_JSON_pack_data_auto ("wtid",
    1333              :                                                  wtid),
    1334              :                      GNUNET_JSON_pack_string ("exchange_url",
    1335              :                                               exchange_url),
    1336              :                      TALER_JSON_pack_amount ("amount",
    1337              :                                              &wired),
    1338              :                      GNUNET_JSON_pack_timestamp ("execution_time",
    1339              :                                                  execution_time),
    1340              :                      GNUNET_JSON_pack_bool ("confirmed",
    1341              :                                             transfer_confirmed))));
    1342              : }
    1343              : 
    1344              : 
    1345              : /**
    1346              :  * Check transfer status in local database.
    1347              :  *
    1348              :  * @param[in,out] gorc order context to update
    1349              :  */
    1350              : static void
    1351           38 : phase_check_local_transfers (struct GetOrderRequestContext *gorc)
    1352              : {
    1353           38 :   struct TMH_HandlerContext *hc = gorc->hc;
    1354              :   enum GNUNET_DB_QueryStatus qs;
    1355              : 
    1356           38 :   GNUNET_assert (gorc->paid);
    1357           38 :   GNUNET_assert (! gorc->order_only);
    1358              : 
    1359           38 :   GNUNET_assert (GNUNET_OK ==
    1360              :                  TALER_amount_set_zero (gorc->contract_amount.currency,
    1361              :                                         &gorc->deposits_total));
    1362           38 :   GNUNET_assert (GNUNET_OK ==
    1363              :                  TALER_amount_set_zero (gorc->contract_amount.currency,
    1364              :                                         &gorc->deposit_fees_total));
    1365              : 
    1366           38 :   qs = TMH_db->lookup_transfer_details_by_order (TMH_db->cls,
    1367              :                                                  gorc->order_serial,
    1368              :                                                  &process_transfer_details,
    1369              :                                                  gorc);
    1370           38 :   if (0 > qs)
    1371              :   {
    1372            0 :     GNUNET_break (0);
    1373            0 :     phase_end (gorc,
    1374              :                TALER_MHD_reply_with_error (gorc->sc.con,
    1375              :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    1376              :                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
    1377              :                                            "transfer details"));
    1378            0 :     return;
    1379              :   }
    1380           38 :   if (gorc->deposit_currency_mismatch)
    1381              :   {
    1382            0 :     GNUNET_break (0);
    1383            0 :     phase_end (gorc,
    1384              :                TALER_MHD_reply_with_error (gorc->sc.con,
    1385              :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
    1386              :                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
    1387              :                                            "deposits in different currency than original order price"));
    1388            0 :     return;
    1389              :   }
    1390              : 
    1391           38 :   if (! gorc->wired)
    1392              :   {
    1393              :     /* we believe(d) the wire transfer did not happen yet, check if maybe
    1394              :        in light of new evidence it did */
    1395              :     struct TALER_Amount expect_total;
    1396              : 
    1397           24 :     if (0 >
    1398           24 :         TALER_amount_subtract (&expect_total,
    1399           24 :                                &gorc->contract_amount,
    1400           24 :                                &gorc->refund_amount))
    1401              :     {
    1402            0 :       GNUNET_break (0);
    1403            0 :       phase_end (gorc,
    1404              :                  TALER_MHD_reply_with_error (
    1405              :                    gorc->sc.con,
    1406              :                    MHD_HTTP_INTERNAL_SERVER_ERROR,
    1407              :                    TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
    1408              :                    "refund exceeds contract value"));
    1409            0 :       return;
    1410              :     }
    1411           24 :     if (0 >
    1412           24 :         TALER_amount_subtract (&expect_total,
    1413              :                                &expect_total,
    1414           24 :                                &gorc->deposit_fees_total))
    1415              :     {
    1416            0 :       GNUNET_break (0);
    1417            0 :       phase_end (gorc,
    1418              :                  TALER_MHD_reply_with_error (
    1419              :                    gorc->sc.con,
    1420              :                    MHD_HTTP_INTERNAL_SERVER_ERROR,
    1421              :                    TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
    1422              :                    "deposit fees exceed total minus refunds"));
    1423            0 :       return;
    1424              :     }
    1425           24 :     if (0 >=
    1426           24 :         TALER_amount_cmp (&expect_total,
    1427           24 :                           &gorc->deposits_total))
    1428              :     {
    1429              :       /* expect_total <= gorc->deposits_total: good: we got the wire transfer */
    1430            0 :       gorc->wired = true;
    1431            0 :       qs = TMH_db->mark_order_wired (TMH_db->cls,
    1432              :                                      gorc->order_serial);
    1433            0 :       GNUNET_break (qs >= 0);   /* just warn if transaction failed */
    1434            0 :       TMH_notify_order_change (hc->instance,
    1435              :                                TMH_OSF_PAID
    1436              :                                | TMH_OSF_WIRED,
    1437            0 :                                gorc->contract_terms->timestamp,
    1438              :                                gorc->order_serial);
    1439              :     }
    1440              :   }
    1441           38 :   gorc->phase++;
    1442              : }
    1443              : 
    1444              : 
    1445              : /**
    1446              :  * Generate final result for the status request.
    1447              :  *
    1448              :  * @param[in,out] gorc order context to update
    1449              :  */
    1450              : static void
    1451           38 : phase_reply_result (struct GetOrderRequestContext *gorc)
    1452              : {
    1453           38 :   struct TMH_HandlerContext *hc = gorc->hc;
    1454              :   MHD_RESULT ret;
    1455              :   char *order_status_url;
    1456              : 
    1457           38 :   GNUNET_assert (gorc->paid);
    1458           38 :   GNUNET_assert (! gorc->order_only);
    1459              : 
    1460              :   {
    1461           38 :     struct TALER_PrivateContractHashP *h_contract = NULL;
    1462              : 
    1463              :     /* In a session-bound payment, allow the browser to check the order
    1464              :      * status page (e.g. to get a refund).
    1465              :      *
    1466              :      * Note that we don't allow this outside of session-based payment, as
    1467              :      * otherwise this becomes an oracle to convert order_id to h_contract.
    1468              :      */
    1469           38 :     if (NULL != gorc->session_id)
    1470            2 :       h_contract = &gorc->h_contract_terms;
    1471              : 
    1472              :     order_status_url =
    1473           38 :       TMH_make_order_status_url (gorc->sc.con,
    1474           38 :                                  hc->infix,
    1475              :                                  gorc->session_id,
    1476           38 :                                  hc->instance->settings.id,
    1477              :                                  &gorc->claim_token,
    1478              :                                  h_contract);
    1479              :   }
    1480           38 :   if (GNUNET_TIME_absolute_is_zero (gorc->last_payment.abs_time))
    1481              :   {
    1482            0 :     GNUNET_break (GNUNET_YES ==
    1483              :                   TALER_amount_is_zero (&gorc->contract_amount));
    1484            0 :     gorc->last_payment = gorc->contract_terms->timestamp;
    1485              :   }
    1486           38 :   ret = TALER_MHD_REPLY_JSON_PACK (
    1487              :     gorc->sc.con,
    1488              :     MHD_HTTP_OK,
    1489              :     // Deprecated in protocol v6.
    1490              :     GNUNET_JSON_pack_array_steal ("wire_reports",
    1491              :                                   json_array ()),
    1492              :     GNUNET_JSON_pack_uint64 ("exchange_code",
    1493              :                              gorc->exchange_ec),
    1494              :     GNUNET_JSON_pack_uint64 ("exchange_http_status",
    1495              :                              gorc->exchange_hc),
    1496              :     /* legacy: */
    1497              :     GNUNET_JSON_pack_uint64 ("exchange_ec",
    1498              :                              gorc->exchange_ec),
    1499              :     /* legacy: */
    1500              :     GNUNET_JSON_pack_uint64 ("exchange_hc",
    1501              :                              gorc->exchange_hc),
    1502              :     TALER_JSON_pack_amount ("deposit_total",
    1503              :                             &gorc->deposits_total),
    1504              :     GNUNET_JSON_pack_object_incref ("contract_terms",
    1505              :                                     gorc->contract_terms_json),
    1506              :     GNUNET_JSON_pack_string ("order_status",
    1507              :                              "paid"),
    1508              :     GNUNET_JSON_pack_timestamp ("last_payment",
    1509              :                                 gorc->last_payment),
    1510              :     GNUNET_JSON_pack_bool ("refunded",
    1511              :                            gorc->refunded),
    1512              :     GNUNET_JSON_pack_bool ("wired",
    1513              :                            gorc->wired),
    1514              :     GNUNET_JSON_pack_bool ("refund_pending",
    1515              :                            gorc->refund_pending),
    1516              :     GNUNET_JSON_pack_allow_null (
    1517              :       TALER_JSON_pack_amount ("refund_amount",
    1518              :                               &gorc->refund_amount)),
    1519              :     GNUNET_JSON_pack_array_steal ("wire_details",
    1520              :                                   gorc->wire_details),
    1521              :     GNUNET_JSON_pack_array_steal ("refund_details",
    1522              :                                   gorc->refund_details),
    1523              :     GNUNET_JSON_pack_string ("order_status_url",
    1524              :                              order_status_url),
    1525              :     (gorc->choice_index >= 0)
    1526              :       ? GNUNET_JSON_pack_int64 ("choice_index",
    1527              :                                 gorc->choice_index)
    1528              :       : GNUNET_JSON_pack_end_ ());
    1529           38 :   GNUNET_free (order_status_url);
    1530           38 :   gorc->wire_details = NULL;
    1531           38 :   gorc->refund_details = NULL;
    1532           38 :   phase_end (gorc,
    1533              :              ret);
    1534           38 : }
    1535              : 
    1536              : 
    1537              : /**
    1538              :  * End with error status in wire_hc and wire_ec.
    1539              :  *
    1540              :  * @param[in,out] gorc order context to update
    1541              :  */
    1542              : static void
    1543            0 : phase_error (struct GetOrderRequestContext *gorc)
    1544              : {
    1545            0 :   GNUNET_assert (TALER_EC_NONE != gorc->wire_ec);
    1546            0 :   phase_end (gorc,
    1547              :              TALER_MHD_reply_with_error (gorc->sc.con,
    1548              :                                          gorc->wire_hc,
    1549              :                                          gorc->wire_ec,
    1550              :                                          NULL));
    1551            0 : }
    1552              : 
    1553              : 
    1554              : MHD_RESULT
    1555           66 : TMH_private_get_orders_ID (
    1556              :   const struct TMH_RequestHandler *rh,
    1557              :   struct MHD_Connection *connection,
    1558              :   struct TMH_HandlerContext *hc)
    1559              : {
    1560           66 :   struct GetOrderRequestContext *gorc = hc->ctx;
    1561              : 
    1562           66 :   if (NULL == gorc)
    1563              :   {
    1564              :     /* First time here, parse request and check order is known */
    1565           64 :     GNUNET_assert (NULL != hc->infix);
    1566           64 :     gorc = GNUNET_new (struct GetOrderRequestContext);
    1567           64 :     hc->cc = &gorc_cleanup;
    1568           64 :     hc->ctx = gorc;
    1569           64 :     gorc->sc.con = connection;
    1570           64 :     gorc->hc = hc;
    1571           64 :     gorc->wire_details = json_array ();
    1572           64 :     GNUNET_assert (NULL != gorc->wire_details);
    1573           64 :     gorc->refund_details = json_array ();
    1574           64 :     GNUNET_assert (NULL != gorc->refund_details);
    1575           64 :     gorc->session_id = MHD_lookup_connection_value (connection,
    1576              :                                                     MHD_GET_ARGUMENT_KIND,
    1577              :                                                     "session_id");
    1578           64 :     if (! (TALER_MHD_arg_to_yna (connection,
    1579              :                                  "allow_refunded_for_repurchase",
    1580              :                                  TALER_EXCHANGE_YNA_NO,
    1581              :                                  &gorc->allow_refunded_for_repurchase)) )
    1582            0 :       return TALER_MHD_reply_with_error (connection,
    1583              :                                          MHD_HTTP_BAD_REQUEST,
    1584              :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    1585              :                                          "allow_refunded_for_repurchase");
    1586           64 :     TALER_MHD_parse_request_timeout (connection,
    1587              :                                      &gorc->sc.long_poll_timeout);
    1588           64 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1589              :                 "Starting GET /private/orders/%s processing with timeout %s\n",
    1590              :                 hc->infix,
    1591              :                 GNUNET_STRINGS_absolute_time_to_string (
    1592              :                   gorc->sc.long_poll_timeout));
    1593              :   }
    1594           66 :   if (GNUNET_SYSERR == gorc->suspended)
    1595            0 :     return MHD_NO; /* we are in shutdown */
    1596              :   while (1)
    1597              :   {
    1598         1150 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1599              :                 "Processing order %s in phase %d\n",
    1600              :                 hc->infix,
    1601              :                 (int) gorc->phase);
    1602          608 :     switch (gorc->phase)
    1603              :     {
    1604           64 :     case GOP_INIT:
    1605           64 :       phase_init (gorc);
    1606           64 :       break;
    1607           66 :     case GOP_FETCH_CONTRACT:
    1608           66 :       phase_fetch_contract (gorc);
    1609           66 :       break;
    1610           66 :     case GOP_PARSE_CONTRACT:
    1611           66 :       phase_parse_contract (gorc);
    1612           66 :       break;
    1613           66 :     case GOP_CHECK_PAID:
    1614           66 :       phase_check_paid (gorc);
    1615           66 :       break;
    1616           66 :     case GOP_CHECK_REPURCHASE:
    1617           66 :       phase_check_repurchase (gorc);
    1618           66 :       break;
    1619           62 :     case GOP_UNPAID_FINISH:
    1620           62 :       phase_unpaid_finish (gorc);
    1621           62 :       break;
    1622           38 :     case GOP_CHECK_REFUNDS:
    1623           38 :       phase_check_refunds (gorc);
    1624           38 :       break;
    1625           38 :     case GOP_CHECK_DEPOSITS:
    1626           38 :       phase_check_deposits (gorc);
    1627           38 :       break;
    1628           38 :     case GOP_CHECK_LOCAL_TRANSFERS:
    1629           38 :       phase_check_local_transfers (gorc);
    1630           38 :       break;
    1631           38 :     case GOP_REPLY_RESULT:
    1632           38 :       phase_reply_result (gorc);
    1633           38 :       break;
    1634            0 :     case GOP_ERROR:
    1635            0 :       phase_error (gorc);
    1636            0 :       break;
    1637            2 :     case GOP_SUSPENDED_ON_UNPAID:
    1638            2 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1639              :                   "Suspending order request awaiting payment\n");
    1640            2 :       return MHD_YES;
    1641           64 :     case GOP_END_YES:
    1642           64 :       return MHD_YES;
    1643            0 :     case GOP_END_NO:
    1644            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1645              :                   "Closing connection, no response generated\n");
    1646            0 :       return MHD_NO;
    1647              :     }
    1648              :   } /* end first-time per-request initialization */
    1649              : }
        

Generated by: LCOV version 2.0-1