merchant

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

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


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