LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_get-private-orders-ORDER_ID.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 80.8 % 546 441
Test Date: 2026-09-04 23:42:01 Functions: 95.2 % 21 20

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

Generated by: LCOV version 2.0-1