merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd_get-private-orders-ORDER_ID.c (55668B)


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