LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_get-orders-ORDER_ID.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 72.0 % 614 442
Test Date: 2026-09-04 23:42:01 Functions: 95.5 % 22 21

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2014-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 Affero 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-orders-ORDER_ID.c
      18              :  * @brief implementation of GET /orders/$ID
      19              :  * @author Marcello Stanisci
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "platform.h"
      23              : #include <jansson.h>
      24              : #include <gnunet/gnunet_uri_lib.h>
      25              : #include <gnunet/gnunet_common.h>
      26              : #include <taler/taler_signatures.h>
      27              : #include <taler/taler_dbevents.h>
      28              : #include <taler/taler_json_lib.h>
      29              : #include <taler/taler_templating_lib.h>
      30              : #include <taler/taler_exchange_service.h>
      31              : #include "taler-merchant-httpd_helper.h"
      32              : #include "taler-merchant-httpd_get-orders-ORDER_ID.h"
      33              : #include "taler-merchant-httpd_mhd.h"
      34              : #include "taler-merchant-httpd_qr.h"
      35              : #include "taler/taler_error_codes.h"
      36              : #include "taler/taler_util.h"
      37              : #include "taler/taler_merchant_util.h"
      38              : #include "merchant-database/get_contract_terms_status.h"
      39              : #include "merchant-database/get_order.h"
      40              : #include "merchant-database/get_order_by_fulfillment.h"
      41              : #include "merchant-database/get_order_status.h"
      42              : #include "merchant-database/iterate_refunds_detailed.h"
      43              : #include "merchant-database/event_listen.h"
      44              : #include "merchant-database/preflight.h"
      45              : 
      46              : /**
      47              :  * How often do we retry DB transactions on serialization failures?
      48              :  */
      49              : #define MAX_RETRIES 5
      50              : 
      51              : 
      52              : /**
      53              :  * The different phases in which we handle the request.
      54              :  */
      55              : enum Phase
      56              : {
      57              :   GOP_INIT = 0,
      58              :   GOP_LOOKUP_TERMS = 1,
      59              :   GOP_PARSE_CONTRACT = 2,
      60              :   GOP_CHECK_CLIENT_ACCESS = 3,
      61              :   GOP_CHECK_PAID = 4,
      62              :   GOP_REDIRECT_TO_PAID_ORDER = 5,
      63              :   GOP_HANDLE_UNPAID = 6,
      64              :   GOP_CHECK_REFUNDED = 7,
      65              :   GOP_RETURN_STATUS = 8,
      66              :   GOP_RETURN_MHD_YES = 9,
      67              :   GOP_RETURN_MHD_NO = 10
      68              : };
      69              : 
      70              : 
      71              : /**
      72              :  * Context for the operation.
      73              :  */
      74              : struct GetOrderData
      75              : {
      76              : 
      77              :   /**
      78              :    * Hashed version of contract terms. All zeros if not provided.
      79              :    */
      80              :   struct TALER_PrivateContractHashP h_contract_terms;
      81              : 
      82              :   /**
      83              :    * Claim token used for access control. All zeros if not provided.
      84              :    */
      85              :   struct TALER_ClaimTokenP claim_token;
      86              : 
      87              :   /**
      88              :    * DLL of (suspended) requests.
      89              :    */
      90              :   struct GetOrderData *next;
      91              : 
      92              :   /**
      93              :    * DLL of (suspended) requests.
      94              :    */
      95              :   struct GetOrderData *prev;
      96              : 
      97              :   /**
      98              :    * Context of the request.
      99              :    */
     100              :   struct TMH_HandlerContext *hc;
     101              : 
     102              :   /**
     103              :    * Entry in the #resume_timeout_heap for this check payment, if we are
     104              :    * suspended.
     105              :    */
     106              :   struct TMH_SuspendedConnection sc;
     107              : 
     108              :   /**
     109              :    * Database event we are waiting on to be resuming on payment.
     110              :    */
     111              :   struct GNUNET_DB_EventHandler *pay_eh;
     112              : 
     113              :   /**
     114              :    * Database event we are waiting on to be resuming for refunds.
     115              :    */
     116              :   struct GNUNET_DB_EventHandler *refund_eh;
     117              : 
     118              :   /**
     119              :    * Database event we are waiting on to be resuming for repurchase
     120              :    * detection updating some equivalent order (same fulfillment URL)
     121              :    * to our session.
     122              :    */
     123              :   struct GNUNET_DB_EventHandler *session_eh;
     124              : 
     125              :   /**
     126              :    * Which merchant instance is this for?
     127              :    */
     128              :   struct MerchantInstance *mi;
     129              : 
     130              :   /**
     131              :    * order ID for the payment
     132              :    */
     133              :   const char *order_id;
     134              : 
     135              :   /**
     136              :    * session of the client
     137              :    */
     138              :   const char *session_id;
     139              : 
     140              :   /**
     141              :    * choice index (contract v1)
     142              :    */
     143              :   int16_t choice_index;
     144              : 
     145              :   /**
     146              :    * Contract terms of the payment we are checking. NULL when they
     147              :    * are not (yet) known.
     148              :    */
     149              :   json_t *contract_terms_json;
     150              : 
     151              :   /**
     152              :    * Parsed contract terms, NULL when parsing failed.
     153              :    */
     154              :   struct TALER_MERCHANT_Contract *contract_terms;
     155              : 
     156              :   /**
     157              :    * Parsed proto contract terms, NULL when parsing failed.
     158              :    * Careful, aliased with pc in @e contract_terms if @e contract_terms is
     159              :    * not NULL!
     160              :    */
     161              :   struct TALER_MERCHANT_ProtoContract *pc;
     162              : 
     163              :   /**
     164              :    * Order of the payment we are checking. NULL when we have a contract.
     165              :    */
     166              :   json_t *order_json;
     167              : 
     168              :   /**
     169              :    * Parsed order, NULL when parsing failed.
     170              :    */
     171              :   struct TALER_MERCHANT_Order *order;
     172              : 
     173              :   /**
     174              :    * Common terms from @e order or @e contract
     175              :    */
     176              :   const struct TALER_MERCHANT_ContractBaseTerms *ct;
     177              : 
     178              :   /**
     179              :    * Total refunds granted for this payment. Only initialized
     180              :    * if @e refunded is set to true.
     181              :    */
     182              :   struct TALER_Amount refund_amount;
     183              : 
     184              :   /**
     185              :    * Total refunds already collected.
     186              :    * if @e refunded is set to true.
     187              :    */
     188              :   struct TALER_Amount refund_taken;
     189              : 
     190              :   /**
     191              :    * Phase in which we currently are handling this
     192              :    * request.
     193              :    */
     194              :   enum Phase phase;
     195              : 
     196              :   /**
     197              :    * Return code: #TALER_EC_NONE if successful.
     198              :    */
     199              :   enum TALER_ErrorCode ec;
     200              : 
     201              :   /**
     202              :    * Did we suspend @a connection and are thus in
     203              :    * the #god_head DLL (#GNUNET_YES). Set to
     204              :    * #GNUNET_NO if we are not suspended, and to
     205              :    * #GNUNET_SYSERR if we should close the connection
     206              :    * without a response due to shutdown.
     207              :    */
     208              :   enum GNUNET_GenericReturnValue suspended;
     209              : 
     210              :   /**
     211              :    * Set to YES if refunded orders should be included when
     212              :    * doing repurchase detection.
     213              :    */
     214              :   enum TALER_EXCHANGE_YesNoAll allow_refunded_for_repurchase;
     215              : 
     216              :   /**
     217              :    * Set to true if the client passed 'h_contract'.
     218              :    */
     219              :   bool h_contract_provided;
     220              : 
     221              :   /**
     222              :    * Set to true if the client passed a 'claim' token.
     223              :    */
     224              :   bool claim_token_provided;
     225              : 
     226              :   /**
     227              :    * Set to true if we are dealing with a claimed order
     228              :    * (and thus @e h_contract_terms is set, otherwise certain
     229              :    * DB queries will not work).
     230              :    */
     231              :   bool claimed;
     232              : 
     233              :   /**
     234              :    * Set to true if this order was paid.
     235              :    */
     236              :   bool paid;
     237              : 
     238              :   /**
     239              :    * Set to true if this order has been refunded and
     240              :    * @e refund_amount is initialized.
     241              :    */
     242              :   bool refunded;
     243              : 
     244              :   /**
     245              :    * Set to true if a refund is still available for the
     246              :    * wallet for this payment.
     247              :    * @deprecated: true if refund_taken < refund_amount
     248              :    */
     249              :   bool refund_pending;
     250              : 
     251              :   /**
     252              :    * Set to true if the client requested HTML, otherwise we generate JSON.
     253              :    */
     254              :   bool generate_html;
     255              : 
     256              :   /**
     257              :    * Did we parse the order?
     258              :    */
     259              :   bool order_parsed;
     260              : 
     261              :   /**
     262              :    * Set to true if the refunds found in the DB have
     263              :    * a different currency then the main contract.
     264              :    */
     265              :   bool bad_refund_currency_in_db;
     266              : 
     267              :   /**
     268              :    * Did the hash of the contract match the contract
     269              :    * hash supplied by the client?
     270              :    */
     271              :   bool contract_match;
     272              : 
     273              :   /**
     274              :    * True if we had a claim token and the claim token
     275              :    * provided by the client matched our claim token.
     276              :    */
     277              :   bool token_match;
     278              : 
     279              :   /**
     280              :    * True if we found a (claimed) contract for the order,
     281              :    * false if we had an unclaimed order.
     282              :    */
     283              :   bool contract_available;
     284              : 
     285              : };
     286              : 
     287              : 
     288              : /**
     289              :  * Head of DLL of (suspended) requests.
     290              :  */
     291              : static struct GetOrderData *god_head;
     292              : 
     293              : /**
     294              :  * Tail of DLL of (suspended) requests.
     295              :  */
     296              : static struct GetOrderData *god_tail;
     297              : 
     298              : 
     299              : void
     300           20 : TMH_force_wallet_get_order_resume (void)
     301              : {
     302              :   struct GetOrderData *god;
     303              : 
     304           20 :   while (NULL != (god = god_head))
     305              :   {
     306            0 :     GNUNET_CONTAINER_DLL_remove (god_head,
     307              :                                  god_tail,
     308              :                                  god);
     309            0 :     GNUNET_assert (god->suspended);
     310            0 :     god->suspended = GNUNET_SYSERR;
     311            0 :     MHD_resume_connection (god->sc.con);
     312            0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     313              :   }
     314           20 : }
     315              : 
     316              : 
     317              : /**
     318              :  * Suspend this @a god until the trigger is satisfied.
     319              :  *
     320              :  * @param god request to suspend
     321              :  */
     322              : static void
     323            9 : suspend_god (struct GetOrderData *god)
     324              : {
     325            9 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     326              :               "Suspending GET /orders/%s\n",
     327              :               god->order_id);
     328              :   /* We reset the contract terms and start by looking them up
     329              :      again, as while we are suspended fundamental things could
     330              :      change (such as the contract being claimed) */
     331            9 :   if (NULL != god->contract_terms_json)
     332              :   {
     333            9 :     json_decref (god->contract_terms_json);
     334            9 :     god->contract_terms_json = NULL;
     335              :   }
     336            9 :   if (NULL != god->order_json)
     337              :   {
     338            0 :     json_decref (god->order_json);
     339            0 :     god->order_json = NULL;
     340            0 :     god->order_parsed = false;
     341              :   }
     342            9 :   if (NULL != god->contract_terms)
     343              :   {
     344            9 :     TALER_MERCHANT_contract_free (god->contract_terms);
     345            9 :     god->contract_terms = NULL;
     346            9 :     god->pc = NULL;
     347              :   }
     348            9 :   if (NULL != god->pc)
     349              :   {
     350            0 :     TALER_MERCHANT_proto_contract_free (god->pc);
     351            0 :     god->pc = NULL;
     352              :   }
     353            9 :   if (NULL != god->order)
     354              :   {
     355            0 :     TALER_MERCHANT_order_free (god->order);
     356            0 :     god->order = NULL;
     357              :   }
     358            9 :   god->ct = NULL; /* ensure not dangling */
     359            9 :   GNUNET_assert (! god->suspended);
     360            9 :   god->contract_match = false;
     361            9 :   god->token_match = false;
     362            9 :   god->contract_available = false;
     363            9 :   god->phase = GOP_LOOKUP_TERMS;
     364            9 :   god->suspended = GNUNET_YES;
     365            9 :   GNUNET_CONTAINER_DLL_insert (god_head,
     366              :                                god_tail,
     367              :                                god);
     368            9 :   MHD_suspend_connection (god->sc.con);
     369            9 : }
     370              : 
     371              : 
     372              : /**
     373              :  * Clean up the session state for a GET /orders/$ID request.
     374              :  *
     375              :  * @param cls must be a `struct GetOrderData *`
     376              :  */
     377              : static void
     378           40 : god_cleanup (void *cls)
     379              : {
     380           40 :   struct GetOrderData *god = cls;
     381              : 
     382           40 :   if (NULL != god->contract_terms_json)
     383              :   {
     384           33 :     json_decref (god->contract_terms_json);
     385           33 :     god->contract_terms_json = NULL;
     386              :   }
     387           40 :   if (NULL != god->order_json)
     388              :   {
     389            7 :     json_decref (god->order_json);
     390            7 :     god->order_json = NULL;
     391              :   }
     392           40 :   if (NULL != god->contract_terms)
     393              :   {
     394           33 :     TALER_MERCHANT_contract_free (god->contract_terms);
     395           33 :     god->contract_terms = NULL;
     396           33 :     god->pc = NULL;
     397              :   }
     398           40 :   if (NULL != god->pc)
     399              :   {
     400            0 :     TALER_MERCHANT_proto_contract_free (god->pc);
     401            0 :     god->pc = NULL;
     402              :   }
     403           40 :   if (NULL != god->order)
     404              :   {
     405            7 :     TALER_MERCHANT_order_free (god->order);
     406            7 :     god->order = NULL;
     407              :   }
     408           40 :   god->ct = NULL; /* ensure not dangling */
     409           40 :   if (NULL != god->session_eh)
     410              :   {
     411            8 :     TALER_MERCHANTDB_event_listen_cancel (god->session_eh);
     412            8 :     god->session_eh = NULL;
     413              :   }
     414           40 :   if (NULL != god->refund_eh)
     415              :   {
     416            5 :     TALER_MERCHANTDB_event_listen_cancel (god->refund_eh);
     417            5 :     god->refund_eh = NULL;
     418              :   }
     419           40 :   if (NULL != god->pay_eh)
     420              :   {
     421            9 :     TALER_MERCHANTDB_event_listen_cancel (god->pay_eh);
     422            9 :     god->pay_eh = NULL;
     423              :   }
     424           40 :   GNUNET_free (god);
     425           40 : }
     426              : 
     427              : 
     428              : /**
     429              :  * Finish the request by returning @a mret as the
     430              :  * final result.
     431              :  *
     432              :  * @param[in,out] god request we are processing
     433              :  * @param mret MHD result to return
     434              :  */
     435              : static void
     436           40 : phase_end (struct GetOrderData *god,
     437              :            enum MHD_Result mret)
     438              : {
     439           40 :   god->phase = (MHD_YES == mret)
     440              :     ? GOP_RETURN_MHD_YES
     441           40 :     : GOP_RETURN_MHD_NO;
     442           40 : }
     443              : 
     444              : 
     445              : /**
     446              :  * Finish the request by returning an error @a ec
     447              :  * with HTTP status @a http_status and @a message.
     448              :  *
     449              :  * @param[in,out] god request we are processing
     450              :  * @param http_status HTTP status code to return
     451              :  * @param ec error code to return
     452              :  * @param message human readable hint to return, can be NULL
     453              :  */
     454              : static void
     455            0 : phase_fail (struct GetOrderData *god,
     456              :             unsigned int http_status,
     457              :             enum TALER_ErrorCode ec,
     458              :             const char *message)
     459              : {
     460            0 :   phase_end (god,
     461              :              TALER_MHD_reply_with_error (god->sc.con,
     462              :                                          http_status,
     463              :                                          ec,
     464              :                                          message));
     465            0 : }
     466              : 
     467              : 
     468              : /**
     469              :  * We have received a trigger from the database
     470              :  * that we should (possibly) resume the request.
     471              :  *
     472              :  * @param cls a `struct GetOrderData` to resume
     473              :  * @param extra string encoding refund amount (or NULL)
     474              :  * @param extra_size number of bytes in @a extra
     475              :  */
     476              : static void
     477           22 : resume_by_event (void *cls,
     478              :                  const void *extra,
     479              :                  size_t extra_size)
     480              : {
     481           22 :   struct GetOrderData *god = cls;
     482              :   struct GNUNET_AsyncScopeSave old;
     483              : 
     484           22 :   GNUNET_async_scope_enter (&god->hc->async_scope_id,
     485              :                             &old);
     486           22 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     487              :               "Received event for %s with argument `%.*s`\n",
     488              :               god->order_id,
     489              :               (int) extra_size,
     490              :               (const char *) extra);
     491           22 :   if (! god->suspended)
     492              :   {
     493            5 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     494              :                 "Not suspended, ignoring event\n");
     495            5 :     GNUNET_async_scope_restore (&old);
     496           13 :     return; /* duplicate event is possible */
     497              :   }
     498           17 :   if (GNUNET_TIME_absolute_is_future (god->sc.long_poll_timeout) &&
     499           17 :       god->sc.awaiting_refund)
     500              :   {
     501              :     char *as;
     502              :     struct TALER_Amount a;
     503              : 
     504           13 :     if (0 == extra_size)
     505              :     {
     506            6 :       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     507              :                   "No amount given, but need refund above threshold\n");
     508            6 :       GNUNET_async_scope_restore (&old);
     509            8 :       return; /* not relevant */
     510              :     }
     511            7 :     as = GNUNET_strndup (extra,
     512              :                          extra_size);
     513            7 :     if (GNUNET_OK !=
     514            7 :         TALER_string_to_amount (as,
     515              :                                 &a))
     516              :     {
     517            0 :       GNUNET_break (0);
     518            0 :       GNUNET_async_scope_restore (&old);
     519            0 :       GNUNET_free (as);
     520            0 :       return;
     521              :     }
     522            7 :     GNUNET_free (as);
     523            7 :     if (GNUNET_OK !=
     524            7 :         TALER_amount_cmp_currency (&god->sc.refund_expected,
     525              :                                    &a))
     526              :     {
     527            0 :       GNUNET_break (0);
     528            0 :       GNUNET_async_scope_restore (&old);
     529            0 :       return; /* bad currency!? */
     530              :     }
     531            7 :     if (1 == TALER_amount_cmp (&god->sc.refund_expected,
     532              :                                &a))
     533              :     {
     534            2 :       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     535              :                   "Amount too small to trigger resuming\n");
     536            2 :       GNUNET_async_scope_restore (&old);
     537            2 :       return; /* refund too small */
     538              :     }
     539              :   }
     540            9 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     541              :               "Resuming (%s/%s) by event with argument `%.*s`\n",
     542              :               GNUNET_TIME_absolute_is_future (god->sc.long_poll_timeout)
     543              :               ? "future"
     544              :               : "past",
     545              :               god->sc.awaiting_refund
     546              :               ? "awaiting refund"
     547              :               : "not waiting for refund",
     548              :               (int) extra_size,
     549              :               (const char *) extra);
     550              :   /* Return the current status after a relevant event.  In particular, a
     551              :      claim changes an order from unclaimed to claimed without changing its
     552              :      unpaid HTTP response, so re-entering the long poll here would hide the
     553              :      state transition until the original timeout. */
     554            9 :   god->sc.long_poll_timeout = GNUNET_TIME_absolute_get ();
     555            9 :   god->suspended = GNUNET_NO;
     556            9 :   GNUNET_CONTAINER_DLL_remove (god_head,
     557              :                                god_tail,
     558              :                                god);
     559            9 :   MHD_resume_connection (god->sc.con);
     560            9 :   TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     561            9 :   GNUNET_async_scope_restore (&old);
     562              : }
     563              : 
     564              : 
     565              : /**
     566              :  * First phase (after request parsing).
     567              :  * Set up long-polling.
     568              :  *
     569              :  * @param[in,out] god request context
     570              :  */
     571              : static void
     572           40 : phase_init (struct GetOrderData *god)
     573              : {
     574           40 :   god->phase++;
     575           40 :   if (god->generate_html)
     576            5 :     return; /* If HTML is requested, we never actually long poll. */
     577           35 :   if (! GNUNET_TIME_absolute_is_future (god->sc.long_poll_timeout))
     578           26 :     return; /* long polling not requested */
     579              : 
     580            9 :   if (god->sc.awaiting_refund ||
     581            4 :       god->sc.awaiting_refund_obtained)
     582              :   {
     583           10 :     struct TMH_OrderPayEventP refund_eh = {
     584            5 :       .header.size = htons (sizeof (refund_eh)),
     585            5 :       .header.type = htons (god->sc.awaiting_refund_obtained
     586              :                                     ? TALER_DBEVENT_MERCHANT_REFUND_OBTAINED
     587              :                                     : TALER_DBEVENT_MERCHANT_ORDER_REFUND),
     588            5 :       .merchant_pub = god->hc->instance->merchant_pub
     589              :     };
     590              : 
     591            5 :     GNUNET_CRYPTO_hash (god->order_id,
     592              :                         strlen (god->order_id),
     593              :                         &refund_eh.h_order_id);
     594            5 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     595              :                 "Subscribing %p to refunds on %s\n",
     596              :                 god,
     597              :                 god->order_id);
     598              :     god->refund_eh
     599            5 :       = TALER_MERCHANTDB_event_listen (
     600              :           TMH_db,
     601              :           &refund_eh.header,
     602              :           GNUNET_TIME_absolute_get_remaining (
     603              :             god->sc.long_poll_timeout),
     604              :           &resume_by_event,
     605              :           god);
     606              :   }
     607              :   {
     608            9 :     struct TMH_OrderPayEventP pay_eh = {
     609            9 :       .header.size = htons (sizeof (pay_eh)),
     610            9 :       .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_STATUS_CHANGED),
     611            9 :       .merchant_pub = god->hc->instance->merchant_pub
     612              :     };
     613              : 
     614            9 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     615              :                 "Subscribing to order status changes on %s\n",
     616              :                 god->order_id);
     617            9 :     GNUNET_CRYPTO_hash (god->order_id,
     618              :                         strlen (god->order_id),
     619              :                         &pay_eh.h_order_id);
     620              :     god->pay_eh
     621            9 :       = TALER_MERCHANTDB_event_listen (
     622              :           TMH_db,
     623              :           &pay_eh.header,
     624              :           GNUNET_TIME_absolute_get_remaining (
     625              :             god->sc.long_poll_timeout),
     626              :           &resume_by_event,
     627              :           god);
     628              :   }
     629              : }
     630              : 
     631              : 
     632              : /**
     633              :  * Lookup contract terms and check client has the
     634              :  * right to access this order (by claim token or
     635              :  * contract hash).
     636              :  *
     637              :  * @param[in,out] god request context
     638              :  */
     639              : static void
     640           49 : phase_lookup_terms (struct GetOrderData *god)
     641              : {
     642              :   uint64_t order_serial;
     643              :   struct TALER_ClaimTokenP db_claim_token;
     644              : 
     645              :   /* Convert order_id to h_contract_terms */
     646           49 :   TALER_MERCHANTDB_preflight (TMH_db);
     647           49 :   GNUNET_assert (NULL == god->contract_terms_json);
     648           49 :   GNUNET_assert (NULL == god->order_json);
     649              : 
     650              :   {
     651              :     enum GNUNET_DB_QueryStatus qs;
     652              : 
     653              :     bool paid;
     654              :     bool wired;
     655              :     bool session_matches;
     656           49 :     qs = TALER_MERCHANTDB_get_contract_terms_status (
     657              :       TMH_db,
     658           49 :       god->hc->instance->settings.id,
     659              :       god->order_id,
     660              :       NULL,
     661              :       &god->contract_terms_json,
     662              :       &order_serial,
     663              :       &paid,
     664              :       &wired,
     665              :       &session_matches,
     666              :       &db_claim_token,
     667              :       &god->choice_index);
     668           49 :     if (0 > qs)
     669              :     {
     670              :       /* single, read-only SQL statements should never cause
     671              :          serialization problems */
     672            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
     673              :       /* Always report on hard error as well to enable diagnostics */
     674            0 :       GNUNET_break (0);
     675            0 :       phase_fail (god,
     676              :                   MHD_HTTP_INTERNAL_SERVER_ERROR,
     677              :                   TALER_EC_GENERIC_DB_FETCH_FAILED,
     678              :                   "get_contract_terms");
     679            0 :       return;
     680              :     }
     681              :     /* Note: when "!ord.requireClaimToken" and the client does not provide
     682              :        a claim token (all zeros!), then token_match==TRUE below: */
     683              :     god->token_match
     684           49 :       = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     685           49 :         && (0 == GNUNET_memcmp (&db_claim_token,
     686              :                                 &god->claim_token));
     687              :   }
     688              : 
     689              :   /* Check if client provided the right hash code of the contract terms */
     690           49 :   if (NULL != god->contract_terms_json)
     691              :   {
     692           42 :     god->contract_available = true;
     693           42 :     if (GNUNET_YES ==
     694           42 :         GNUNET_is_zero (&god->h_contract_terms))
     695              :     {
     696            3 :       if (GNUNET_OK !=
     697            3 :           TALER_JSON_contract_hash (god->contract_terms_json,
     698              :                                     &god->h_contract_terms))
     699              :       {
     700            0 :         GNUNET_break (0);
     701            0 :         phase_fail (god,
     702              :                     MHD_HTTP_INTERNAL_SERVER_ERROR,
     703              :                     TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
     704              :                     "contract terms");
     705            0 :         return;
     706              :       }
     707              :     }
     708              :     else
     709              :     {
     710              :       struct TALER_PrivateContractHashP h;
     711              : 
     712           39 :       if (GNUNET_OK !=
     713           39 :           TALER_JSON_contract_hash (god->contract_terms_json,
     714              :                                     &h))
     715              :       {
     716            0 :         GNUNET_break (0);
     717            0 :         phase_fail (god,
     718              :                     MHD_HTTP_INTERNAL_SERVER_ERROR,
     719              :                     TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
     720              :                     "contract terms");
     721            0 :         return;
     722              :       }
     723           39 :       god->contract_match = (0 ==
     724           39 :                              GNUNET_memcmp (&h,
     725              :                                             &god->h_contract_terms));
     726           39 :       if (! god->contract_match)
     727              :       {
     728            0 :         GNUNET_break_op (0);
     729            0 :         phase_fail (god,
     730              :                     MHD_HTTP_FORBIDDEN,
     731              :                     TALER_EC_MERCHANT_GENERIC_CONTRACT_HASH_DOES_NOT_MATCH_ORDER,
     732              :                     NULL);
     733            0 :         return;
     734              :       }
     735              :     }
     736              :   }
     737              : 
     738           49 :   if (god->contract_available)
     739              :   {
     740           42 :     god->claimed = true;
     741              :   }
     742              :   else
     743              :   {
     744              :     struct TALER_MerchantPostDataHashP unused;
     745              :     enum GNUNET_DB_QueryStatus qs;
     746              : 
     747              :     /* No contract exists yet, so no payment choice has been selected. */
     748            7 :     god->choice_index = -1;
     749              : 
     750            7 :     qs = TALER_MERCHANTDB_get_order (
     751              :       TMH_db,
     752            7 :       god->hc->instance->settings.id,
     753              :       god->order_id,
     754              :       &db_claim_token,
     755              :       &unused,
     756              :       &god->order_json);
     757            7 :     if (0 > qs)
     758              :     {
     759              :       /* single, read-only SQL statements should never cause
     760              :          serialization problems */
     761            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
     762              :       /* Always report on hard error as well to enable diagnostics */
     763            0 :       GNUNET_break (0);
     764            0 :       phase_fail (god,
     765              :                   MHD_HTTP_INTERNAL_SERVER_ERROR,
     766              :                   TALER_EC_GENERIC_DB_FETCH_FAILED,
     767              :                   "get_order");
     768            0 :       return;
     769              :     }
     770            7 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     771              :     {
     772            0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     773              :                   "Unknown order id given: `%s'\n",
     774              :                   god->order_id);
     775            0 :       phase_fail (god,
     776              :                   MHD_HTTP_NOT_FOUND,
     777              :                   TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
     778              :                   god->order_id);
     779            0 :       return;
     780              :     }
     781              :     /* Note: when "!ord.requireClaimToken" and the client does not provide
     782              :        a claim token (all zeros!), then token_match==TRUE below: */
     783              :     god->token_match
     784           14 :       = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
     785           14 :         (0 == GNUNET_memcmp (&db_claim_token,
     786              :                              &god->claim_token));
     787              :   } /* end unclaimed order logic */
     788           49 :   god->phase++;
     789              : }
     790              : 
     791              : 
     792              : /**
     793              :  * Parse contract terms.
     794              :  *
     795              :  * @param[in,out] god request context
     796              :  */
     797              : static void
     798           49 : phase_parse_contract (struct GetOrderData *god)
     799              : {
     800           49 :   GNUNET_break (NULL == god->contract_terms);
     801           49 :   GNUNET_break (NULL == god->order);
     802           49 :   if (NULL != god->contract_terms_json)
     803              :   {
     804           42 :     if (NULL ==
     805           42 :         json_object_get (god->contract_terms_json,
     806              :                          "nonce"))
     807              :     {
     808            0 :       god->pc = TALER_MERCHANT_proto_contract_parse (
     809              :         god->contract_terms_json);
     810            0 :       if (NULL == god->pc)
     811              :       {
     812            0 :         phase_fail (god,
     813              :                     MHD_HTTP_INTERNAL_SERVER_ERROR,
     814              :                     TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
     815              :                     god->order_id);
     816            0 :         return;
     817              :       }
     818            0 :       god->ct = god->pc->base;
     819              :     }
     820              :     else
     821              :     {
     822           42 :       god->contract_terms = TALER_MERCHANT_contract_parse (
     823              :         god->contract_terms_json);
     824           42 :       if (NULL == god->contract_terms)
     825              :       {
     826            0 :         phase_fail (god,
     827              :                     MHD_HTTP_INTERNAL_SERVER_ERROR,
     828              :                     TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
     829              :                     god->order_id);
     830            0 :         return;
     831              :       }
     832           42 :       god->pc = god->contract_terms->pc;
     833           42 :       god->ct = god->contract_terms->pc->base;
     834              :     }
     835              :   }
     836           49 :   if (NULL != god->order_json)
     837              :   {
     838            7 :     god->order = TALER_MERCHANT_order_parse (
     839              :       god->order_json);
     840            7 :     if (NULL == god->order)
     841              :     {
     842            0 :       phase_fail (god,
     843              :                   MHD_HTTP_INTERNAL_SERVER_ERROR,
     844              :                   TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
     845              :                   god->order_id);
     846            0 :       return;
     847              :     }
     848            7 :     god->order_parsed = true;
     849            7 :     god->ct = god->order->base;
     850              :   }
     851           49 :   GNUNET_assert ( (NULL != god->order) ||
     852              :                   (NULL != god->pc) );
     853              : 
     854           49 :   if ( (NULL != god->session_id) &&
     855           10 :        (NULL != god->ct->fulfillment_url) &&
     856           10 :        (NULL == god->session_eh) )
     857              :   {
     858            8 :     struct TMH_SessionEventP session_eh = {
     859            8 :       .header.size = htons (sizeof (session_eh)),
     860            8 :       .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED),
     861            8 :       .merchant_pub = god->hc->instance->merchant_pub
     862              :     };
     863              : 
     864            8 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     865              :                 "Subscribing to session triggers for %p\n",
     866              :                 god);
     867            8 :     GNUNET_CRYPTO_hash (god->session_id,
     868              :                         strlen (god->session_id),
     869              :                         &session_eh.h_session_id);
     870            8 :     GNUNET_CRYPTO_hash (god->ct->fulfillment_url,
     871            8 :                         strlen (god->ct->fulfillment_url),
     872              :                         &session_eh.h_fulfillment_url);
     873              :     god->session_eh
     874            8 :       = TALER_MERCHANTDB_event_listen (
     875              :           TMH_db,
     876              :           &session_eh.header,
     877              :           GNUNET_TIME_absolute_get_remaining (god->sc.long_poll_timeout),
     878              :           &resume_by_event,
     879              :           god);
     880              :   }
     881           49 :   god->phase++;
     882              : }
     883              : 
     884              : 
     885              : /**
     886              :  * Check that this order is unclaimed or claimed by
     887              :  * this client.
     888              :  *
     889              :  * @param[in,out] god request context
     890              :  */
     891              : static void
     892           49 : phase_check_client_access (struct GetOrderData *god)
     893              : {
     894           49 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     895              :               "Token match: %d, contract_available: %d, contract match: %d, claimed: %d\n",
     896              :               god->token_match,
     897              :               god->contract_available,
     898              :               god->contract_match,
     899              :               god->claimed);
     900              : 
     901           49 :   if (god->claim_token_provided && ! god->token_match)
     902              :   {
     903              :     /* Authentication provided but wrong. */
     904            0 :     GNUNET_break_op (0);
     905            0 :     phase_fail (god,
     906              :                 MHD_HTTP_FORBIDDEN,
     907              :                 TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_TOKEN,
     908              :                 "authentication with claim token provided but wrong");
     909            0 :     return;
     910              :   }
     911              : 
     912           49 :   if (god->h_contract_provided && ! god->contract_match)
     913              :   {
     914              :     /* Authentication provided but wrong. */
     915            0 :     GNUNET_break_op (0);
     916            0 :     phase_fail (god,
     917              :                 MHD_HTTP_FORBIDDEN,
     918              :                 TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_HASH,
     919              :                 NULL);
     920            0 :     return;
     921              :   }
     922              : 
     923           49 :   if (! (god->token_match ||
     924           39 :          god->contract_match) )
     925              :   {
     926            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     927              :                 "Neither claim token nor contract matched\n");
     928              :     /* Client has no rights to this order */
     929            0 :     if (NULL == god->ct->public_reorder_url)
     930              :     {
     931              :       /* We cannot give the client a new order, just fail */
     932            0 :       if (! GNUNET_is_zero (&god->h_contract_terms))
     933              :       {
     934            0 :         GNUNET_break_op (0);
     935            0 :         phase_fail (god,
     936              :                     MHD_HTTP_FORBIDDEN,
     937              :                     TALER_EC_MERCHANT_GENERIC_CONTRACT_HASH_DOES_NOT_MATCH_ORDER,
     938              :                     NULL);
     939            0 :         return;
     940              :       }
     941            0 :       GNUNET_break_op (0);
     942            0 :       phase_fail (god,
     943              :                   MHD_HTTP_FORBIDDEN,
     944              :                   TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_TOKEN,
     945              :                   "no 'public_reorder_url'");
     946            0 :       return;
     947              :     }
     948              :     /* We have a fulfillment URL, redirect the client there, maybe
     949              :        the frontend can generate a fresh order for this new customer */
     950            0 :     if (god->generate_html)
     951              :     {
     952              :       /* Contract was claimed (maybe by another device), so this client
     953              :          cannot get the status information. Redirect to fulfillment page,
     954              :          where the client may be able to pickup a fresh order -- or might
     955              :          be able authenticate via session ID */
     956              :       struct MHD_Response *reply;
     957              :       enum MHD_Result ret;
     958              : 
     959            0 :       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     960              :                   "Contract claimed, redirecting to fulfillment page for order %s\n",
     961              :                   god->order_id);
     962            0 :       reply = MHD_create_response_from_buffer (0,
     963              :                                                NULL,
     964              :                                                MHD_RESPMEM_PERSISTENT);
     965            0 :       if (NULL == reply)
     966              :       {
     967            0 :         GNUNET_break (0);
     968            0 :         phase_end (god,
     969              :                    MHD_NO);
     970            0 :         return;
     971              :       }
     972            0 :       GNUNET_break (MHD_YES ==
     973              :                     MHD_add_response_header (
     974              :                       reply,
     975              :                       MHD_HTTP_HEADER_LOCATION,
     976              :                       god->ct->public_reorder_url));
     977            0 :       ret = MHD_queue_response (god->sc.con,
     978              :                                 MHD_HTTP_FOUND,
     979              :                                 reply);
     980            0 :       MHD_destroy_response (reply);
     981            0 :       phase_end (god,
     982              :                  ret);
     983            0 :       return;
     984              :     }
     985              :     /* Need to generate JSON reply */
     986            0 :     phase_end (god,
     987            0 :                TALER_MHD_REPLY_JSON_PACK (
     988              :                  god->sc.con,
     989              :                  MHD_HTTP_ACCEPTED,
     990              :                  GNUNET_JSON_pack_string (
     991              :                    "public_reorder_url",
     992              :                    god->ct->public_reorder_url)));
     993            0 :     return;
     994              :   }
     995           49 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     996              :               "Claim token or contract matched\n");
     997           49 :   god->phase++;
     998              : }
     999              : 
    1000              : 
    1001              : /**
    1002              :  * Return the order summary of the contract of @a god in the
    1003              :  * preferred language of the HTTP client.
    1004              :  *
    1005              :  * @param god order to extract summary from
    1006              :  * @return dummy error message summary if no summary was provided in the contract
    1007              :  */
    1008              : static const char *
    1009            5 : get_order_summary (const struct GetOrderData *god)
    1010              : {
    1011              :   const char *language_pattern;
    1012              :   const char *ret;
    1013              :   json_t *terms;
    1014              : 
    1015            5 :   if (NULL != god->contract_terms_json)
    1016            3 :     terms = god->contract_terms_json;
    1017              :   else
    1018            2 :     terms = god->order_json;
    1019            5 :   language_pattern = MHD_lookup_connection_value (
    1020            5 :     god->sc.con,
    1021              :     MHD_HEADER_KIND,
    1022              :     MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
    1023            5 :   if (NULL == language_pattern)
    1024            5 :     language_pattern = "en";
    1025            5 :   ret = json_string_value (TALER_JSON_extract_i18n (
    1026              :                              terms,
    1027              :                              language_pattern,
    1028              :                              "summary"));
    1029            5 :   if (NULL == ret)
    1030              :   {
    1031              :     /* Upon order creation (and insertion into the database), the presence
    1032              :        of a summary should have been checked. So if we get here, someone
    1033              :        did something fishy to our database... */
    1034            0 :     GNUNET_break (0);
    1035            0 :     ret = "<bug: no summary>";
    1036              :   }
    1037            5 :   return ret;
    1038              : }
    1039              : 
    1040              : 
    1041              : /**
    1042              :  * Return the localized fulfillment message of the contract of @a god.
    1043              :  *
    1044              :  * @param god order to extract the fulfillment message from
    1045              :  * @return localized message, or NULL if no message was provided
    1046              :  */
    1047              : static const char *
    1048            1 : get_fulfillment_message (const struct GetOrderData *god)
    1049              : {
    1050              :   const char *language_pattern;
    1051              :   const char *ret;
    1052              :   json_t *terms;
    1053              : 
    1054            1 :   if (NULL != god->contract_terms_json)
    1055            1 :     terms = god->contract_terms_json;
    1056              :   else
    1057            0 :     terms = god->order_json;
    1058            1 :   language_pattern = MHD_lookup_connection_value (
    1059            1 :     god->sc.con,
    1060              :     MHD_HEADER_KIND,
    1061              :     MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
    1062            1 :   if (NULL == language_pattern)
    1063            1 :     language_pattern = "en";
    1064            1 :   ret = json_string_value (TALER_JSON_extract_i18n (
    1065              :                              terms,
    1066              :                              language_pattern,
    1067              :                              "fulfillment_message"));
    1068            1 :   if (NULL == ret)
    1069            1 :     ret = god->ct->fulfillment_message;
    1070            1 :   return ret;
    1071              : }
    1072              : 
    1073              : 
    1074              : /**
    1075              :  * Return the amount to display for the order. Before a version 1 order with
    1076              :  * multiple choices is claimed, the exact amount is selected by the wallet.
    1077              :  *
    1078              :  * @param god order to extract the amount from
    1079              :  * @param[out] multiple_payment_options set if the wallet selects the amount
    1080              :  * @return order amount, or NULL if no single amount is available
    1081              :  */
    1082              : static const struct TALER_Amount *
    1083            4 : get_order_amount (const struct GetOrderData *god,
    1084              :                   bool *multiple_payment_options)
    1085              : {
    1086            4 :   *multiple_payment_options = false;
    1087            4 :   switch (god->ct->version)
    1088              :   {
    1089            2 :   case TALER_MERCHANT_CONTRACT_VERSION_0:
    1090            2 :     return (NULL != god->pc)
    1091            1 :       ? &god->pc->details.v0.brutto
    1092            2 :       : &god->order->details.v0.brutto;
    1093            2 :   case TALER_MERCHANT_CONTRACT_VERSION_1:
    1094              :     {
    1095            4 :       unsigned int choices_len = (NULL != god->pc)
    1096            1 :         ? god->pc->details.v1.choices_len
    1097            2 :         : god->order->details.v1.choices_len;
    1098              : 
    1099            2 :       if (god->choice_index >= 0)
    1100              :       {
    1101            0 :         if (god->choice_index >= choices_len)
    1102              :         {
    1103            0 :           GNUNET_break (0);
    1104            0 :           return NULL;
    1105              :         }
    1106            0 :         return (NULL != god->pc)
    1107            0 :           ? &god->pc->details.v1.choices[god->choice_index].amount
    1108            0 :           : &god->order->details.v1.choices[god->choice_index].amount;
    1109              :       }
    1110            2 :       if (1 == choices_len)
    1111            0 :         return (NULL != god->pc)
    1112            0 :           ? &god->pc->details.v1.choices[0].amount
    1113            0 :           : &god->order->details.v1.choices[0].amount;
    1114            2 :       *multiple_payment_options = true;
    1115            2 :       return NULL;
    1116              :     }
    1117            0 :   default:
    1118            0 :     GNUNET_break (0);
    1119            0 :     return NULL;
    1120              :   }
    1121              : }
    1122              : 
    1123              : 
    1124              : /**
    1125              :  * The client did not yet pay, send it the payment request.
    1126              :  *
    1127              :  * @param god check pay request context
    1128              :  * @param already_paid_order_id if for the fulfillment URI there is
    1129              :  *          already a paid order, this is the order ID to redirect
    1130              :  *          the wallet to; NULL if not applicable
    1131              :  * @return true to exit due to suspension
    1132              :  */
    1133              : static bool
    1134           25 : send_pay_request (struct GetOrderData *god,
    1135              :                   const char *already_paid_order_id)
    1136              : {
    1137              :   enum MHD_Result ret;
    1138              :   char *taler_pay_uri;
    1139              :   char *order_status_url;
    1140              :   struct GNUNET_TIME_Relative remaining;
    1141              : 
    1142           25 :   remaining = GNUNET_TIME_absolute_get_remaining (god->sc.long_poll_timeout);
    1143           25 :   if ( (! GNUNET_TIME_relative_is_zero (remaining)) &&
    1144            8 :        (! god->generate_html) &&
    1145              :        (NULL == already_paid_order_id) )
    1146              :   {
    1147              :     /* long polling: do not queue a response, suspend connection instead */
    1148            8 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1149              :                 "Suspending request: long polling for payment\n");
    1150            8 :     suspend_god (god);
    1151            8 :     return true;
    1152              :   }
    1153              :   /* Check if resource_id has been paid for in the same session
    1154              :    * with another order_id.
    1155              :    */
    1156           17 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1157              :               "Sending payment request\n");
    1158           17 :   taler_pay_uri = TMH_make_taler_pay_uri (
    1159              :     god->sc.con,
    1160              :     god->order_id,
    1161              :     god->session_id,
    1162           17 :     god->hc->instance->settings.id,
    1163              :     &god->claim_token);
    1164           17 :   order_status_url = TMH_make_order_status_url (
    1165              :     god->sc.con,
    1166              :     god->order_id,
    1167              :     god->session_id,
    1168           17 :     god->hc->instance->settings.id,
    1169              :     &god->claim_token,
    1170              :     NULL);
    1171           17 :   if ( (NULL == taler_pay_uri) ||
    1172              :        (NULL == order_status_url) )
    1173              :   {
    1174            0 :     GNUNET_break_op (0);
    1175            0 :     GNUNET_free (taler_pay_uri);
    1176            0 :     GNUNET_free (order_status_url);
    1177            0 :     phase_fail (god,
    1178              :                 MHD_HTTP_BAD_REQUEST,
    1179              :                 TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
    1180              :                 "host");
    1181            0 :     return false;
    1182              :   }
    1183           17 :   if (god->generate_html)
    1184              :   {
    1185            3 :     if (NULL != already_paid_order_id)
    1186              :     {
    1187              :       struct MHD_Response *reply;
    1188              : 
    1189            0 :       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1190              :                   "Redirecting to already paid order %s via fulfillment URL %s\n",
    1191              :                   already_paid_order_id,
    1192              :                   god->ct->fulfillment_url);
    1193            0 :       reply = MHD_create_response_from_buffer (0,
    1194              :                                                NULL,
    1195              :                                                MHD_RESPMEM_PERSISTENT);
    1196            0 :       if (NULL == reply)
    1197              :       {
    1198            0 :         GNUNET_break (0);
    1199            0 :         phase_end (god,
    1200              :                    MHD_NO);
    1201            0 :         return false;
    1202              :       }
    1203            0 :       GNUNET_break (MHD_YES ==
    1204              :                     MHD_add_response_header (
    1205              :                       reply,
    1206              :                       MHD_HTTP_HEADER_LOCATION,
    1207              :                       god->ct->fulfillment_url));
    1208              :       {
    1209            0 :         ret = MHD_queue_response (god->sc.con,
    1210              :                                   MHD_HTTP_FOUND,
    1211              :                                   reply);
    1212            0 :         MHD_destroy_response (reply);
    1213            0 :         phase_end (god,
    1214              :                    ret);
    1215            0 :         return false;
    1216              :       }
    1217              :     }
    1218              : 
    1219              :     {
    1220              :       char *qr;
    1221              :       bool multiple_payment_options;
    1222              :       const struct TALER_Amount *order_amount;
    1223              :       struct GNUNET_TIME_Timestamp pay_deadline;
    1224              : 
    1225            3 :       order_amount = get_order_amount (god,
    1226              :                                        &multiple_payment_options);
    1227            6 :       pay_deadline = (NULL != god->pc)
    1228            1 :         ? god->pc->pay_deadline
    1229            3 :         : god->order->pay_deadline;
    1230              : 
    1231            3 :       qr = TMH_create_qrcode (taler_pay_uri);
    1232            3 :       if (NULL == qr)
    1233              :       {
    1234            0 :         GNUNET_break (0);
    1235            0 :         phase_end (god,
    1236              :                    MHD_NO);
    1237            0 :         return false;
    1238              :       }
    1239              :       {
    1240              :         enum GNUNET_GenericReturnValue res;
    1241              :         json_t *context;
    1242              : 
    1243            3 :         context = GNUNET_JSON_PACK (
    1244              :           GNUNET_JSON_pack_string ("taler_pay_uri",
    1245              :                                    taler_pay_uri),
    1246              :           GNUNET_JSON_pack_string ("order_status_url",
    1247              :                                    order_status_url),
    1248              :           GNUNET_JSON_pack_string ("taler_pay_qrcode_svg",
    1249              :                                    qr),
    1250              :           GNUNET_JSON_pack_string ("order_summary",
    1251              :                                    get_order_summary (god)),
    1252              :           GNUNET_JSON_pack_string ("order_id",
    1253              :                                    god->order_id),
    1254              :           GNUNET_JSON_pack_string ("merchant_name",
    1255              :                                    god->hc->instance->settings.name),
    1256              :           GNUNET_JSON_pack_allow_null (
    1257              :             TALER_JSON_pack_amount ("order_amount",
    1258              :                                     order_amount)),
    1259              :           GNUNET_JSON_pack_bool ("multiple_payment_options",
    1260              :                                  multiple_payment_options),
    1261              :           GNUNET_JSON_pack_bool ("claimed",
    1262              :                                  god->claimed),
    1263              :           GNUNET_JSON_pack_timestamp ("pay_deadline",
    1264              :                                       pay_deadline));
    1265            3 :         res = TALER_TEMPLATING_reply (
    1266              :           god->sc.con,
    1267              :           MHD_HTTP_PAYMENT_REQUIRED,
    1268              :           "request_payment",
    1269            3 :           god->hc->instance->settings.id,
    1270              :           taler_pay_uri,
    1271              :           context);
    1272            3 :         if (GNUNET_SYSERR == res)
    1273              :         {
    1274            0 :           GNUNET_break (0);
    1275            0 :           ret = MHD_NO;
    1276              :         }
    1277              :         else
    1278              :         {
    1279            3 :           ret = MHD_YES;
    1280              :         }
    1281            3 :         json_decref (context);
    1282              :       }
    1283            3 :       GNUNET_free (qr);
    1284              :     }
    1285              :   }
    1286              :   else /* end of 'generate HTML' */
    1287              :   {
    1288           14 :     ret = TALER_MHD_REPLY_JSON_PACK (
    1289              :       god->sc.con,
    1290              :       MHD_HTTP_PAYMENT_REQUIRED,
    1291              :       GNUNET_JSON_pack_string ("taler_pay_uri",
    1292              :                                taler_pay_uri),
    1293              :       GNUNET_JSON_pack_allow_null (
    1294              :         GNUNET_JSON_pack_string ("fulfillment_url",
    1295              :                                  god->ct->fulfillment_url)),
    1296              :       GNUNET_JSON_pack_allow_null (
    1297              :         GNUNET_JSON_pack_string ("already_paid_order_id",
    1298              :                                  already_paid_order_id)));
    1299              :   }
    1300           17 :   GNUNET_free (taler_pay_uri);
    1301           17 :   GNUNET_free (order_status_url);
    1302           17 :   phase_end (god,
    1303              :              ret);
    1304           17 :   return false;
    1305              : }
    1306              : 
    1307              : 
    1308              : /**
    1309              :  * Check if the order has been paid.
    1310              :  *
    1311              :  * @param[in,out] god request context
    1312              :  */
    1313              : static void
    1314           49 : phase_check_paid (struct GetOrderData *god)
    1315              : {
    1316              :   enum GNUNET_DB_QueryStatus qs;
    1317              :   struct TALER_PrivateContractHashP h_contract;
    1318              : 
    1319           49 :   god->paid = false;
    1320           49 :   qs = TALER_MERCHANTDB_get_order_status (
    1321              :     TMH_db,
    1322           49 :     god->hc->instance->settings.id,
    1323              :     god->order_id,
    1324              :     &h_contract,
    1325              :     &god->paid);
    1326           49 :   if (0 > qs)
    1327              :   {
    1328              :     /* Always report on hard error as well to enable diagnostics */
    1329            0 :     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
    1330            0 :     phase_fail (god,
    1331              :                 MHD_HTTP_INTERNAL_SERVER_ERROR,
    1332              :                 TALER_EC_GENERIC_DB_FETCH_FAILED,
    1333              :                 "get_order_status");
    1334            0 :     return;
    1335              :   }
    1336           49 :   god->phase++;
    1337              : }
    1338              : 
    1339              : 
    1340              : /**
    1341              :  * Check if the client already paid for an equivalent
    1342              :  * order under this session, and if so redirect to
    1343              :  * that order.
    1344              :  *
    1345              :  * @param[in,out] god request context
    1346              :  * @return true to exit due to suspension
    1347              :  */
    1348              : static bool
    1349           49 : phase_redirect_to_paid_order (struct GetOrderData *god)
    1350              : {
    1351           49 :   if ( (NULL != god->session_id) &&
    1352           10 :        (NULL != god->ct->fulfillment_url) )
    1353              :   {
    1354              :     /* Check if client paid for this fulfillment article
    1355              :        already within this session, but using a different
    1356              :        order ID. If so, redirect the client to the order
    1357              :        it already paid.  Allows, for example, the case
    1358              :        where a mobile phone pays for a browser's session,
    1359              :        where the mobile phone has a different order
    1360              :        ID (because it purchased the article earlier)
    1361              :        than the one that the browser is waiting for. */
    1362           10 :     char *already_paid_order_id = NULL;
    1363              :     enum GNUNET_DB_QueryStatus qs;
    1364              : 
    1365           10 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1366              :                 "Running re-purchase detection for %s/%s\n",
    1367              :                 god->session_id,
    1368              :                 god->ct->fulfillment_url);
    1369           10 :     qs = TALER_MERCHANTDB_get_order_by_fulfillment (
    1370              :       TMH_db,
    1371           10 :       god->hc->instance->settings.id,
    1372           10 :       god->ct->fulfillment_url,
    1373              :       god->session_id,
    1374           10 :       TALER_EXCHANGE_YNA_NO != god->allow_refunded_for_repurchase,
    1375              :       &already_paid_order_id);
    1376           10 :     if (qs < 0)
    1377              :     {
    1378              :       /* single, read-only SQL statements should never cause
    1379              :          serialization problems */
    1380            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
    1381              :       /* Always report on hard error as well to enable diagnostics */
    1382            0 :       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
    1383            0 :       phase_fail (god,
    1384              :                   MHD_HTTP_INTERNAL_SERVER_ERROR,
    1385              :                   TALER_EC_GENERIC_DB_FETCH_FAILED,
    1386              :                   "order by fulfillment");
    1387            8 :       return false;
    1388              :     }
    1389           10 :     if ( (! god->paid) &&
    1390            4 :          ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
    1391            4 :            (0 != strcmp (god->order_id,
    1392              :                          already_paid_order_id)) ) )
    1393              :     {
    1394              :       bool ret;
    1395              : 
    1396            8 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1397              :                   "Sending pay request for order %s (already paid: %s)\n",
    1398              :                   god->order_id,
    1399              :                   already_paid_order_id);
    1400            8 :       ret = send_pay_request (god,
    1401              :                               already_paid_order_id);
    1402            8 :       GNUNET_free (already_paid_order_id);
    1403            8 :       return ret;
    1404              :     }
    1405            2 :     GNUNET_free (already_paid_order_id);
    1406              :   }
    1407           41 :   god->phase++;
    1408           41 :   return false;
    1409              : }
    1410              : 
    1411              : 
    1412              : /**
    1413              :  * Check if the order has been paid, and if not
    1414              :  * request payment.
    1415              :  *
    1416              :  * @param[in,out] god request context
    1417              :  * @return true to exit due to suspension
    1418              :  */
    1419              : static bool
    1420           41 : phase_handle_unpaid (struct GetOrderData *god)
    1421              : {
    1422           41 :   if (god->paid)
    1423              :   {
    1424           24 :     god->phase++;
    1425           24 :     return false;
    1426              :   }
    1427           17 :   if (god->claimed)
    1428              :   {
    1429           10 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1430              :                 "Order claimed but unpaid, sending pay request for order %s\n",
    1431              :                 god->order_id);
    1432              :   }
    1433              :   else
    1434              :   {
    1435            7 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1436              :                 "Order unclaimed, sending pay request for order %s\n",
    1437              :                 god->order_id);
    1438              :   }
    1439           17 :   return send_pay_request (god,
    1440              :                            NULL);
    1441              : }
    1442              : 
    1443              : 
    1444              : /**
    1445              :  * Function called with detailed information about a refund.
    1446              :  * It is responsible for packing up the data to return.
    1447              :  *
    1448              :  * @param cls closure
    1449              :  * @param refund_serial unique serial number of the refund
    1450              :  * @param timestamp time of the refund (for grouping of refunds in the wallet UI)
    1451              :  * @param coin_pub public coin from which the refund comes from
    1452              :  * @param exchange_url URL of the exchange that issued @a coin_pub
    1453              :  * @param rtransaction_id identificator of the refund
    1454              :  * @param reason human-readable explanation of the refund
    1455              :  * @param refund_amount refund amount which is being taken from @a coin_pub
    1456              :  * @param pending true if the this refund was not yet processed by the wallet/exchange
    1457              :  */
    1458              : static void
    1459           22 : process_refunds_cb (void *cls,
    1460              :                     uint64_t refund_serial,
    1461              :                     struct GNUNET_TIME_Timestamp timestamp,
    1462              :                     const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1463              :                     const char *exchange_url,
    1464              :                     uint64_t rtransaction_id,
    1465              :                     const char *reason,
    1466              :                     const struct TALER_Amount *refund_amount,
    1467              :                     bool pending)
    1468              : {
    1469           22 :   struct GetOrderData *god = cls;
    1470              : 
    1471              :   (void) refund_serial;
    1472              :   (void) timestamp;
    1473              :   (void) exchange_url;
    1474              :   (void) rtransaction_id;
    1475           22 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1476              :               "Found refund of %s for coin %s with reason `%s' in database\n",
    1477              :               TALER_amount2s (refund_amount),
    1478              :               TALER_B2S (coin_pub),
    1479              :               reason);
    1480           22 :   god->refund_pending |= pending;
    1481           22 :   if ( (GNUNET_OK !=
    1482           22 :         TALER_amount_cmp_currency (&god->refund_taken,
    1483           22 :                                    refund_amount)) ||
    1484              :        (GNUNET_OK !=
    1485           22 :         TALER_amount_cmp_currency (&god->refund_amount,
    1486              :                                    refund_amount)) )
    1487              :   {
    1488            0 :     god->bad_refund_currency_in_db = true;
    1489            0 :     return;
    1490              :   }
    1491           22 :   if (! pending)
    1492              :   {
    1493            4 :     GNUNET_assert (0 <=
    1494              :                    TALER_amount_add (&god->refund_taken,
    1495              :                                      &god->refund_taken,
    1496              :                                      refund_amount));
    1497              :   }
    1498           22 :   GNUNET_assert (0 <=
    1499              :                  TALER_amount_add (&god->refund_amount,
    1500              :                                    &god->refund_amount,
    1501              :                                    refund_amount));
    1502           22 :   god->refunded = true;
    1503              : }
    1504              : 
    1505              : 
    1506              : /**
    1507              :  * Check if the order has been refunded.
    1508              :  *
    1509              :  * @param[in,out] god request context
    1510              :  * @return true to exit due to suspension
    1511              :  */
    1512              : static bool
    1513           24 : phase_check_refunded (struct GetOrderData *god)
    1514              : {
    1515              :   enum GNUNET_DB_QueryStatus qs;
    1516              :   struct TALER_Amount refund_amount;
    1517              :   const char *refund_currency;
    1518              : 
    1519           24 :   GNUNET_assert (NULL != god->contract_terms);
    1520           24 :   switch (god->ct->version)
    1521              :   {
    1522           24 :   case TALER_MERCHANT_CONTRACT_VERSION_0:
    1523           24 :     refund_amount = god->pc->details.v0.brutto;
    1524           24 :     refund_currency = god->pc->details.v0.brutto.currency;
    1525           24 :     break;
    1526            0 :   case TALER_MERCHANT_CONTRACT_VERSION_1:
    1527            0 :     if (god->choice_index < 0)
    1528              :     {
    1529              :       // order was not paid, no refund to be checked
    1530            0 :       god->phase++;
    1531            0 :       return false;
    1532              :     }
    1533            0 :     GNUNET_assert (god->choice_index <
    1534              :                    god->pc->details.v1.choices_len);
    1535            0 :     refund_currency = god->pc->details.v1.choices[
    1536            0 :       god->choice_index].amount.currency;
    1537            0 :     GNUNET_assert (GNUNET_OK ==
    1538              :                    TALER_amount_set_zero (refund_currency,
    1539              :                                           &refund_amount));
    1540            0 :     break;
    1541            0 :   default:
    1542              :     {
    1543            0 :       GNUNET_break (0);
    1544            0 :       phase_fail (god,
    1545              :                   MHD_HTTP_INTERNAL_SERVER_ERROR,
    1546              :                   TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_VERSION,
    1547              :                   NULL);
    1548            0 :       return false;
    1549              :     }
    1550              :   }
    1551              : 
    1552           30 :   if ( (god->sc.awaiting_refund) &&
    1553              :        (GNUNET_OK !=
    1554            6 :         TALER_amount_cmp_currency (&refund_amount,
    1555            6 :                                    &god->sc.refund_expected)) )
    1556              :   {
    1557            0 :     GNUNET_break (0);
    1558            0 :     phase_fail (god,
    1559              :                 MHD_HTTP_CONFLICT,
    1560              :                 TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
    1561              :                 refund_currency);
    1562            0 :     return false;
    1563              :   }
    1564              : 
    1565              :   /* At this point, we know the contract was paid. Let's check for
    1566              :      refunds. First, clear away refunds found from previous invocations. */
    1567           24 :   GNUNET_assert (GNUNET_OK ==
    1568              :                  TALER_amount_set_zero (refund_currency,
    1569              :                                         &god->refund_amount));
    1570           24 :   GNUNET_assert (GNUNET_OK ==
    1571              :                  TALER_amount_set_zero (refund_currency,
    1572              :                                         &god->refund_taken));
    1573           24 :   qs = TALER_MERCHANTDB_iterate_refunds_detailed (
    1574              :     TMH_db,
    1575           24 :     god->hc->instance->settings.id,
    1576           24 :     &god->h_contract_terms,
    1577              :     &process_refunds_cb,
    1578              :     god);
    1579           24 :   if (0 > qs)
    1580              :   {
    1581            0 :     GNUNET_break (0);
    1582            0 :     phase_fail (god,
    1583              :                 MHD_HTTP_INTERNAL_SERVER_ERROR,
    1584              :                 TALER_EC_GENERIC_DB_FETCH_FAILED,
    1585              :                 "iterate_refunds_detailed");
    1586            0 :     return false;
    1587              :   }
    1588           24 :   if (god->bad_refund_currency_in_db)
    1589              :   {
    1590            0 :     GNUNET_break (0);
    1591            0 :     phase_fail (god,
    1592              :                 MHD_HTTP_INTERNAL_SERVER_ERROR,
    1593              :                 TALER_EC_GENERIC_DB_FETCH_FAILED,
    1594              :                 "currency mix-up between contract price and refunds in database");
    1595            0 :     return false;
    1596              :   }
    1597           24 :   if ( ((god->sc.awaiting_refund) &&
    1598           11 :         ( (! god->refunded) ||
    1599            5 :           (1 != TALER_amount_cmp (&god->refund_amount,
    1600            5 :                                   &god->sc.refund_expected)) )) ||
    1601           23 :        ( (god->sc.awaiting_refund_obtained) &&
    1602            0 :          (god->refund_pending) ) )
    1603              :   {
    1604              :     /* Client is waiting for a refund larger than what we have, suspend
    1605              :        until timeout */
    1606              :     struct GNUNET_TIME_Relative remaining;
    1607              : 
    1608            1 :     remaining = GNUNET_TIME_absolute_get_remaining (god->sc.long_poll_timeout);
    1609            1 :     if ( (! GNUNET_TIME_relative_is_zero (remaining)) &&
    1610            1 :          (! god->generate_html) )
    1611              :     {
    1612              :       /* yes, indeed suspend */
    1613            1 :       if (god->sc.awaiting_refund)
    1614            1 :         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1615              :                     "Awaiting refund exceeding %s\n",
    1616              :                     TALER_amount2s (&god->sc.refund_expected));
    1617            1 :       if (god->sc.awaiting_refund_obtained)
    1618            0 :         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1619              :                     "Awaiting pending refunds\n");
    1620            1 :       suspend_god (god);
    1621            1 :       return true;
    1622              :     }
    1623              :   }
    1624           23 :   god->phase++;
    1625           23 :   return false;
    1626              : }
    1627              : 
    1628              : 
    1629              : /**
    1630              :  * Create a taler://refund/ URI for the given @a con and @a order_id
    1631              :  * and @a instance_id.
    1632              :  *
    1633              :  * @param merchant_base_url URL to take host and path from;
    1634              :  *        we cannot take it from the MHD connection as a browser
    1635              :  *        may have changed 'http' to 'https' and we MUST be consistent
    1636              :  *        with what the merchant's frontend used initially
    1637              :  * @param order_id the order id
    1638              :  * @return corresponding taler://refund/ URI, or NULL on missing "host"
    1639              :  */
    1640              : static char *
    1641            1 : make_taler_refund_uri (const char *merchant_base_url,
    1642              :                        const char *order_id)
    1643              : {
    1644            1 :   struct GNUNET_Buffer buf = { 0 };
    1645              :   char *url;
    1646              :   struct GNUNET_Uri uri;
    1647              : 
    1648            1 :   url = GNUNET_strdup (merchant_base_url);
    1649            1 :   if (-1 == GNUNET_uri_parse (&uri,
    1650              :                               url))
    1651              :   {
    1652            0 :     GNUNET_break (0);
    1653            0 :     GNUNET_free (url);
    1654            0 :     return NULL;
    1655              :   }
    1656            1 :   GNUNET_assert (NULL != order_id);
    1657            1 :   GNUNET_buffer_write_str (&buf,
    1658              :                            "taler");
    1659            1 :   if (0 == strcasecmp ("http",
    1660            1 :                        uri.scheme))
    1661            1 :     GNUNET_buffer_write_str (&buf,
    1662              :                              "+http");
    1663            1 :   GNUNET_buffer_write_str (&buf,
    1664              :                            "://refund/");
    1665            1 :   GNUNET_buffer_write_str (&buf,
    1666            1 :                            uri.host);
    1667            1 :   if (0 != uri.port)
    1668            1 :     GNUNET_buffer_write_fstr (&buf,
    1669              :                               ":%u",
    1670            1 :                               (unsigned int) uri.port);
    1671            1 :   if (NULL != uri.path)
    1672            1 :     GNUNET_buffer_write_path (&buf,
    1673            1 :                               uri.path);
    1674            1 :   GNUNET_buffer_write_path (&buf,
    1675              :                             order_id);
    1676            1 :   GNUNET_buffer_write_path (&buf,
    1677              :                             ""); // Trailing slash
    1678            1 :   GNUNET_free (url);
    1679            1 :   return GNUNET_buffer_reap_str (&buf);
    1680              : }
    1681              : 
    1682              : 
    1683              : /**
    1684              :  * Generate the order status response.
    1685              :  *
    1686              :  * @param[in,out] god request context
    1687              :  */
    1688              : static void
    1689           23 : phase_return_status (struct GetOrderData *god)
    1690              : {
    1691              :   /* All operations done, build final response */
    1692           23 :   if (! god->generate_html)
    1693              :   {
    1694           21 :     phase_end (god,
    1695           21 :                TALER_MHD_REPLY_JSON_PACK (
    1696              :                  god->sc.con,
    1697              :                  MHD_HTTP_OK,
    1698              :                  GNUNET_JSON_pack_allow_null (
    1699              :                    GNUNET_JSON_pack_string ("fulfillment_url",
    1700              :                                             god->ct->fulfillment_url)),
    1701              :                  GNUNET_JSON_pack_bool ("refunded",
    1702              :                                         god->refunded),
    1703              :                  GNUNET_JSON_pack_bool ("refund_pending",
    1704              :                                         god->refund_pending),
    1705              :                  TALER_JSON_pack_amount ("refund_taken",
    1706              :                                          &god->refund_taken),
    1707              :                  TALER_JSON_pack_amount ("refund_amount",
    1708              :                                          &god->refund_amount)));
    1709           21 :     return;
    1710              :   }
    1711              : 
    1712            2 :   if (god->refund_pending)
    1713              :   {
    1714              :     struct TALER_Amount refund_remaining;
    1715              :     char *qr;
    1716              :     char *uri;
    1717              :     char *order_status_url;
    1718              : 
    1719            1 :     GNUNET_assert (NULL != god->contract_terms_json);
    1720            1 :     GNUNET_assert (NULL != god->contract_terms);
    1721            1 :     GNUNET_assert (0 <=
    1722              :                    TALER_amount_subtract (&refund_remaining,
    1723              :                                           &god->refund_amount,
    1724              :                                           &god->refund_taken));
    1725            1 :     uri = make_taler_refund_uri (god->pc->merchant_base_url,
    1726              :                                  god->order_id);
    1727            1 :     order_status_url = TMH_make_order_status_url (
    1728              :       god->sc.con,
    1729              :       god->order_id,
    1730              :       god->session_id,
    1731            1 :       god->hc->instance->settings.id,
    1732              :       NULL,
    1733              :       &god->h_contract_terms);
    1734            1 :     if ( (NULL == uri) ||
    1735              :          (NULL == order_status_url) )
    1736              :     {
    1737            0 :       GNUNET_break (0);
    1738            0 :       GNUNET_free (uri);
    1739            0 :       GNUNET_free (order_status_url);
    1740            0 :       phase_fail (god,
    1741              :                   MHD_HTTP_BAD_REQUEST,
    1742              :                   TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
    1743              :                   "host");
    1744            0 :       return;
    1745              :     }
    1746            1 :     qr = TMH_create_qrcode (uri);
    1747            1 :     if (NULL == qr)
    1748              :     {
    1749            0 :       GNUNET_break (0);
    1750            0 :       GNUNET_free (uri);
    1751            0 :       GNUNET_free (order_status_url);
    1752            0 :       phase_fail (god,
    1753              :                   MHD_HTTP_INTERNAL_SERVER_ERROR,
    1754              :                   TALER_EC_GENERIC_ALLOCATION_FAILURE,
    1755              :                   "qr code");
    1756            0 :       return;
    1757              :     }
    1758              : 
    1759              :     {
    1760              :       enum GNUNET_GenericReturnValue res;
    1761              :       json_t *context;
    1762              : 
    1763            1 :       context = GNUNET_JSON_PACK (
    1764              :         GNUNET_JSON_pack_string ("order_summary",
    1765              :                                  get_order_summary (god)),
    1766              :         TALER_JSON_pack_amount ("refund_amount",
    1767              :                                 &god->refund_amount),
    1768              :         TALER_JSON_pack_amount ("refund_taken",
    1769              :                                 &god->refund_taken),
    1770              :         TALER_JSON_pack_amount ("refund_remaining",
    1771              :                                 &refund_remaining),
    1772              :         GNUNET_JSON_pack_bool ("has_refund_taken",
    1773              :                                ! TALER_amount_is_zero (
    1774              :                                  &god->refund_taken)),
    1775              :         GNUNET_JSON_pack_string ("order_id",
    1776              :                                  god->order_id),
    1777              :         GNUNET_JSON_pack_string ("merchant_name",
    1778              :                                  god->hc->instance->settings.name),
    1779              :         GNUNET_JSON_pack_string ("order_status_url",
    1780              :                                  order_status_url),
    1781              :         GNUNET_JSON_pack_string ("taler_refund_uri",
    1782              :                                  uri),
    1783              :         GNUNET_JSON_pack_string ("taler_refund_qrcode_svg",
    1784              :                                  qr));
    1785            1 :       res = TALER_TEMPLATING_reply (
    1786              :         god->sc.con,
    1787              :         MHD_HTTP_OK,
    1788              :         "offer_refund",
    1789            1 :         god->hc->instance->settings.id,
    1790              :         uri,
    1791              :         context);
    1792            1 :       GNUNET_break (GNUNET_OK == res);
    1793            1 :       json_decref (context);
    1794            1 :       phase_end (god,
    1795              :                  (GNUNET_SYSERR == res)
    1796              :                  ? MHD_NO
    1797              :                  : MHD_YES);
    1798              :     }
    1799            1 :     GNUNET_free (uri);
    1800            1 :     GNUNET_free (order_status_url);
    1801            1 :     GNUNET_free (qr);
    1802            1 :     return;
    1803              :   }
    1804              : 
    1805              :   {
    1806              :     enum GNUNET_GenericReturnValue res;
    1807              :     bool multiple_payment_options;
    1808              :     const struct TALER_Amount *order_amount;
    1809              :     json_t *context;
    1810              :     const char *fulfillment_message;
    1811              : 
    1812            1 :     order_amount = get_order_amount (god,
    1813              :                                      &multiple_payment_options);
    1814            1 :     GNUNET_break (! multiple_payment_options);
    1815            1 :     fulfillment_message = get_fulfillment_message (god);
    1816              : 
    1817            1 :     context = GNUNET_JSON_PACK (
    1818              :       GNUNET_JSON_pack_object_incref ("contract_terms",
    1819              :                                       NULL != god->contract_terms_json
    1820              :                                       ? god->contract_terms_json
    1821              :                                       : god->order_json),
    1822              :       GNUNET_JSON_pack_string ("order_summary",
    1823              :                                get_order_summary (god)),
    1824              :       TALER_JSON_pack_amount ("refund_amount",
    1825              :                               &god->refund_amount),
    1826              :       TALER_JSON_pack_amount ("refund_taken",
    1827              :                               &god->refund_taken),
    1828              :       GNUNET_JSON_pack_allow_null (
    1829              :         TALER_JSON_pack_amount ("order_amount",
    1830              :                                 order_amount)),
    1831              :       GNUNET_JSON_pack_string ("order_id",
    1832              :                                god->order_id),
    1833              :       GNUNET_JSON_pack_string ("merchant_name",
    1834              :                                god->hc->instance->settings.name),
    1835              :       GNUNET_JSON_pack_bool ("has_refund",
    1836              :                              god->refunded),
    1837              :       GNUNET_JSON_pack_allow_null (
    1838              :         GNUNET_JSON_pack_string ("fulfillment_message",
    1839              :                                  fulfillment_message)),
    1840              :       GNUNET_JSON_pack_allow_null (
    1841              :         GNUNET_JSON_pack_string ("fulfillment_url",
    1842              :                                  god->ct->fulfillment_url)));
    1843            1 :     res = TALER_TEMPLATING_reply (
    1844              :       god->sc.con,
    1845              :       MHD_HTTP_OK,
    1846              :       "show_order_details",
    1847            1 :       god->hc->instance->settings.id,
    1848              :       NULL,
    1849              :       context);
    1850            1 :     GNUNET_break (GNUNET_OK == res);
    1851            1 :     json_decref (context);
    1852            1 :     phase_end (god,
    1853              :                (GNUNET_SYSERR == res)
    1854              :                ? MHD_NO
    1855              :                : MHD_YES);
    1856              :   }
    1857              : }
    1858              : 
    1859              : 
    1860              : enum MHD_Result
    1861           49 : TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
    1862              :                    struct MHD_Connection *connection,
    1863              :                    struct TMH_HandlerContext *hc)
    1864              : {
    1865           49 :   struct GetOrderData *god = hc->ctx;
    1866              : 
    1867              :   (void) rh;
    1868           49 :   if (NULL == god)
    1869              :   {
    1870           40 :     god = GNUNET_new (struct GetOrderData);
    1871           40 :     god->choice_index = -1;
    1872           40 :     hc->ctx = god;
    1873           40 :     hc->cc = &god_cleanup;
    1874           40 :     god->sc.con = connection;
    1875           40 :     god->hc = hc;
    1876           40 :     god->order_id = hc->infix;
    1877              :     god->generate_html
    1878           40 :       = TMH_MHD_test_html_desired (connection);
    1879              : 
    1880              :     /* first-time initialization / sanity checks */
    1881           40 :     TALER_MHD_parse_request_arg_auto (connection,
    1882              :                                       "h_contract",
    1883              :                                       &god->h_contract_terms,
    1884              :                                       god->h_contract_provided);
    1885           40 :     TALER_MHD_parse_request_arg_auto (connection,
    1886              :                                       "token",
    1887              :                                       &god->claim_token,
    1888              :                                       god->claim_token_provided);
    1889           40 :     if (! (TALER_MHD_arg_to_yna (connection,
    1890              :                                  "allow_refunded_for_repurchase",
    1891              :                                  TALER_EXCHANGE_YNA_NO,
    1892              :                                  &god->allow_refunded_for_repurchase)) )
    1893            0 :       return TALER_MHD_reply_with_error (connection,
    1894              :                                          MHD_HTTP_BAD_REQUEST,
    1895              :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    1896              :                                          "allow_refunded_for_repurchase");
    1897           40 :     god->session_id = MHD_lookup_connection_value (connection,
    1898              :                                                    MHD_GET_ARGUMENT_KIND,
    1899              :                                                    "session_id");
    1900              : 
    1901              :     /* process await_refund_obtained argument */
    1902              :     {
    1903              :       const char *await_refund_obtained_s;
    1904              : 
    1905              :       await_refund_obtained_s =
    1906           40 :         MHD_lookup_connection_value (connection,
    1907              :                                      MHD_GET_ARGUMENT_KIND,
    1908              :                                      "await_refund_obtained");
    1909           40 :       god->sc.awaiting_refund_obtained =
    1910              :         (NULL != await_refund_obtained_s)
    1911            0 :         ? 0 == strcasecmp (await_refund_obtained_s,
    1912              :                            "yes")
    1913           40 :         : false;
    1914           40 :       if (god->sc.awaiting_refund_obtained)
    1915            0 :         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1916              :                     "Awaiting refund obtained\n");
    1917              :     }
    1918              : 
    1919           40 :     TALER_MHD_parse_request_amount (connection,
    1920              :                                     "refund",
    1921              :                                     &god->sc.refund_expected);
    1922           40 :     if (TALER_amount_is_valid (&god->sc.refund_expected))
    1923              :     {
    1924            5 :       god->sc.awaiting_refund = true;
    1925            5 :       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1926              :                   "Awaiting minimum refund of %s\n",
    1927              :                   TALER_amount2s (&god->sc.refund_expected));
    1928              :     }
    1929           40 :     TALER_MHD_parse_request_timeout (connection,
    1930              :                                      &god->sc.long_poll_timeout);
    1931              :   }
    1932              : 
    1933           49 :   if (GNUNET_SYSERR == god->suspended)
    1934            0 :     return MHD_NO; /* we are in shutdown */
    1935           49 :   if (GNUNET_YES == god->suspended)
    1936              :   {
    1937            0 :     god->suspended = GNUNET_NO;
    1938            0 :     GNUNET_CONTAINER_DLL_remove (god_head,
    1939              :                                  god_tail,
    1940              :                                  god);
    1941              :   }
    1942              : 
    1943              :   while (1)
    1944              :   {
    1945          777 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1946              :                 "Handling request in phase %d\n",
    1947              :                 (int) god->phase);
    1948          413 :     switch (god->phase)
    1949              :     {
    1950           40 :     case GOP_INIT:
    1951           40 :       phase_init (god);
    1952           40 :       break;
    1953           49 :     case GOP_LOOKUP_TERMS:
    1954           49 :       phase_lookup_terms (god);
    1955           49 :       break;
    1956           49 :     case GOP_PARSE_CONTRACT:
    1957           49 :       phase_parse_contract (god);
    1958           49 :       break;
    1959           49 :     case GOP_CHECK_CLIENT_ACCESS:
    1960           49 :       phase_check_client_access (god);
    1961           49 :       break;
    1962           49 :     case GOP_CHECK_PAID:
    1963           49 :       phase_check_paid (god);
    1964           49 :       break;
    1965           49 :     case GOP_REDIRECT_TO_PAID_ORDER:
    1966           49 :       if (phase_redirect_to_paid_order (god))
    1967            2 :         return MHD_YES;
    1968           47 :       break;
    1969           41 :     case GOP_HANDLE_UNPAID:
    1970           41 :       if (phase_handle_unpaid (god))
    1971            6 :         return MHD_YES;
    1972           35 :       break;
    1973           24 :     case GOP_CHECK_REFUNDED:
    1974           24 :       if (phase_check_refunded (god))
    1975            1 :         return MHD_YES;
    1976           23 :       break;
    1977           23 :     case GOP_RETURN_STATUS:
    1978           23 :       phase_return_status (god);
    1979           23 :       break;
    1980           40 :     case GOP_RETURN_MHD_YES:
    1981           40 :       return MHD_YES;
    1982            0 :     case GOP_RETURN_MHD_NO:
    1983            0 :       return MHD_NO;
    1984              :     }
    1985              :   }
    1986              : }
    1987              : 
    1988              : 
    1989              : /* end of taler-merchant-httpd_get-orders-ORDER_ID.c */
        

Generated by: LCOV version 2.0-1