merchant

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

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


      1 /*
      2   This file is part of TALER
      3   (C) 2014-2024 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU 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/lookup_contract_terms3.h"
     39 #include "merchant-database/lookup_order.h"
     40 #include "merchant-database/lookup_order_by_fulfillment.h"
     41 #include "merchant-database/lookup_order_status.h"
     42 #include "merchant-database/lookup_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   god->suspended = GNUNET_NO;
    551   GNUNET_CONTAINER_DLL_remove (god_head,
    552                                god_tail,
    553                                god);
    554   MHD_resume_connection (god->sc.con);
    555   TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
    556   GNUNET_async_scope_restore (&old);
    557 }
    558 
    559 
    560 /**
    561  * First phase (after request parsing).
    562  * Set up long-polling.
    563  *
    564  * @param[in,out] god request context
    565  */
    566 static void
    567 phase_init (struct GetOrderData *god)
    568 {
    569   god->phase++;
    570   if (god->generate_html)
    571     return; /* If HTML is requested, we never actually long poll. */
    572   if (! GNUNET_TIME_absolute_is_future (god->sc.long_poll_timeout))
    573     return; /* long polling not requested */
    574 
    575   if (god->sc.awaiting_refund ||
    576       god->sc.awaiting_refund_obtained)
    577   {
    578     struct TMH_OrderPayEventP refund_eh = {
    579       .header.size = htons (sizeof (refund_eh)),
    580       .header.type = htons (god->sc.awaiting_refund_obtained
    581                                     ? TALER_DBEVENT_MERCHANT_REFUND_OBTAINED
    582                                     : TALER_DBEVENT_MERCHANT_ORDER_REFUND),
    583       .merchant_pub = god->hc->instance->merchant_pub
    584     };
    585 
    586     GNUNET_CRYPTO_hash (god->order_id,
    587                         strlen (god->order_id),
    588                         &refund_eh.h_order_id);
    589     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    590                 "Subscribing %p to refunds on %s\n",
    591                 god,
    592                 god->order_id);
    593     god->refund_eh
    594       = TALER_MERCHANTDB_event_listen (
    595           TMH_db,
    596           &refund_eh.header,
    597           GNUNET_TIME_absolute_get_remaining (
    598             god->sc.long_poll_timeout),
    599           &resume_by_event,
    600           god);
    601   }
    602   {
    603     struct TMH_OrderPayEventP pay_eh = {
    604       .header.size = htons (sizeof (pay_eh)),
    605       .header.type = htons (TALER_DBEVENT_MERCHANT_ORDER_PAID),
    606       .merchant_pub = god->hc->instance->merchant_pub
    607     };
    608 
    609     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    610                 "Subscribing to payments on %s\n",
    611                 god->order_id);
    612     GNUNET_CRYPTO_hash (god->order_id,
    613                         strlen (god->order_id),
    614                         &pay_eh.h_order_id);
    615     god->pay_eh
    616       = TALER_MERCHANTDB_event_listen (
    617           TMH_db,
    618           &pay_eh.header,
    619           GNUNET_TIME_absolute_get_remaining (
    620             god->sc.long_poll_timeout),
    621           &resume_by_event,
    622           god);
    623   }
    624 }
    625 
    626 
    627 /**
    628  * Lookup contract terms and check client has the
    629  * right to access this order (by claim token or
    630  * contract hash).
    631  *
    632  * @param[in,out] god request context
    633  */
    634 static void
    635 phase_lookup_terms (struct GetOrderData *god)
    636 {
    637   uint64_t order_serial;
    638   struct TALER_ClaimTokenP db_claim_token;
    639 
    640   /* Convert order_id to h_contract_terms */
    641   TALER_MERCHANTDB_preflight (TMH_db);
    642   GNUNET_assert (NULL == god->contract_terms_json);
    643   GNUNET_assert (NULL == god->order_json);
    644 
    645   {
    646     enum GNUNET_DB_QueryStatus qs;
    647 
    648     bool paid;
    649     bool wired;
    650     bool session_matches;
    651     qs = TALER_MERCHANTDB_lookup_contract_terms3 (
    652       TMH_db,
    653       god->hc->instance->settings.id,
    654       god->order_id,
    655       NULL,
    656       &god->contract_terms_json,
    657       &order_serial,
    658       &paid,
    659       &wired,
    660       &session_matches,
    661       &db_claim_token,
    662       &god->choice_index);
    663     if (0 > qs)
    664     {
    665       /* single, read-only SQL statements should never cause
    666          serialization problems */
    667       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
    668       /* Always report on hard error as well to enable diagnostics */
    669       GNUNET_break (0);
    670       phase_fail (god,
    671                   MHD_HTTP_INTERNAL_SERVER_ERROR,
    672                   TALER_EC_GENERIC_DB_FETCH_FAILED,
    673                   "lookup_contract_terms");
    674       return;
    675     }
    676     /* Note: when "!ord.requireClaimToken" and the client does not provide
    677        a claim token (all zeros!), then token_match==TRUE below: */
    678     god->token_match
    679       = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    680         && (0 == GNUNET_memcmp (&db_claim_token,
    681                                 &god->claim_token));
    682   }
    683 
    684   /* Check if client provided the right hash code of the contract terms */
    685   if (NULL != god->contract_terms_json)
    686   {
    687     god->contract_available = true;
    688     if (GNUNET_YES ==
    689         GNUNET_is_zero (&god->h_contract_terms))
    690     {
    691       if (GNUNET_OK !=
    692           TALER_JSON_contract_hash (god->contract_terms_json,
    693                                     &god->h_contract_terms))
    694       {
    695         GNUNET_break (0);
    696         phase_fail (god,
    697                     MHD_HTTP_INTERNAL_SERVER_ERROR,
    698                     TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
    699                     "contract terms");
    700         return;
    701       }
    702     }
    703     else
    704     {
    705       struct TALER_PrivateContractHashP h;
    706 
    707       if (GNUNET_OK !=
    708           TALER_JSON_contract_hash (god->contract_terms_json,
    709                                     &h))
    710       {
    711         GNUNET_break (0);
    712         phase_fail (god,
    713                     MHD_HTTP_INTERNAL_SERVER_ERROR,
    714                     TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
    715                     "contract terms");
    716         return;
    717       }
    718       god->contract_match = (0 ==
    719                              GNUNET_memcmp (&h,
    720                                             &god->h_contract_terms));
    721       if (! god->contract_match)
    722       {
    723         GNUNET_break_op (0);
    724         phase_fail (god,
    725                     MHD_HTTP_FORBIDDEN,
    726                     TALER_EC_MERCHANT_GENERIC_CONTRACT_HASH_DOES_NOT_MATCH_ORDER,
    727                     NULL);
    728         return;
    729       }
    730     }
    731   }
    732 
    733   if (god->contract_available)
    734   {
    735     god->claimed = true;
    736   }
    737   else
    738   {
    739     struct TALER_MerchantPostDataHashP unused;
    740     enum GNUNET_DB_QueryStatus qs;
    741 
    742     qs = TALER_MERCHANTDB_lookup_order (
    743       TMH_db,
    744       god->hc->instance->settings.id,
    745       god->order_id,
    746       &db_claim_token,
    747       &unused,
    748       &god->order_json);
    749     if (0 > qs)
    750     {
    751       /* single, read-only SQL statements should never cause
    752          serialization problems */
    753       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
    754       /* Always report on hard error as well to enable diagnostics */
    755       GNUNET_break (0);
    756       phase_fail (god,
    757                   MHD_HTTP_INTERNAL_SERVER_ERROR,
    758                   TALER_EC_GENERIC_DB_FETCH_FAILED,
    759                   "lookup_order");
    760       return;
    761     }
    762     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    763     {
    764       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    765                   "Unknown order id given: `%s'\n",
    766                   god->order_id);
    767       phase_fail (god,
    768                   MHD_HTTP_NOT_FOUND,
    769                   TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
    770                   god->order_id);
    771       return;
    772     }
    773     /* Note: when "!ord.requireClaimToken" and the client does not provide
    774        a claim token (all zeros!), then token_match==TRUE below: */
    775     god->token_match
    776       = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
    777         (0 == GNUNET_memcmp (&db_claim_token,
    778                              &god->claim_token));
    779   } /* end unclaimed order logic */
    780   god->phase++;
    781 }
    782 
    783 
    784 /**
    785  * Parse contract terms.
    786  *
    787  * @param[in,out] god request context
    788  */
    789 static void
    790 phase_parse_contract (struct GetOrderData *god)
    791 {
    792   GNUNET_break (NULL == god->contract_terms);
    793   GNUNET_break (NULL == god->order);
    794   if (NULL != god->contract_terms_json)
    795   {
    796     if (NULL ==
    797         json_object_get (god->contract_terms_json,
    798                          "nonce"))
    799     {
    800       god->pc = TALER_MERCHANT_proto_contract_parse (
    801         god->contract_terms_json);
    802       if (NULL == god->pc)
    803       {
    804         phase_fail (god,
    805                     MHD_HTTP_INTERNAL_SERVER_ERROR,
    806                     TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
    807                     god->order_id);
    808         return;
    809       }
    810       god->ct = god->pc->base;
    811     }
    812     else
    813     {
    814       god->contract_terms = TALER_MERCHANT_contract_parse (
    815         god->contract_terms_json);
    816       if (NULL == god->contract_terms)
    817       {
    818         phase_fail (god,
    819                     MHD_HTTP_INTERNAL_SERVER_ERROR,
    820                     TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
    821                     god->order_id);
    822         return;
    823       }
    824       god->pc = god->contract_terms->pc;
    825       god->ct = god->contract_terms->pc->base;
    826     }
    827   }
    828   if (NULL != god->order_json)
    829   {
    830     god->order = TALER_MERCHANT_order_parse (
    831       god->order_json);
    832     if (NULL == god->order)
    833     {
    834       phase_fail (god,
    835                   MHD_HTTP_INTERNAL_SERVER_ERROR,
    836                   TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID,
    837                   god->order_id);
    838       return;
    839     }
    840     god->order_parsed = true;
    841     god->ct = god->order->base;
    842   }
    843   GNUNET_assert ( (NULL != god->order) ||
    844                   (NULL != god->pc) );
    845 
    846   if ( (NULL != god->session_id) &&
    847        (NULL != god->ct->fulfillment_url) &&
    848        (NULL == god->session_eh) )
    849   {
    850     struct TMH_SessionEventP session_eh = {
    851       .header.size = htons (sizeof (session_eh)),
    852       .header.type = htons (TALER_DBEVENT_MERCHANT_SESSION_CAPTURED),
    853       .merchant_pub = god->hc->instance->merchant_pub
    854     };
    855 
    856     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    857                 "Subscribing to session triggers for %p\n",
    858                 god);
    859     GNUNET_CRYPTO_hash (god->session_id,
    860                         strlen (god->session_id),
    861                         &session_eh.h_session_id);
    862     GNUNET_CRYPTO_hash (god->ct->fulfillment_url,
    863                         strlen (god->ct->fulfillment_url),
    864                         &session_eh.h_fulfillment_url);
    865     god->session_eh
    866       = TALER_MERCHANTDB_event_listen (
    867           TMH_db,
    868           &session_eh.header,
    869           GNUNET_TIME_absolute_get_remaining (god->sc.long_poll_timeout),
    870           &resume_by_event,
    871           god);
    872   }
    873   god->phase++;
    874 }
    875 
    876 
    877 /**
    878  * Check that this order is unclaimed or claimed by
    879  * this client.
    880  *
    881  * @param[in,out] god request context
    882  */
    883 static void
    884 phase_check_client_access (struct GetOrderData *god)
    885 {
    886   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    887               "Token match: %d, contract_available: %d, contract match: %d, claimed: %d\n",
    888               god->token_match,
    889               god->contract_available,
    890               god->contract_match,
    891               god->claimed);
    892 
    893   if (god->claim_token_provided && ! god->token_match)
    894   {
    895     /* Authentication provided but wrong. */
    896     GNUNET_break_op (0);
    897     phase_fail (god,
    898                 MHD_HTTP_FORBIDDEN,
    899                 TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_TOKEN,
    900                 "authentication with claim token provided but wrong");
    901     return;
    902   }
    903 
    904   if (god->h_contract_provided && ! god->contract_match)
    905   {
    906     /* Authentication provided but wrong. */
    907     GNUNET_break_op (0);
    908     phase_fail (god,
    909                 MHD_HTTP_FORBIDDEN,
    910                 TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_HASH,
    911                 NULL);
    912     return;
    913   }
    914 
    915   if (! (god->token_match ||
    916          god->contract_match) )
    917   {
    918     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    919                 "Neither claim token nor contract matched\n");
    920     /* Client has no rights to this order */
    921     if (NULL == god->ct->public_reorder_url)
    922     {
    923       /* We cannot give the client a new order, just fail */
    924       if (! GNUNET_is_zero (&god->h_contract_terms))
    925       {
    926         GNUNET_break_op (0);
    927         phase_fail (god,
    928                     MHD_HTTP_FORBIDDEN,
    929                     TALER_EC_MERCHANT_GENERIC_CONTRACT_HASH_DOES_NOT_MATCH_ORDER,
    930                     NULL);
    931         return;
    932       }
    933       GNUNET_break_op (0);
    934       phase_fail (god,
    935                   MHD_HTTP_FORBIDDEN,
    936                   TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_TOKEN,
    937                   "no 'public_reorder_url'");
    938       return;
    939     }
    940     /* We have a fulfillment URL, redirect the client there, maybe
    941        the frontend can generate a fresh order for this new customer */
    942     if (god->generate_html)
    943     {
    944       /* Contract was claimed (maybe by another device), so this client
    945          cannot get the status information. Redirect to fulfillment page,
    946          where the client may be able to pickup a fresh order -- or might
    947          be able authenticate via session ID */
    948       struct MHD_Response *reply;
    949       enum MHD_Result ret;
    950 
    951       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    952                   "Contract claimed, redirecting to fulfillment page for order %s\n",
    953                   god->order_id);
    954       reply = MHD_create_response_from_buffer (0,
    955                                                NULL,
    956                                                MHD_RESPMEM_PERSISTENT);
    957       if (NULL == reply)
    958       {
    959         GNUNET_break (0);
    960         phase_end (god,
    961                    MHD_NO);
    962         return;
    963       }
    964       GNUNET_break (MHD_YES ==
    965                     MHD_add_response_header (
    966                       reply,
    967                       MHD_HTTP_HEADER_LOCATION,
    968                       god->ct->public_reorder_url));
    969       ret = MHD_queue_response (god->sc.con,
    970                                 MHD_HTTP_FOUND,
    971                                 reply);
    972       MHD_destroy_response (reply);
    973       phase_end (god,
    974                  ret);
    975       return;
    976     }
    977     /* Need to generate JSON reply */
    978     phase_end (god,
    979                TALER_MHD_REPLY_JSON_PACK (
    980                  god->sc.con,
    981                  MHD_HTTP_ACCEPTED,
    982                  GNUNET_JSON_pack_string (
    983                    "public_reorder_url",
    984                    god->ct->public_reorder_url)));
    985     return;
    986   }
    987   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    988               "Claim token or contract matched\n");
    989   god->phase++;
    990 }
    991 
    992 
    993 /**
    994  * Return the order summary of the contract of @a god in the
    995  * preferred language of the HTTP client.
    996  *
    997  * @param god order to extract summary from
    998  * @return dummy error message summary if no summary was provided in the contract
    999  */
   1000 static const char *
   1001 get_order_summary (const struct GetOrderData *god)
   1002 {
   1003   const char *language_pattern;
   1004   const char *ret;
   1005   json_t *terms;
   1006 
   1007   if (NULL != god->contract_terms_json)
   1008     terms = god->contract_terms_json;
   1009   else
   1010     terms = god->order_json;
   1011   language_pattern = MHD_lookup_connection_value (
   1012     god->sc.con,
   1013     MHD_HEADER_KIND,
   1014     MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
   1015   if (NULL == language_pattern)
   1016     language_pattern = "en";
   1017   ret = json_string_value (TALER_JSON_extract_i18n (
   1018                              terms,
   1019                              language_pattern,
   1020                              "summary"));
   1021   if (NULL == ret)
   1022   {
   1023     /* Upon order creation (and insertion into the database), the presence
   1024        of a summary should have been checked. So if we get here, someone
   1025        did something fishy to our database... */
   1026     GNUNET_break (0);
   1027     ret = "<bug: no summary>";
   1028   }
   1029   return ret;
   1030 }
   1031 
   1032 
   1033 /**
   1034  * The client did not yet pay, send it the payment request.
   1035  *
   1036  * @param god check pay request context
   1037  * @param already_paid_order_id if for the fulfillment URI there is
   1038  *          already a paid order, this is the order ID to redirect
   1039  *          the wallet to; NULL if not applicable
   1040  * @return true to exit due to suspension
   1041  */
   1042 static bool
   1043 send_pay_request (struct GetOrderData *god,
   1044                   const char *already_paid_order_id)
   1045 {
   1046   enum MHD_Result ret;
   1047   char *taler_pay_uri;
   1048   char *order_status_url;
   1049   struct GNUNET_TIME_Relative remaining;
   1050 
   1051   remaining = GNUNET_TIME_absolute_get_remaining (god->sc.long_poll_timeout);
   1052   if ( (! GNUNET_TIME_relative_is_zero (remaining)) &&
   1053        (NULL == already_paid_order_id) )
   1054   {
   1055     /* long polling: do not queue a response, suspend connection instead */
   1056     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1057                 "Suspending request: long polling for payment\n");
   1058     suspend_god (god);
   1059     return true;
   1060   }
   1061   /* Check if resource_id has been paid for in the same session
   1062    * with another order_id.
   1063    */
   1064   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1065               "Sending payment request\n");
   1066   taler_pay_uri = TMH_make_taler_pay_uri (
   1067     god->sc.con,
   1068     god->order_id,
   1069     god->session_id,
   1070     god->hc->instance->settings.id,
   1071     &god->claim_token);
   1072   order_status_url = TMH_make_order_status_url (
   1073     god->sc.con,
   1074     god->order_id,
   1075     god->session_id,
   1076     god->hc->instance->settings.id,
   1077     &god->claim_token,
   1078     NULL);
   1079   if ( (NULL == taler_pay_uri) ||
   1080        (NULL == order_status_url) )
   1081   {
   1082     GNUNET_break_op (0);
   1083     GNUNET_free (taler_pay_uri);
   1084     GNUNET_free (order_status_url);
   1085     phase_fail (god,
   1086                 MHD_HTTP_BAD_REQUEST,
   1087                 TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
   1088                 "host");
   1089     return false;
   1090   }
   1091   if (god->generate_html)
   1092   {
   1093     if (NULL != already_paid_order_id)
   1094     {
   1095       struct MHD_Response *reply;
   1096 
   1097       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1098                   "Redirecting to already paid order %s via fulfillment URL %s\n",
   1099                   already_paid_order_id,
   1100                   god->ct->fulfillment_url);
   1101       reply = MHD_create_response_from_buffer (0,
   1102                                                NULL,
   1103                                                MHD_RESPMEM_PERSISTENT);
   1104       if (NULL == reply)
   1105       {
   1106         GNUNET_break (0);
   1107         phase_end (god,
   1108                    MHD_NO);
   1109         return false;
   1110       }
   1111       GNUNET_break (MHD_YES ==
   1112                     MHD_add_response_header (
   1113                       reply,
   1114                       MHD_HTTP_HEADER_LOCATION,
   1115                       god->ct->fulfillment_url));
   1116       {
   1117         ret = MHD_queue_response (god->sc.con,
   1118                                   MHD_HTTP_FOUND,
   1119                                   reply);
   1120         MHD_destroy_response (reply);
   1121         phase_end (god,
   1122                    ret);
   1123         return false;
   1124       }
   1125     }
   1126 
   1127     {
   1128       char *qr;
   1129 
   1130       qr = TMH_create_qrcode (taler_pay_uri);
   1131       if (NULL == qr)
   1132       {
   1133         GNUNET_break (0);
   1134         phase_end (god,
   1135                    MHD_NO);
   1136         return false;
   1137       }
   1138       {
   1139         enum GNUNET_GenericReturnValue res;
   1140         json_t *context;
   1141 
   1142         context = GNUNET_JSON_PACK (
   1143           GNUNET_JSON_pack_string ("taler_pay_uri",
   1144                                    taler_pay_uri),
   1145           GNUNET_JSON_pack_string ("order_status_url",
   1146                                    order_status_url),
   1147           GNUNET_JSON_pack_string ("taler_pay_qrcode_svg",
   1148                                    qr),
   1149           GNUNET_JSON_pack_string ("order_summary",
   1150                                    get_order_summary (god)));
   1151         res = TALER_TEMPLATING_reply (
   1152           god->sc.con,
   1153           MHD_HTTP_PAYMENT_REQUIRED,
   1154           "request_payment",
   1155           god->hc->instance->settings.id,
   1156           taler_pay_uri,
   1157           context);
   1158         if (GNUNET_SYSERR == res)
   1159         {
   1160           GNUNET_break (0);
   1161           ret = MHD_NO;
   1162         }
   1163         else
   1164         {
   1165           ret = MHD_YES;
   1166         }
   1167         json_decref (context);
   1168       }
   1169       GNUNET_free (qr);
   1170     }
   1171   }
   1172   else /* end of 'generate HTML' */
   1173   {
   1174     ret = TALER_MHD_REPLY_JSON_PACK (
   1175       god->sc.con,
   1176       MHD_HTTP_PAYMENT_REQUIRED,
   1177       GNUNET_JSON_pack_string ("taler_pay_uri",
   1178                                taler_pay_uri),
   1179       GNUNET_JSON_pack_allow_null (
   1180         GNUNET_JSON_pack_string ("fulfillment_url",
   1181                                  god->ct->fulfillment_url)),
   1182       GNUNET_JSON_pack_allow_null (
   1183         GNUNET_JSON_pack_string ("already_paid_order_id",
   1184                                  already_paid_order_id)));
   1185   }
   1186   GNUNET_free (taler_pay_uri);
   1187   GNUNET_free (order_status_url);
   1188   phase_end (god,
   1189              ret);
   1190   return false;
   1191 }
   1192 
   1193 
   1194 /**
   1195  * Check if the order has been paid.
   1196  *
   1197  * @param[in,out] god request context
   1198  */
   1199 static void
   1200 phase_check_paid (struct GetOrderData *god)
   1201 {
   1202   enum GNUNET_DB_QueryStatus qs;
   1203   struct TALER_PrivateContractHashP h_contract;
   1204 
   1205   god->paid = false;
   1206   qs = TALER_MERCHANTDB_lookup_order_status (
   1207     TMH_db,
   1208     god->hc->instance->settings.id,
   1209     god->order_id,
   1210     &h_contract,
   1211     &god->paid);
   1212   if (0 > qs)
   1213   {
   1214     /* Always report on hard error as well to enable diagnostics */
   1215     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
   1216     phase_fail (god,
   1217                 MHD_HTTP_INTERNAL_SERVER_ERROR,
   1218                 TALER_EC_GENERIC_DB_FETCH_FAILED,
   1219                 "lookup_order_status");
   1220     return;
   1221   }
   1222   god->phase++;
   1223 }
   1224 
   1225 
   1226 /**
   1227  * Check if the client already paid for an equivalent
   1228  * order under this session, and if so redirect to
   1229  * that order.
   1230  *
   1231  * @param[in,out] god request context
   1232  * @return true to exit due to suspension
   1233  */
   1234 static bool
   1235 phase_redirect_to_paid_order (struct GetOrderData *god)
   1236 {
   1237   if ( (NULL != god->session_id) &&
   1238        (NULL != god->ct->fulfillment_url) )
   1239   {
   1240     /* Check if client paid for this fulfillment article
   1241        already within this session, but using a different
   1242        order ID. If so, redirect the client to the order
   1243        it already paid.  Allows, for example, the case
   1244        where a mobile phone pays for a browser's session,
   1245        where the mobile phone has a different order
   1246        ID (because it purchased the article earlier)
   1247        than the one that the browser is waiting for. */
   1248     char *already_paid_order_id = NULL;
   1249     enum GNUNET_DB_QueryStatus qs;
   1250 
   1251     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1252                 "Running re-purchase detection for %s/%s\n",
   1253                 god->session_id,
   1254                 god->ct->fulfillment_url);
   1255     qs = TALER_MERCHANTDB_lookup_order_by_fulfillment (
   1256       TMH_db,
   1257       god->hc->instance->settings.id,
   1258       god->ct->fulfillment_url,
   1259       god->session_id,
   1260       TALER_EXCHANGE_YNA_NO != god->allow_refunded_for_repurchase,
   1261       &already_paid_order_id);
   1262     if (qs < 0)
   1263     {
   1264       /* single, read-only SQL statements should never cause
   1265          serialization problems */
   1266       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
   1267       /* Always report on hard error as well to enable diagnostics */
   1268       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
   1269       phase_fail (god,
   1270                   MHD_HTTP_INTERNAL_SERVER_ERROR,
   1271                   TALER_EC_GENERIC_DB_FETCH_FAILED,
   1272                   "order by fulfillment");
   1273       return false;
   1274     }
   1275     if ( (! god->paid) &&
   1276          ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
   1277            (0 != strcmp (god->order_id,
   1278                          already_paid_order_id)) ) )
   1279     {
   1280       bool ret;
   1281 
   1282       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1283                   "Sending pay request for order %s (already paid: %s)\n",
   1284                   god->order_id,
   1285                   already_paid_order_id);
   1286       ret = send_pay_request (god,
   1287                               already_paid_order_id);
   1288       GNUNET_free (already_paid_order_id);
   1289       return ret;
   1290     }
   1291     GNUNET_free (already_paid_order_id);
   1292   }
   1293   god->phase++;
   1294   return false;
   1295 }
   1296 
   1297 
   1298 /**
   1299  * Check if the order has been paid, and if not
   1300  * request payment.
   1301  *
   1302  * @param[in,out] god request context
   1303  * @return true to exit due to suspension
   1304  */
   1305 static bool
   1306 phase_handle_unpaid (struct GetOrderData *god)
   1307 {
   1308   if (god->paid)
   1309   {
   1310     god->phase++;
   1311     return false;
   1312   }
   1313   if (god->claimed)
   1314   {
   1315     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1316                 "Order claimed but unpaid, sending pay request for order %s\n",
   1317                 god->order_id);
   1318   }
   1319   else
   1320   {
   1321     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1322                 "Order unclaimed, sending pay request for order %s\n",
   1323                 god->order_id);
   1324   }
   1325   return send_pay_request (god,
   1326                            NULL);
   1327 }
   1328 
   1329 
   1330 /**
   1331  * Function called with detailed information about a refund.
   1332  * It is responsible for packing up the data to return.
   1333  *
   1334  * @param cls closure
   1335  * @param refund_serial unique serial number of the refund
   1336  * @param timestamp time of the refund (for grouping of refunds in the wallet UI)
   1337  * @param coin_pub public coin from which the refund comes from
   1338  * @param exchange_url URL of the exchange that issued @a coin_pub
   1339  * @param rtransaction_id identificator of the refund
   1340  * @param reason human-readable explanation of the refund
   1341  * @param refund_amount refund amount which is being taken from @a coin_pub
   1342  * @param pending true if the this refund was not yet processed by the wallet/exchange
   1343  */
   1344 static void
   1345 process_refunds_cb (void *cls,
   1346                     uint64_t refund_serial,
   1347                     struct GNUNET_TIME_Timestamp timestamp,
   1348                     const struct TALER_CoinSpendPublicKeyP *coin_pub,
   1349                     const char *exchange_url,
   1350                     uint64_t rtransaction_id,
   1351                     const char *reason,
   1352                     const struct TALER_Amount *refund_amount,
   1353                     bool pending)
   1354 {
   1355   struct GetOrderData *god = cls;
   1356 
   1357   (void) refund_serial;
   1358   (void) timestamp;
   1359   (void) exchange_url;
   1360   (void) rtransaction_id;
   1361   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1362               "Found refund of %s for coin %s with reason `%s' in database\n",
   1363               TALER_amount2s (refund_amount),
   1364               TALER_B2S (coin_pub),
   1365               reason);
   1366   god->refund_pending |= pending;
   1367   if ( (GNUNET_OK !=
   1368         TALER_amount_cmp_currency (&god->refund_taken,
   1369                                    refund_amount)) ||
   1370        (GNUNET_OK !=
   1371         TALER_amount_cmp_currency (&god->refund_amount,
   1372                                    refund_amount)) )
   1373   {
   1374     god->bad_refund_currency_in_db = true;
   1375     return;
   1376   }
   1377   if (! pending)
   1378   {
   1379     GNUNET_assert (0 <=
   1380                    TALER_amount_add (&god->refund_taken,
   1381                                      &god->refund_taken,
   1382                                      refund_amount));
   1383   }
   1384   GNUNET_assert (0 <=
   1385                  TALER_amount_add (&god->refund_amount,
   1386                                    &god->refund_amount,
   1387                                    refund_amount));
   1388   god->refunded = true;
   1389 }
   1390 
   1391 
   1392 /**
   1393  * Check if the order has been refunded.
   1394  *
   1395  * @param[in,out] god request context
   1396  * @return true to exit due to suspension
   1397  */
   1398 static bool
   1399 phase_check_refunded (struct GetOrderData *god)
   1400 {
   1401   enum GNUNET_DB_QueryStatus qs;
   1402   struct TALER_Amount refund_amount;
   1403   const char *refund_currency;
   1404 
   1405   GNUNET_assert (NULL != god->contract_terms);
   1406   switch (god->ct->version)
   1407   {
   1408   case TALER_MERCHANT_CONTRACT_VERSION_0:
   1409     refund_amount = god->pc->details.v0.brutto;
   1410     refund_currency = god->pc->details.v0.brutto.currency;
   1411     break;
   1412   case TALER_MERCHANT_CONTRACT_VERSION_1:
   1413     if (god->choice_index < 0)
   1414     {
   1415       // order was not paid, no refund to be checked
   1416       god->phase++;
   1417       return false;
   1418     }
   1419     GNUNET_assert (god->choice_index <
   1420                    god->pc->details.v1.choices_len);
   1421     refund_currency = god->pc->details.v1.choices[
   1422       god->choice_index].amount.currency;
   1423     GNUNET_assert (GNUNET_OK ==
   1424                    TALER_amount_set_zero (refund_currency,
   1425                                           &refund_amount));
   1426     break;
   1427   default:
   1428     {
   1429       GNUNET_break (0);
   1430       phase_fail (god,
   1431                   MHD_HTTP_INTERNAL_SERVER_ERROR,
   1432                   TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_VERSION,
   1433                   NULL);
   1434       return false;
   1435     }
   1436   }
   1437 
   1438   if ( (god->sc.awaiting_refund) &&
   1439        (GNUNET_OK !=
   1440         TALER_amount_cmp_currency (&refund_amount,
   1441                                    &god->sc.refund_expected)) )
   1442   {
   1443     GNUNET_break (0);
   1444     phase_fail (god,
   1445                 MHD_HTTP_CONFLICT,
   1446                 TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1447                 refund_currency);
   1448     return false;
   1449   }
   1450 
   1451   /* At this point, we know the contract was paid. Let's check for
   1452      refunds. First, clear away refunds found from previous invocations. */
   1453   GNUNET_assert (GNUNET_OK ==
   1454                  TALER_amount_set_zero (refund_currency,
   1455                                         &god->refund_amount));
   1456   GNUNET_assert (GNUNET_OK ==
   1457                  TALER_amount_set_zero (refund_currency,
   1458                                         &god->refund_taken));
   1459   qs = TALER_MERCHANTDB_lookup_refunds_detailed (
   1460     TMH_db,
   1461     god->hc->instance->settings.id,
   1462     &god->h_contract_terms,
   1463     &process_refunds_cb,
   1464     god);
   1465   if (0 > qs)
   1466   {
   1467     GNUNET_break (0);
   1468     phase_fail (god,
   1469                 MHD_HTTP_INTERNAL_SERVER_ERROR,
   1470                 TALER_EC_GENERIC_DB_FETCH_FAILED,
   1471                 "lookup_refunds_detailed");
   1472     return false;
   1473   }
   1474   if (god->bad_refund_currency_in_db)
   1475   {
   1476     GNUNET_break (0);
   1477     phase_fail (god,
   1478                 MHD_HTTP_INTERNAL_SERVER_ERROR,
   1479                 TALER_EC_GENERIC_DB_FETCH_FAILED,
   1480                 "currency mix-up between contract price and refunds in database");
   1481     return false;
   1482   }
   1483   if ( ((god->sc.awaiting_refund) &&
   1484         ( (! god->refunded) ||
   1485           (1 != TALER_amount_cmp (&god->refund_amount,
   1486                                   &god->sc.refund_expected)) )) ||
   1487        ( (god->sc.awaiting_refund_obtained) &&
   1488          (god->refund_pending) ) )
   1489   {
   1490     /* Client is waiting for a refund larger than what we have, suspend
   1491        until timeout */
   1492     struct GNUNET_TIME_Relative remaining;
   1493 
   1494     remaining = GNUNET_TIME_absolute_get_remaining (god->sc.long_poll_timeout);
   1495     if ( (! GNUNET_TIME_relative_is_zero (remaining)) &&
   1496          (! god->generate_html) )
   1497     {
   1498       /* yes, indeed suspend */
   1499       if (god->sc.awaiting_refund)
   1500         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1501                     "Awaiting refund exceeding %s\n",
   1502                     TALER_amount2s (&god->sc.refund_expected));
   1503       if (god->sc.awaiting_refund_obtained)
   1504         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1505                     "Awaiting pending refunds\n");
   1506       suspend_god (god);
   1507       return true;
   1508     }
   1509   }
   1510   god->phase++;
   1511   return false;
   1512 }
   1513 
   1514 
   1515 /**
   1516  * Create a taler://refund/ URI for the given @a con and @a order_id
   1517  * and @a instance_id.
   1518  *
   1519  * @param merchant_base_url URL to take host and path from;
   1520  *        we cannot take it from the MHD connection as a browser
   1521  *        may have changed 'http' to 'https' and we MUST be consistent
   1522  *        with what the merchant's frontend used initially
   1523  * @param order_id the order id
   1524  * @return corresponding taler://refund/ URI, or NULL on missing "host"
   1525  */
   1526 static char *
   1527 make_taler_refund_uri (const char *merchant_base_url,
   1528                        const char *order_id)
   1529 {
   1530   struct GNUNET_Buffer buf = { 0 };
   1531   char *url;
   1532   struct GNUNET_Uri uri;
   1533 
   1534   url = GNUNET_strdup (merchant_base_url);
   1535   if (-1 == GNUNET_uri_parse (&uri,
   1536                               url))
   1537   {
   1538     GNUNET_break (0);
   1539     GNUNET_free (url);
   1540     return NULL;
   1541   }
   1542   GNUNET_assert (NULL != order_id);
   1543   GNUNET_buffer_write_str (&buf,
   1544                            "taler");
   1545   if (0 == strcasecmp ("http",
   1546                        uri.scheme))
   1547     GNUNET_buffer_write_str (&buf,
   1548                              "+http");
   1549   GNUNET_buffer_write_str (&buf,
   1550                            "://refund/");
   1551   GNUNET_buffer_write_str (&buf,
   1552                            uri.host);
   1553   if (0 != uri.port)
   1554     GNUNET_buffer_write_fstr (&buf,
   1555                               ":%u",
   1556                               (unsigned int) uri.port);
   1557   if (NULL != uri.path)
   1558     GNUNET_buffer_write_path (&buf,
   1559                               uri.path);
   1560   GNUNET_buffer_write_path (&buf,
   1561                             order_id);
   1562   GNUNET_buffer_write_path (&buf,
   1563                             ""); // Trailing slash
   1564   GNUNET_free (url);
   1565   return GNUNET_buffer_reap_str (&buf);
   1566 }
   1567 
   1568 
   1569 /**
   1570  * Generate the order status response.
   1571  *
   1572  * @param[in,out] god request context
   1573  */
   1574 static void
   1575 phase_return_status (struct GetOrderData *god)
   1576 {
   1577   /* All operations done, build final response */
   1578   if (! god->generate_html)
   1579   {
   1580     phase_end (god,
   1581                TALER_MHD_REPLY_JSON_PACK (
   1582                  god->sc.con,
   1583                  MHD_HTTP_OK,
   1584                  GNUNET_JSON_pack_allow_null (
   1585                    GNUNET_JSON_pack_string ("fulfillment_url",
   1586                                             god->ct->fulfillment_url)),
   1587                  GNUNET_JSON_pack_bool ("refunded",
   1588                                         god->refunded),
   1589                  GNUNET_JSON_pack_bool ("refund_pending",
   1590                                         god->refund_pending),
   1591                  TALER_JSON_pack_amount ("refund_taken",
   1592                                          &god->refund_taken),
   1593                  TALER_JSON_pack_amount ("refund_amount",
   1594                                          &god->refund_amount)));
   1595     return;
   1596   }
   1597 
   1598   if (god->refund_pending)
   1599   {
   1600     char *qr;
   1601     char *uri;
   1602 
   1603     GNUNET_assert (NULL != god->contract_terms_json);
   1604     GNUNET_assert (NULL != god->contract_terms);
   1605     uri = make_taler_refund_uri (god->pc->merchant_base_url,
   1606                                  god->order_id);
   1607     if (NULL == uri)
   1608     {
   1609       GNUNET_break (0);
   1610       phase_fail (god,
   1611                   MHD_HTTP_INTERNAL_SERVER_ERROR,
   1612                   TALER_EC_GENERIC_ALLOCATION_FAILURE,
   1613                   "refund URI");
   1614       return;
   1615     }
   1616     qr = TMH_create_qrcode (uri);
   1617     if (NULL == qr)
   1618     {
   1619       GNUNET_break (0);
   1620       GNUNET_free (uri);
   1621       phase_fail (god,
   1622                   MHD_HTTP_INTERNAL_SERVER_ERROR,
   1623                   TALER_EC_GENERIC_ALLOCATION_FAILURE,
   1624                   "qr code");
   1625       return;
   1626     }
   1627 
   1628     {
   1629       enum GNUNET_GenericReturnValue res;
   1630       json_t *context;
   1631 
   1632       context = GNUNET_JSON_PACK (
   1633         GNUNET_JSON_pack_string ("order_summary",
   1634                                  get_order_summary (god)),
   1635         TALER_JSON_pack_amount ("refund_amount",
   1636                                 &god->refund_amount),
   1637         TALER_JSON_pack_amount ("refund_taken",
   1638                                 &god->refund_taken),
   1639         GNUNET_JSON_pack_string ("taler_refund_uri",
   1640                                  uri),
   1641         GNUNET_JSON_pack_string ("taler_refund_qrcode_svg",
   1642                                  qr));
   1643       res = TALER_TEMPLATING_reply (
   1644         god->sc.con,
   1645         MHD_HTTP_OK,
   1646         "offer_refund",
   1647         god->hc->instance->settings.id,
   1648         uri,
   1649         context);
   1650       GNUNET_break (GNUNET_OK == res);
   1651       json_decref (context);
   1652       phase_end (god,
   1653                  (GNUNET_SYSERR == res)
   1654                  ? MHD_NO
   1655                  : MHD_YES);
   1656     }
   1657     GNUNET_free (uri);
   1658     GNUNET_free (qr);
   1659     return;
   1660   }
   1661 
   1662   {
   1663     enum GNUNET_GenericReturnValue res;
   1664     json_t *context;
   1665 
   1666     context = GNUNET_JSON_PACK (
   1667       GNUNET_JSON_pack_object_incref ("contract_terms",
   1668                                       NULL != god->contract_terms_json
   1669                                       ? god->contract_terms_json
   1670                                       : god->order_json),
   1671       GNUNET_JSON_pack_string ("order_summary",
   1672                                get_order_summary (god)),
   1673       TALER_JSON_pack_amount ("refund_amount",
   1674                               &god->refund_amount),
   1675       TALER_JSON_pack_amount ("refund_taken",
   1676                               &god->refund_taken));
   1677     res = TALER_TEMPLATING_reply (
   1678       god->sc.con,
   1679       MHD_HTTP_OK,
   1680       "show_order_details",
   1681       god->hc->instance->settings.id,
   1682       NULL,
   1683       context);
   1684     GNUNET_break (GNUNET_OK == res);
   1685     json_decref (context);
   1686     phase_end (god,
   1687                (GNUNET_SYSERR == res)
   1688                ? MHD_NO
   1689                : MHD_YES);
   1690   }
   1691 }
   1692 
   1693 
   1694 enum MHD_Result
   1695 TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
   1696                    struct MHD_Connection *connection,
   1697                    struct TMH_HandlerContext *hc)
   1698 {
   1699   struct GetOrderData *god = hc->ctx;
   1700 
   1701   (void) rh;
   1702   if (NULL == god)
   1703   {
   1704     god = GNUNET_new (struct GetOrderData);
   1705     hc->ctx = god;
   1706     hc->cc = &god_cleanup;
   1707     god->sc.con = connection;
   1708     god->hc = hc;
   1709     god->order_id = hc->infix;
   1710     god->generate_html
   1711       = TMH_MHD_test_html_desired (connection);
   1712 
   1713     /* first-time initialization / sanity checks */
   1714     TALER_MHD_parse_request_arg_auto (connection,
   1715                                       "h_contract",
   1716                                       &god->h_contract_terms,
   1717                                       god->h_contract_provided);
   1718     TALER_MHD_parse_request_arg_auto (connection,
   1719                                       "token",
   1720                                       &god->claim_token,
   1721                                       god->claim_token_provided);
   1722     if (! (TALER_MHD_arg_to_yna (connection,
   1723                                  "allow_refunded_for_repurchase",
   1724                                  TALER_EXCHANGE_YNA_NO,
   1725                                  &god->allow_refunded_for_repurchase)) )
   1726       return TALER_MHD_reply_with_error (connection,
   1727                                          MHD_HTTP_BAD_REQUEST,
   1728                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1729                                          "allow_refunded_for_repurchase");
   1730     god->session_id = MHD_lookup_connection_value (connection,
   1731                                                    MHD_GET_ARGUMENT_KIND,
   1732                                                    "session_id");
   1733 
   1734     /* process await_refund_obtained argument */
   1735     {
   1736       const char *await_refund_obtained_s;
   1737 
   1738       await_refund_obtained_s =
   1739         MHD_lookup_connection_value (connection,
   1740                                      MHD_GET_ARGUMENT_KIND,
   1741                                      "await_refund_obtained");
   1742       god->sc.awaiting_refund_obtained =
   1743         (NULL != await_refund_obtained_s)
   1744         ? 0 == strcasecmp (await_refund_obtained_s,
   1745                            "yes")
   1746         : false;
   1747       if (god->sc.awaiting_refund_obtained)
   1748         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1749                     "Awaiting refund obtained\n");
   1750     }
   1751 
   1752     TALER_MHD_parse_request_amount (connection,
   1753                                     "refund",
   1754                                     &god->sc.refund_expected);
   1755     if (TALER_amount_is_valid (&god->sc.refund_expected))
   1756     {
   1757       god->sc.awaiting_refund = true;
   1758       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1759                   "Awaiting minimum refund of %s\n",
   1760                   TALER_amount2s (&god->sc.refund_expected));
   1761     }
   1762     TALER_MHD_parse_request_timeout (connection,
   1763                                      &god->sc.long_poll_timeout);
   1764   }
   1765 
   1766   if (GNUNET_SYSERR == god->suspended)
   1767     return MHD_NO; /* we are in shutdown */
   1768   if (GNUNET_YES == god->suspended)
   1769   {
   1770     god->suspended = GNUNET_NO;
   1771     GNUNET_CONTAINER_DLL_remove (god_head,
   1772                                  god_tail,
   1773                                  god);
   1774   }
   1775 
   1776   while (1)
   1777   {
   1778     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1779                 "Handling request in phase %d\n",
   1780                 (int) god->phase);
   1781     switch (god->phase)
   1782     {
   1783     case GOP_INIT:
   1784       phase_init (god);
   1785       break;
   1786     case GOP_LOOKUP_TERMS:
   1787       phase_lookup_terms (god);
   1788       break;
   1789     case GOP_PARSE_CONTRACT:
   1790       phase_parse_contract (god);
   1791       break;
   1792     case GOP_CHECK_CLIENT_ACCESS:
   1793       phase_check_client_access (god);
   1794       break;
   1795     case GOP_CHECK_PAID:
   1796       phase_check_paid (god);
   1797       break;
   1798     case GOP_REDIRECT_TO_PAID_ORDER:
   1799       if (phase_redirect_to_paid_order (god))
   1800         return MHD_YES;
   1801       break;
   1802     case GOP_HANDLE_UNPAID:
   1803       if (phase_handle_unpaid (god))
   1804         return MHD_YES;
   1805       break;
   1806     case GOP_CHECK_REFUNDED:
   1807       if (phase_check_refunded (god))
   1808         return MHD_YES;
   1809       break;
   1810     case GOP_RETURN_STATUS:
   1811       phase_return_status (god);
   1812       break;
   1813     case GOP_RETURN_MHD_YES:
   1814       return MHD_YES;
   1815     case GOP_RETURN_MHD_NO:
   1816       return MHD_NO;
   1817     }
   1818   }
   1819 }
   1820 
   1821 
   1822 /* end of taler-merchant-httpd_get-orders-ORDER_ID.c */