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: 265 377 70.3 %
Date: 2021-08-30 06:54:17 Functions: 10 11 90.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14