merchant

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

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


      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/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   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_get_contract_terms_status (
    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                   "get_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_get_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                   "get_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        (! god->generate_html) &&
   1054        (NULL == already_paid_order_id) )
   1055   {
   1056     /* long polling: do not queue a response, suspend connection instead */
   1057     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1058                 "Suspending request: long polling for payment\n");
   1059     suspend_god (god);
   1060     return true;
   1061   }
   1062   /* Check if resource_id has been paid for in the same session
   1063    * with another order_id.
   1064    */
   1065   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1066               "Sending payment request\n");
   1067   taler_pay_uri = TMH_make_taler_pay_uri (
   1068     god->sc.con,
   1069     god->order_id,
   1070     god->session_id,
   1071     god->hc->instance->settings.id,
   1072     &god->claim_token);
   1073   order_status_url = TMH_make_order_status_url (
   1074     god->sc.con,
   1075     god->order_id,
   1076     god->session_id,
   1077     god->hc->instance->settings.id,
   1078     &god->claim_token,
   1079     NULL);
   1080   if ( (NULL == taler_pay_uri) ||
   1081        (NULL == order_status_url) )
   1082   {
   1083     GNUNET_break_op (0);
   1084     GNUNET_free (taler_pay_uri);
   1085     GNUNET_free (order_status_url);
   1086     phase_fail (god,
   1087                 MHD_HTTP_BAD_REQUEST,
   1088                 TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
   1089                 "host");
   1090     return false;
   1091   }
   1092   if (god->generate_html)
   1093   {
   1094     if (NULL != already_paid_order_id)
   1095     {
   1096       struct MHD_Response *reply;
   1097 
   1098       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1099                   "Redirecting to already paid order %s via fulfillment URL %s\n",
   1100                   already_paid_order_id,
   1101                   god->ct->fulfillment_url);
   1102       reply = MHD_create_response_from_buffer (0,
   1103                                                NULL,
   1104                                                MHD_RESPMEM_PERSISTENT);
   1105       if (NULL == reply)
   1106       {
   1107         GNUNET_break (0);
   1108         phase_end (god,
   1109                    MHD_NO);
   1110         return false;
   1111       }
   1112       GNUNET_break (MHD_YES ==
   1113                     MHD_add_response_header (
   1114                       reply,
   1115                       MHD_HTTP_HEADER_LOCATION,
   1116                       god->ct->fulfillment_url));
   1117       {
   1118         ret = MHD_queue_response (god->sc.con,
   1119                                   MHD_HTTP_FOUND,
   1120                                   reply);
   1121         MHD_destroy_response (reply);
   1122         phase_end (god,
   1123                    ret);
   1124         return false;
   1125       }
   1126     }
   1127 
   1128     {
   1129       char *qr;
   1130 
   1131       qr = TMH_create_qrcode (taler_pay_uri);
   1132       if (NULL == qr)
   1133       {
   1134         GNUNET_break (0);
   1135         phase_end (god,
   1136                    MHD_NO);
   1137         return false;
   1138       }
   1139       {
   1140         enum GNUNET_GenericReturnValue res;
   1141         json_t *context;
   1142 
   1143         context = GNUNET_JSON_PACK (
   1144           GNUNET_JSON_pack_string ("taler_pay_uri",
   1145                                    taler_pay_uri),
   1146           GNUNET_JSON_pack_string ("order_status_url",
   1147                                    order_status_url),
   1148           GNUNET_JSON_pack_string ("taler_pay_qrcode_svg",
   1149                                    qr),
   1150           GNUNET_JSON_pack_string ("order_summary",
   1151                                    get_order_summary (god)));
   1152         res = TALER_TEMPLATING_reply (
   1153           god->sc.con,
   1154           MHD_HTTP_PAYMENT_REQUIRED,
   1155           "request_payment",
   1156           god->hc->instance->settings.id,
   1157           taler_pay_uri,
   1158           context);
   1159         if (GNUNET_SYSERR == res)
   1160         {
   1161           GNUNET_break (0);
   1162           ret = MHD_NO;
   1163         }
   1164         else
   1165         {
   1166           ret = MHD_YES;
   1167         }
   1168         json_decref (context);
   1169       }
   1170       GNUNET_free (qr);
   1171     }
   1172   }
   1173   else /* end of 'generate HTML' */
   1174   {
   1175     ret = TALER_MHD_REPLY_JSON_PACK (
   1176       god->sc.con,
   1177       MHD_HTTP_PAYMENT_REQUIRED,
   1178       GNUNET_JSON_pack_string ("taler_pay_uri",
   1179                                taler_pay_uri),
   1180       GNUNET_JSON_pack_allow_null (
   1181         GNUNET_JSON_pack_string ("fulfillment_url",
   1182                                  god->ct->fulfillment_url)),
   1183       GNUNET_JSON_pack_allow_null (
   1184         GNUNET_JSON_pack_string ("already_paid_order_id",
   1185                                  already_paid_order_id)));
   1186   }
   1187   GNUNET_free (taler_pay_uri);
   1188   GNUNET_free (order_status_url);
   1189   phase_end (god,
   1190              ret);
   1191   return false;
   1192 }
   1193 
   1194 
   1195 /**
   1196  * Check if the order has been paid.
   1197  *
   1198  * @param[in,out] god request context
   1199  */
   1200 static void
   1201 phase_check_paid (struct GetOrderData *god)
   1202 {
   1203   enum GNUNET_DB_QueryStatus qs;
   1204   struct TALER_PrivateContractHashP h_contract;
   1205 
   1206   god->paid = false;
   1207   qs = TALER_MERCHANTDB_get_order_status (
   1208     TMH_db,
   1209     god->hc->instance->settings.id,
   1210     god->order_id,
   1211     &h_contract,
   1212     &god->paid);
   1213   if (0 > qs)
   1214   {
   1215     /* Always report on hard error as well to enable diagnostics */
   1216     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
   1217     phase_fail (god,
   1218                 MHD_HTTP_INTERNAL_SERVER_ERROR,
   1219                 TALER_EC_GENERIC_DB_FETCH_FAILED,
   1220                 "get_order_status");
   1221     return;
   1222   }
   1223   god->phase++;
   1224 }
   1225 
   1226 
   1227 /**
   1228  * Check if the client already paid for an equivalent
   1229  * order under this session, and if so redirect to
   1230  * that order.
   1231  *
   1232  * @param[in,out] god request context
   1233  * @return true to exit due to suspension
   1234  */
   1235 static bool
   1236 phase_redirect_to_paid_order (struct GetOrderData *god)
   1237 {
   1238   if ( (NULL != god->session_id) &&
   1239        (NULL != god->ct->fulfillment_url) )
   1240   {
   1241     /* Check if client paid for this fulfillment article
   1242        already within this session, but using a different
   1243        order ID. If so, redirect the client to the order
   1244        it already paid.  Allows, for example, the case
   1245        where a mobile phone pays for a browser's session,
   1246        where the mobile phone has a different order
   1247        ID (because it purchased the article earlier)
   1248        than the one that the browser is waiting for. */
   1249     char *already_paid_order_id = NULL;
   1250     enum GNUNET_DB_QueryStatus qs;
   1251 
   1252     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1253                 "Running re-purchase detection for %s/%s\n",
   1254                 god->session_id,
   1255                 god->ct->fulfillment_url);
   1256     qs = TALER_MERCHANTDB_get_order_by_fulfillment (
   1257       TMH_db,
   1258       god->hc->instance->settings.id,
   1259       god->ct->fulfillment_url,
   1260       god->session_id,
   1261       TALER_EXCHANGE_YNA_NO != god->allow_refunded_for_repurchase,
   1262       &already_paid_order_id);
   1263     if (qs < 0)
   1264     {
   1265       /* single, read-only SQL statements should never cause
   1266          serialization problems */
   1267       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
   1268       /* Always report on hard error as well to enable diagnostics */
   1269       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
   1270       phase_fail (god,
   1271                   MHD_HTTP_INTERNAL_SERVER_ERROR,
   1272                   TALER_EC_GENERIC_DB_FETCH_FAILED,
   1273                   "order by fulfillment");
   1274       return false;
   1275     }
   1276     if ( (! god->paid) &&
   1277          ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
   1278            (0 != strcmp (god->order_id,
   1279                          already_paid_order_id)) ) )
   1280     {
   1281       bool ret;
   1282 
   1283       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1284                   "Sending pay request for order %s (already paid: %s)\n",
   1285                   god->order_id,
   1286                   already_paid_order_id);
   1287       ret = send_pay_request (god,
   1288                               already_paid_order_id);
   1289       GNUNET_free (already_paid_order_id);
   1290       return ret;
   1291     }
   1292     GNUNET_free (already_paid_order_id);
   1293   }
   1294   god->phase++;
   1295   return false;
   1296 }
   1297 
   1298 
   1299 /**
   1300  * Check if the order has been paid, and if not
   1301  * request payment.
   1302  *
   1303  * @param[in,out] god request context
   1304  * @return true to exit due to suspension
   1305  */
   1306 static bool
   1307 phase_handle_unpaid (struct GetOrderData *god)
   1308 {
   1309   if (god->paid)
   1310   {
   1311     god->phase++;
   1312     return false;
   1313   }
   1314   if (god->claimed)
   1315   {
   1316     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1317                 "Order claimed but unpaid, sending pay request for order %s\n",
   1318                 god->order_id);
   1319   }
   1320   else
   1321   {
   1322     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1323                 "Order unclaimed, sending pay request for order %s\n",
   1324                 god->order_id);
   1325   }
   1326   return send_pay_request (god,
   1327                            NULL);
   1328 }
   1329 
   1330 
   1331 /**
   1332  * Function called with detailed information about a refund.
   1333  * It is responsible for packing up the data to return.
   1334  *
   1335  * @param cls closure
   1336  * @param refund_serial unique serial number of the refund
   1337  * @param timestamp time of the refund (for grouping of refunds in the wallet UI)
   1338  * @param coin_pub public coin from which the refund comes from
   1339  * @param exchange_url URL of the exchange that issued @a coin_pub
   1340  * @param rtransaction_id identificator of the refund
   1341  * @param reason human-readable explanation of the refund
   1342  * @param refund_amount refund amount which is being taken from @a coin_pub
   1343  * @param pending true if the this refund was not yet processed by the wallet/exchange
   1344  */
   1345 static void
   1346 process_refunds_cb (void *cls,
   1347                     uint64_t refund_serial,
   1348                     struct GNUNET_TIME_Timestamp timestamp,
   1349                     const struct TALER_CoinSpendPublicKeyP *coin_pub,
   1350                     const char *exchange_url,
   1351                     uint64_t rtransaction_id,
   1352                     const char *reason,
   1353                     const struct TALER_Amount *refund_amount,
   1354                     bool pending)
   1355 {
   1356   struct GetOrderData *god = cls;
   1357 
   1358   (void) refund_serial;
   1359   (void) timestamp;
   1360   (void) exchange_url;
   1361   (void) rtransaction_id;
   1362   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1363               "Found refund of %s for coin %s with reason `%s' in database\n",
   1364               TALER_amount2s (refund_amount),
   1365               TALER_B2S (coin_pub),
   1366               reason);
   1367   god->refund_pending |= pending;
   1368   if ( (GNUNET_OK !=
   1369         TALER_amount_cmp_currency (&god->refund_taken,
   1370                                    refund_amount)) ||
   1371        (GNUNET_OK !=
   1372         TALER_amount_cmp_currency (&god->refund_amount,
   1373                                    refund_amount)) )
   1374   {
   1375     god->bad_refund_currency_in_db = true;
   1376     return;
   1377   }
   1378   if (! pending)
   1379   {
   1380     GNUNET_assert (0 <=
   1381                    TALER_amount_add (&god->refund_taken,
   1382                                      &god->refund_taken,
   1383                                      refund_amount));
   1384   }
   1385   GNUNET_assert (0 <=
   1386                  TALER_amount_add (&god->refund_amount,
   1387                                    &god->refund_amount,
   1388                                    refund_amount));
   1389   god->refunded = true;
   1390 }
   1391 
   1392 
   1393 /**
   1394  * Check if the order has been refunded.
   1395  *
   1396  * @param[in,out] god request context
   1397  * @return true to exit due to suspension
   1398  */
   1399 static bool
   1400 phase_check_refunded (struct GetOrderData *god)
   1401 {
   1402   enum GNUNET_DB_QueryStatus qs;
   1403   struct TALER_Amount refund_amount;
   1404   const char *refund_currency;
   1405 
   1406   GNUNET_assert (NULL != god->contract_terms);
   1407   switch (god->ct->version)
   1408   {
   1409   case TALER_MERCHANT_CONTRACT_VERSION_0:
   1410     refund_amount = god->pc->details.v0.brutto;
   1411     refund_currency = god->pc->details.v0.brutto.currency;
   1412     break;
   1413   case TALER_MERCHANT_CONTRACT_VERSION_1:
   1414     if (god->choice_index < 0)
   1415     {
   1416       // order was not paid, no refund to be checked
   1417       god->phase++;
   1418       return false;
   1419     }
   1420     GNUNET_assert (god->choice_index <
   1421                    god->pc->details.v1.choices_len);
   1422     refund_currency = god->pc->details.v1.choices[
   1423       god->choice_index].amount.currency;
   1424     GNUNET_assert (GNUNET_OK ==
   1425                    TALER_amount_set_zero (refund_currency,
   1426                                           &refund_amount));
   1427     break;
   1428   default:
   1429     {
   1430       GNUNET_break (0);
   1431       phase_fail (god,
   1432                   MHD_HTTP_INTERNAL_SERVER_ERROR,
   1433                   TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_VERSION,
   1434                   NULL);
   1435       return false;
   1436     }
   1437   }
   1438 
   1439   if ( (god->sc.awaiting_refund) &&
   1440        (GNUNET_OK !=
   1441         TALER_amount_cmp_currency (&refund_amount,
   1442                                    &god->sc.refund_expected)) )
   1443   {
   1444     GNUNET_break (0);
   1445     phase_fail (god,
   1446                 MHD_HTTP_CONFLICT,
   1447                 TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1448                 refund_currency);
   1449     return false;
   1450   }
   1451 
   1452   /* At this point, we know the contract was paid. Let's check for
   1453      refunds. First, clear away refunds found from previous invocations. */
   1454   GNUNET_assert (GNUNET_OK ==
   1455                  TALER_amount_set_zero (refund_currency,
   1456                                         &god->refund_amount));
   1457   GNUNET_assert (GNUNET_OK ==
   1458                  TALER_amount_set_zero (refund_currency,
   1459                                         &god->refund_taken));
   1460   qs = TALER_MERCHANTDB_iterate_refunds_detailed (
   1461     TMH_db,
   1462     god->hc->instance->settings.id,
   1463     &god->h_contract_terms,
   1464     &process_refunds_cb,
   1465     god);
   1466   if (0 > qs)
   1467   {
   1468     GNUNET_break (0);
   1469     phase_fail (god,
   1470                 MHD_HTTP_INTERNAL_SERVER_ERROR,
   1471                 TALER_EC_GENERIC_DB_FETCH_FAILED,
   1472                 "iterate_refunds_detailed");
   1473     return false;
   1474   }
   1475   if (god->bad_refund_currency_in_db)
   1476   {
   1477     GNUNET_break (0);
   1478     phase_fail (god,
   1479                 MHD_HTTP_INTERNAL_SERVER_ERROR,
   1480                 TALER_EC_GENERIC_DB_FETCH_FAILED,
   1481                 "currency mix-up between contract price and refunds in database");
   1482     return false;
   1483   }
   1484   if ( ((god->sc.awaiting_refund) &&
   1485         ( (! god->refunded) ||
   1486           (1 != TALER_amount_cmp (&god->refund_amount,
   1487                                   &god->sc.refund_expected)) )) ||
   1488        ( (god->sc.awaiting_refund_obtained) &&
   1489          (god->refund_pending) ) )
   1490   {
   1491     /* Client is waiting for a refund larger than what we have, suspend
   1492        until timeout */
   1493     struct GNUNET_TIME_Relative remaining;
   1494 
   1495     remaining = GNUNET_TIME_absolute_get_remaining (god->sc.long_poll_timeout);
   1496     if ( (! GNUNET_TIME_relative_is_zero (remaining)) &&
   1497          (! god->generate_html) )
   1498     {
   1499       /* yes, indeed suspend */
   1500       if (god->sc.awaiting_refund)
   1501         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1502                     "Awaiting refund exceeding %s\n",
   1503                     TALER_amount2s (&god->sc.refund_expected));
   1504       if (god->sc.awaiting_refund_obtained)
   1505         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1506                     "Awaiting pending refunds\n");
   1507       suspend_god (god);
   1508       return true;
   1509     }
   1510   }
   1511   god->phase++;
   1512   return false;
   1513 }
   1514 
   1515 
   1516 /**
   1517  * Create a taler://refund/ URI for the given @a con and @a order_id
   1518  * and @a instance_id.
   1519  *
   1520  * @param merchant_base_url URL to take host and path from;
   1521  *        we cannot take it from the MHD connection as a browser
   1522  *        may have changed 'http' to 'https' and we MUST be consistent
   1523  *        with what the merchant's frontend used initially
   1524  * @param order_id the order id
   1525  * @return corresponding taler://refund/ URI, or NULL on missing "host"
   1526  */
   1527 static char *
   1528 make_taler_refund_uri (const char *merchant_base_url,
   1529                        const char *order_id)
   1530 {
   1531   struct GNUNET_Buffer buf = { 0 };
   1532   char *url;
   1533   struct GNUNET_Uri uri;
   1534 
   1535   url = GNUNET_strdup (merchant_base_url);
   1536   if (-1 == GNUNET_uri_parse (&uri,
   1537                               url))
   1538   {
   1539     GNUNET_break (0);
   1540     GNUNET_free (url);
   1541     return NULL;
   1542   }
   1543   GNUNET_assert (NULL != order_id);
   1544   GNUNET_buffer_write_str (&buf,
   1545                            "taler");
   1546   if (0 == strcasecmp ("http",
   1547                        uri.scheme))
   1548     GNUNET_buffer_write_str (&buf,
   1549                              "+http");
   1550   GNUNET_buffer_write_str (&buf,
   1551                            "://refund/");
   1552   GNUNET_buffer_write_str (&buf,
   1553                            uri.host);
   1554   if (0 != uri.port)
   1555     GNUNET_buffer_write_fstr (&buf,
   1556                               ":%u",
   1557                               (unsigned int) uri.port);
   1558   if (NULL != uri.path)
   1559     GNUNET_buffer_write_path (&buf,
   1560                               uri.path);
   1561   GNUNET_buffer_write_path (&buf,
   1562                             order_id);
   1563   GNUNET_buffer_write_path (&buf,
   1564                             ""); // Trailing slash
   1565   GNUNET_free (url);
   1566   return GNUNET_buffer_reap_str (&buf);
   1567 }
   1568 
   1569 
   1570 /**
   1571  * Generate the order status response.
   1572  *
   1573  * @param[in,out] god request context
   1574  */
   1575 static void
   1576 phase_return_status (struct GetOrderData *god)
   1577 {
   1578   /* All operations done, build final response */
   1579   if (! god->generate_html)
   1580   {
   1581     phase_end (god,
   1582                TALER_MHD_REPLY_JSON_PACK (
   1583                  god->sc.con,
   1584                  MHD_HTTP_OK,
   1585                  GNUNET_JSON_pack_allow_null (
   1586                    GNUNET_JSON_pack_string ("fulfillment_url",
   1587                                             god->ct->fulfillment_url)),
   1588                  GNUNET_JSON_pack_bool ("refunded",
   1589                                         god->refunded),
   1590                  GNUNET_JSON_pack_bool ("refund_pending",
   1591                                         god->refund_pending),
   1592                  TALER_JSON_pack_amount ("refund_taken",
   1593                                          &god->refund_taken),
   1594                  TALER_JSON_pack_amount ("refund_amount",
   1595                                          &god->refund_amount)));
   1596     return;
   1597   }
   1598 
   1599   if (god->refund_pending)
   1600   {
   1601     char *qr;
   1602     char *uri;
   1603 
   1604     GNUNET_assert (NULL != god->contract_terms_json);
   1605     GNUNET_assert (NULL != god->contract_terms);
   1606     uri = make_taler_refund_uri (god->pc->merchant_base_url,
   1607                                  god->order_id);
   1608     if (NULL == uri)
   1609     {
   1610       GNUNET_break (0);
   1611       phase_fail (god,
   1612                   MHD_HTTP_INTERNAL_SERVER_ERROR,
   1613                   TALER_EC_GENERIC_ALLOCATION_FAILURE,
   1614                   "refund URI");
   1615       return;
   1616     }
   1617     qr = TMH_create_qrcode (uri);
   1618     if (NULL == qr)
   1619     {
   1620       GNUNET_break (0);
   1621       GNUNET_free (uri);
   1622       phase_fail (god,
   1623                   MHD_HTTP_INTERNAL_SERVER_ERROR,
   1624                   TALER_EC_GENERIC_ALLOCATION_FAILURE,
   1625                   "qr code");
   1626       return;
   1627     }
   1628 
   1629     {
   1630       enum GNUNET_GenericReturnValue res;
   1631       json_t *context;
   1632 
   1633       context = GNUNET_JSON_PACK (
   1634         GNUNET_JSON_pack_string ("order_summary",
   1635                                  get_order_summary (god)),
   1636         TALER_JSON_pack_amount ("refund_amount",
   1637                                 &god->refund_amount),
   1638         TALER_JSON_pack_amount ("refund_taken",
   1639                                 &god->refund_taken),
   1640         GNUNET_JSON_pack_string ("taler_refund_uri",
   1641                                  uri),
   1642         GNUNET_JSON_pack_string ("taler_refund_qrcode_svg",
   1643                                  qr));
   1644       res = TALER_TEMPLATING_reply (
   1645         god->sc.con,
   1646         MHD_HTTP_OK,
   1647         "offer_refund",
   1648         god->hc->instance->settings.id,
   1649         uri,
   1650         context);
   1651       GNUNET_break (GNUNET_OK == res);
   1652       json_decref (context);
   1653       phase_end (god,
   1654                  (GNUNET_SYSERR == res)
   1655                  ? MHD_NO
   1656                  : MHD_YES);
   1657     }
   1658     GNUNET_free (uri);
   1659     GNUNET_free (qr);
   1660     return;
   1661   }
   1662 
   1663   {
   1664     enum GNUNET_GenericReturnValue res;
   1665     json_t *context;
   1666 
   1667     context = GNUNET_JSON_PACK (
   1668       GNUNET_JSON_pack_object_incref ("contract_terms",
   1669                                       NULL != god->contract_terms_json
   1670                                       ? god->contract_terms_json
   1671                                       : god->order_json),
   1672       GNUNET_JSON_pack_string ("order_summary",
   1673                                get_order_summary (god)),
   1674       TALER_JSON_pack_amount ("refund_amount",
   1675                               &god->refund_amount),
   1676       TALER_JSON_pack_amount ("refund_taken",
   1677                               &god->refund_taken));
   1678     res = TALER_TEMPLATING_reply (
   1679       god->sc.con,
   1680       MHD_HTTP_OK,
   1681       "show_order_details",
   1682       god->hc->instance->settings.id,
   1683       NULL,
   1684       context);
   1685     GNUNET_break (GNUNET_OK == res);
   1686     json_decref (context);
   1687     phase_end (god,
   1688                (GNUNET_SYSERR == res)
   1689                ? MHD_NO
   1690                : MHD_YES);
   1691   }
   1692 }
   1693 
   1694 
   1695 enum MHD_Result
   1696 TMH_get_orders_ID (const struct TMH_RequestHandler *rh,
   1697                    struct MHD_Connection *connection,
   1698                    struct TMH_HandlerContext *hc)
   1699 {
   1700   struct GetOrderData *god = hc->ctx;
   1701 
   1702   (void) rh;
   1703   if (NULL == god)
   1704   {
   1705     god = GNUNET_new (struct GetOrderData);
   1706     hc->ctx = god;
   1707     hc->cc = &god_cleanup;
   1708     god->sc.con = connection;
   1709     god->hc = hc;
   1710     god->order_id = hc->infix;
   1711     god->generate_html
   1712       = TMH_MHD_test_html_desired (connection);
   1713 
   1714     /* first-time initialization / sanity checks */
   1715     TALER_MHD_parse_request_arg_auto (connection,
   1716                                       "h_contract",
   1717                                       &god->h_contract_terms,
   1718                                       god->h_contract_provided);
   1719     TALER_MHD_parse_request_arg_auto (connection,
   1720                                       "token",
   1721                                       &god->claim_token,
   1722                                       god->claim_token_provided);
   1723     if (! (TALER_MHD_arg_to_yna (connection,
   1724                                  "allow_refunded_for_repurchase",
   1725                                  TALER_EXCHANGE_YNA_NO,
   1726                                  &god->allow_refunded_for_repurchase)) )
   1727       return TALER_MHD_reply_with_error (connection,
   1728                                          MHD_HTTP_BAD_REQUEST,
   1729                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1730                                          "allow_refunded_for_repurchase");
   1731     god->session_id = MHD_lookup_connection_value (connection,
   1732                                                    MHD_GET_ARGUMENT_KIND,
   1733                                                    "session_id");
   1734 
   1735     /* process await_refund_obtained argument */
   1736     {
   1737       const char *await_refund_obtained_s;
   1738 
   1739       await_refund_obtained_s =
   1740         MHD_lookup_connection_value (connection,
   1741                                      MHD_GET_ARGUMENT_KIND,
   1742                                      "await_refund_obtained");
   1743       god->sc.awaiting_refund_obtained =
   1744         (NULL != await_refund_obtained_s)
   1745         ? 0 == strcasecmp (await_refund_obtained_s,
   1746                            "yes")
   1747         : false;
   1748       if (god->sc.awaiting_refund_obtained)
   1749         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1750                     "Awaiting refund obtained\n");
   1751     }
   1752 
   1753     TALER_MHD_parse_request_amount (connection,
   1754                                     "refund",
   1755                                     &god->sc.refund_expected);
   1756     if (TALER_amount_is_valid (&god->sc.refund_expected))
   1757     {
   1758       god->sc.awaiting_refund = true;
   1759       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1760                   "Awaiting minimum refund of %s\n",
   1761                   TALER_amount2s (&god->sc.refund_expected));
   1762     }
   1763     TALER_MHD_parse_request_timeout (connection,
   1764                                      &god->sc.long_poll_timeout);
   1765   }
   1766 
   1767   if (GNUNET_SYSERR == god->suspended)
   1768     return MHD_NO; /* we are in shutdown */
   1769   if (GNUNET_YES == god->suspended)
   1770   {
   1771     god->suspended = GNUNET_NO;
   1772     GNUNET_CONTAINER_DLL_remove (god_head,
   1773                                  god_tail,
   1774                                  god);
   1775   }
   1776 
   1777   while (1)
   1778   {
   1779     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1780                 "Handling request in phase %d\n",
   1781                 (int) god->phase);
   1782     switch (god->phase)
   1783     {
   1784     case GOP_INIT:
   1785       phase_init (god);
   1786       break;
   1787     case GOP_LOOKUP_TERMS:
   1788       phase_lookup_terms (god);
   1789       break;
   1790     case GOP_PARSE_CONTRACT:
   1791       phase_parse_contract (god);
   1792       break;
   1793     case GOP_CHECK_CLIENT_ACCESS:
   1794       phase_check_client_access (god);
   1795       break;
   1796     case GOP_CHECK_PAID:
   1797       phase_check_paid (god);
   1798       break;
   1799     case GOP_REDIRECT_TO_PAID_ORDER:
   1800       if (phase_redirect_to_paid_order (god))
   1801         return MHD_YES;
   1802       break;
   1803     case GOP_HANDLE_UNPAID:
   1804       if (phase_handle_unpaid (god))
   1805         return MHD_YES;
   1806       break;
   1807     case GOP_CHECK_REFUNDED:
   1808       if (phase_check_refunded (god))
   1809         return MHD_YES;
   1810       break;
   1811     case GOP_RETURN_STATUS:
   1812       phase_return_status (god);
   1813       break;
   1814     case GOP_RETURN_MHD_YES:
   1815       return MHD_YES;
   1816     case GOP_RETURN_MHD_NO:
   1817       return MHD_NO;
   1818     }
   1819   }
   1820 }
   1821 
   1822 
   1823 /* end of taler-merchant-httpd_get-orders-ORDER_ID.c */