LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-get-orders-ID.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 326 439 74.3 %
Date: 2025-06-23 16:22:09 Functions: 18 19 94.7 %

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

Generated by: LCOV version 1.16