merchant

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

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


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