merchant

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

taler-merchant-httpd_private-get-orders-ID.c (50588B)


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