merchant

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

taler-merchant-httpd_get-orders-ORDER_ID.c (59185B)


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