LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-get-orders-ID.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 3 414 0.7 %
Date: 2022-06-30 06:15:34 Functions: 1 12 8.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14