merchant

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

taler-merchant-httpd_post-templates-TEMPLATE_ID.c (66714B)


      1 /*
      2   This file is part of TALER
      3   (C) 2022-2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU Affero General Public License as
      7   published by the Free Software Foundation; either version 3,
      8   or (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful, but
     11   WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public
     16   License along with TALER; see the file COPYING.  If not,
     17   see <http://www.gnu.org/licenses/>
     18 */
     19 
     20 /**
     21  * @file src/backend/taler-merchant-httpd_post-templates-TEMPLATE_ID.c
     22  * @brief implementing POST /using-templates request handling
     23  * @author Priscilla HUANG
     24  * @author Christian Grothoff
     25  */
     26 #include "platform.h"
     27 #include "taler-merchant-httpd_exchanges.h"
     28 #include "taler-merchant-httpd_post-templates-TEMPLATE_ID.h"
     29 #include "taler-merchant-httpd_post-private-orders.h"
     30 #include "taler-merchant-httpd_helper.h"
     31 #include "taler-merchant-httpd_get-exchanges.h"
     32 #include "taler/taler_merchant_util.h"
     33 #include <taler/taler_json_lib.h>
     34 #include <regex.h>
     35 #include "merchant-database/get_product.h"
     36 #include "merchant-database/get_template.h"
     37 
     38 
     39 /**
     40  * Maximum number of entries we accept in the @e inventory_selection
     41  * array of a request.  Each entry costs us one database round-trip
     42  * and a full copy of the product details (including the base64-encoded
     43  * product image), so this must be bounded independently of the maximum
     44  * upload size.
     45  */
     46 #define MAX_INVENTORY_SELECTION 1024
     47 
     48 
     49 /**
     50  * Amount the client chose for one of the choices of a paivana template.
     51  */
     52 struct PaivanaChoiceAmount
     53 {
     54   /**
     55    * Index into the @e choices array of the template contract.
     56    */
     57   uint32_t choice_index;
     58 
     59   /**
     60    * Amount to use for that choice, excluding any tip.
     61    */
     62   struct TALER_Amount amount;
     63 };
     64 
     65 
     66 /**
     67  * Item selected from inventory_selection.
     68  */
     69 struct InventoryTemplateItemContext
     70 {
     71   /**
     72    * Product ID as referenced in inventory.
     73    */
     74   const char *product_id;
     75 
     76   /**
     77    * Unit quantity string as provided by the client.
     78    */
     79   const char *unit_quantity;
     80 
     81   /**
     82    * Parsed integer quantity.
     83    */
     84   uint64_t quantity_value;
     85 
     86   /**
     87    * Parsed fractional quantity.
     88    */
     89   uint32_t quantity_frac;
     90 
     91   /**
     92    * Product details from the DB (includes price array).
     93    */
     94   struct TALER_MERCHANTDB_ProductDetails pd;
     95 
     96   /**
     97    * Categories referenced by the product.
     98    */
     99   uint64_t *categories;
    100 
    101   /**
    102    * Length of @e categories.
    103    */
    104   size_t num_categories;
    105 };
    106 
    107 
    108 /**
    109  * Our context.
    110  */
    111 enum UsePhase
    112 {
    113   /**
    114    * Parse request payload into context fields.
    115    */
    116   USE_PHASE_PARSE_REQUEST,
    117 
    118   /**
    119    * Fetch template details from the database.
    120    */
    121   USE_PHASE_LOOKUP_TEMPLATE,
    122 
    123   /**
    124    * Parse template.
    125    */
    126   USE_PHASE_PARSE_TEMPLATE,
    127 
    128   /**
    129    * Load additional details (like products and
    130    * categories) needed for verification and
    131    * price computation.
    132    */
    133   USE_PHASE_DB_FETCH,
    134 
    135   /**
    136    * Validate request and template compatibility.
    137    */
    138   USE_PHASE_VERIFY,
    139 
    140   /**
    141    * Compute price of the order.
    142    */
    143   USE_PHASE_COMPUTE_PRICE,
    144 
    145   /**
    146    * Handle tip.
    147    */
    148   USE_PHASE_CHECK_TIP,
    149 
    150   /**
    151    * Check if client-supplied total amount matches
    152    * our calculation (if we did any).
    153    */
    154   USE_PHASE_CHECK_TOTAL,
    155 
    156   /**
    157    * Construct the internal order request body.
    158    */
    159   USE_PHASE_CREATE_ORDER,
    160 
    161   /**
    162    * Submit the order to the shared order handler.
    163    */
    164   USE_PHASE_SUBMIT_ORDER,
    165 
    166   /**
    167    * Finished successfully with MHD_YES.
    168    */
    169   USE_PHASE_FINISHED_MHD_YES,
    170 
    171   /**
    172    * Finished with MHD_NO.
    173    */
    174   USE_PHASE_FINISHED_MHD_NO
    175 };
    176 
    177 struct UseContext
    178 {
    179   /**
    180    * Context for our handler.
    181    */
    182   struct TMH_HandlerContext *hc;
    183 
    184   /**
    185    * Internal handler context we are passing into the
    186    * POST /private/orders handler.
    187    */
    188   struct TMH_HandlerContext ihc;
    189 
    190   /**
    191    * Phase we are currently in.
    192    */
    193   enum UsePhase phase;
    194 
    195   /**
    196    * Template type from the contract.
    197    */
    198   enum TALER_MERCHANT_TemplateType template_type;
    199 
    200   /**
    201    * Information set in the #USE_PHASE_PARSE_REQUEST phase.
    202    */
    203   struct
    204   {
    205     /**
    206      * Summary override from request, if any.
    207      */
    208     const char *summary;
    209 
    210     /**
    211      * Amount provided by the client.
    212      */
    213     struct TALER_Amount amount;
    214 
    215     /**
    216      * Tip provided by the client.
    217      */
    218     struct TALER_Amount tip;
    219 
    220     /**
    221      * True if @e amount was not provided.
    222      */
    223     bool no_amount;
    224 
    225     /**
    226      * Challenge from the offline verifier, to be signed once the
    227      * order is paid.
    228      */
    229     struct TALER_PosChallengeP challenge;
    230 
    231     /**
    232      * True if @e challenge was not provided.
    233      */
    234     bool no_challenge;
    235 
    236     /**
    237      * True if @e tip was not provided.
    238      */
    239     bool no_tip;
    240 
    241     /**
    242      * Parsed fields for inventory templates.
    243      */
    244     struct
    245     {
    246       /**
    247        * Selected products from inventory_selection.
    248        */
    249       struct InventoryTemplateItemContext *items;
    250 
    251       /**
    252        * Length of @e items.
    253        */
    254       unsigned int items_len;
    255 
    256     } inventory;
    257 
    258     /**
    259      * Request details if this is a paivana instantiation.
    260      */
    261     struct
    262     {
    263 
    264       /**
    265        * Target website for the request.
    266        */
    267       const char *website;
    268 
    269       /**
    270        * Unique client identifier, consisting of
    271        * current time, "-", and the hash of a nonce,
    272        * the website and the current time.
    273        */
    274       const char *paivana_id;
    275 
    276       /**
    277        * Amounts the client picked for those choices of the
    278        * template that allow the amount to be edited.
    279        */
    280       struct PaivanaChoiceAmount *choice_amounts;
    281 
    282       /**
    283        * Length of the @e choice_amounts array.
    284        */
    285       unsigned int choice_amounts_len;
    286 
    287     } paivana;
    288 
    289   } parse_request;
    290 
    291   /**
    292    * Information set in the #USE_PHASE_LOOKUP_TEMPLATE phase.
    293    */
    294   struct
    295   {
    296 
    297     /**
    298      * Our template details from the DB.
    299      */
    300     struct TALER_MERCHANTDB_TemplateDetails etp;
    301 
    302   } lookup_template;
    303 
    304   /**
    305    * Information set in the #USE_PHASE_PARSE_TEMPLATE phase.
    306    */
    307   struct TALER_MERCHANT_TemplateContract template_contract;
    308 
    309   /**
    310    * Information set in the #USE_PHASE_COMPUTE_PRICE phase.
    311    */
    312   struct
    313   {
    314 
    315     /**
    316      * Per-currency totals across selected products (without tips).
    317      */
    318     struct TALER_Amount *totals;
    319 
    320     /**
    321      * Length of @e totals.
    322      */
    323     unsigned int totals_len;
    324 
    325     /**
    326      * Array of payment choices, used with Paviana.
    327      */
    328     json_t *choices;
    329 
    330   } compute_price;
    331 
    332 };
    333 
    334 
    335 /**
    336  * Clean up inventory items.
    337  *
    338  * @param items_len length of @a items
    339  * @param[in] items item array to free
    340  */
    341 static void
    342 cleanup_inventory_items (
    343   unsigned int items_len,
    344   struct InventoryTemplateItemContext items[static items_len])
    345 {
    346   for (unsigned int i = 0; i < items_len; i++)
    347   {
    348     struct InventoryTemplateItemContext *item = &items[i];
    349 
    350     TALER_MERCHANTDB_product_details_free (&item->pd);
    351     GNUNET_free (item->categories);
    352   }
    353   GNUNET_free (items);
    354 }
    355 
    356 
    357 /**
    358  * Clean up a `struct UseContext *`
    359  *
    360  * @param[in] cls a `struct UseContext *`
    361  */
    362 static void
    363 cleanup_use_context (void *cls)
    364 {
    365   struct UseContext *uc = cls;
    366 
    367   TALER_MERCHANTDB_template_details_free (&uc->lookup_template.etp);
    368   if (NULL !=
    369       uc->parse_request.inventory.items)
    370     cleanup_inventory_items (uc->parse_request.inventory.items_len,
    371                              uc->parse_request.inventory.items);
    372   GNUNET_array_grow (uc->parse_request.paivana.choice_amounts,
    373                      uc->parse_request.paivana.choice_amounts_len,
    374                      0);
    375   TALER_MERCHANT_template_contract_free (&uc->template_contract);
    376   GNUNET_free (uc->compute_price.totals);
    377   uc->compute_price.totals_len = 0;
    378   json_decref (uc->compute_price.choices);
    379   if (NULL != uc->ihc.cc)
    380     uc->ihc.cc (uc->ihc.ctx);
    381   GNUNET_free (uc->ihc.infix);
    382   json_decref (uc->ihc.request_body);
    383   GNUNET_free (uc);
    384 }
    385 
    386 
    387 /**
    388  * Finalize a template use request.
    389  *
    390  * @param[in,out] uc use context
    391  * @param ret handler return value
    392  */
    393 static void
    394 use_finalize (struct UseContext *uc,
    395               enum MHD_Result ret)
    396 {
    397   uc->phase = (MHD_YES == ret)
    398     ? USE_PHASE_FINISHED_MHD_YES
    399     : USE_PHASE_FINISHED_MHD_NO;
    400 }
    401 
    402 
    403 /**
    404  * Finalize after JSON parsing result.
    405  *
    406  * @param[in,out] uc use context
    407  * @param res parse result
    408  */
    409 static void
    410 use_finalize_parse (struct UseContext *uc,
    411                     enum GNUNET_GenericReturnValue res)
    412 {
    413   GNUNET_assert (GNUNET_OK != res);
    414   use_finalize (uc,
    415                 (GNUNET_NO == res)
    416                 ? MHD_YES
    417                 : MHD_NO);
    418 }
    419 
    420 
    421 /**
    422  * Reply with error and finalize the request.
    423  *
    424  * @param[in,out] uc use context
    425  * @param http_status HTTP status code
    426  * @param ec error code
    427  * @param detail error detail
    428  */
    429 static void
    430 use_reply_with_error (struct UseContext *uc,
    431                       unsigned int http_status,
    432                       enum TALER_ErrorCode ec,
    433                       const char *detail)
    434 {
    435   enum MHD_Result mret;
    436 
    437   mret = TALER_MHD_reply_with_error (uc->hc->connection,
    438                                      http_status,
    439                                      ec,
    440                                      detail);
    441   use_finalize (uc,
    442                 mret);
    443 }
    444 
    445 
    446 /* ***************** USE_PHASE_PARSE_REQUEST **************** */
    447 
    448 /**
    449  * Parse request data for inventory templates.
    450  *
    451  * @param[in,out] uc use context
    452  * @return #GNUNET_OK on success
    453  */
    454 static enum GNUNET_GenericReturnValue
    455 parse_using_templates_inventory_request (
    456   struct UseContext *uc)
    457 {
    458   const json_t *inventory_selection;
    459   struct GNUNET_JSON_Specification spec[] = {
    460     GNUNET_JSON_spec_array_const ("inventory_selection",
    461                                   &inventory_selection),
    462     GNUNET_JSON_spec_end ()
    463   };
    464   enum GNUNET_GenericReturnValue res;
    465 
    466   GNUNET_assert (NULL == uc->ihc.request_body);
    467   res = TALER_MHD_parse_json_data (uc->hc->connection,
    468                                    uc->hc->request_body,
    469                                    spec);
    470   if (GNUNET_OK != res)
    471   {
    472     GNUNET_break_op (0);
    473     use_finalize_parse (uc,
    474                         res);
    475     return GNUNET_SYSERR;
    476   }
    477 
    478   if ( (! uc->parse_request.no_amount) &&
    479        (! TMH_test_exchange_configured_for_currency (
    480           uc->parse_request.amount.currency)) )
    481   {
    482     GNUNET_break_op (0);
    483     use_reply_with_error (uc,
    484                           MHD_HTTP_CONFLICT,
    485                           TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
    486                           "Currency is not supported by backend");
    487     return GNUNET_SYSERR;
    488   }
    489 
    490   if (MAX_INVENTORY_SELECTION < json_array_size (inventory_selection))
    491   {
    492     GNUNET_break_op (0);
    493     use_reply_with_error (uc,
    494                           MHD_HTTP_BAD_REQUEST,
    495                           TALER_EC_GENERIC_PARAMETER_MALFORMED,
    496                           "inventory_selection (too many entries)");
    497     return GNUNET_SYSERR;
    498   }
    499   for (size_t i = 0; i < json_array_size (inventory_selection); i++)
    500   {
    501     struct InventoryTemplateItemContext item = { 0 };
    502     struct GNUNET_JSON_Specification ispec[] = {
    503       TALER_JSON_spec_slug ("product_id",
    504                             &item.product_id),
    505       GNUNET_JSON_spec_string ("quantity",
    506                                &item.unit_quantity),
    507       GNUNET_JSON_spec_end ()
    508     };
    509     const char *err_name;
    510     unsigned int err_line;
    511 
    512     res = GNUNET_JSON_parse (json_array_get (inventory_selection,
    513                                              i),
    514                              ispec,
    515                              &err_name,
    516                              &err_line);
    517     if (GNUNET_OK != res)
    518     {
    519       GNUNET_break_op (0);
    520       use_reply_with_error (uc,
    521                             MHD_HTTP_BAD_REQUEST,
    522                             TALER_EC_GENERIC_PARAMETER_MALFORMED,
    523                             "inventory_selection");
    524       return GNUNET_SYSERR;
    525     }
    526 
    527     GNUNET_array_append (uc->parse_request.inventory.items,
    528                          uc->parse_request.inventory.items_len,
    529                          item);
    530   }
    531   return GNUNET_OK;
    532 }
    533 
    534 
    535 /**
    536  * Parse request data for paivana templates.
    537  *
    538  * @param[in,out] uc use context
    539  * @return #GNUNET_OK on success
    540  */
    541 static enum GNUNET_GenericReturnValue
    542 parse_using_templates_paivana_request (
    543   struct UseContext *uc)
    544 {
    545   const json_t *choice_amounts = NULL;
    546   struct GNUNET_JSON_Specification spec[] = {
    547     TALER_JSON_spec_web_url ("website",
    548                              &uc->parse_request.paivana.website),
    549     GNUNET_JSON_spec_string ("paivana_id",
    550                              &uc->parse_request.paivana.paivana_id),
    551     GNUNET_JSON_spec_mark_optional (
    552       GNUNET_JSON_spec_array_const ("choice_amounts",
    553                                     &choice_amounts),
    554       NULL),
    555     GNUNET_JSON_spec_end ()
    556   };
    557   enum GNUNET_GenericReturnValue res;
    558   unsigned long long tv;
    559   const char *dash;
    560 
    561   GNUNET_assert (NULL == uc->ihc.request_body);
    562   res = TALER_MHD_parse_json_data (uc->hc->connection,
    563                                    uc->hc->request_body,
    564                                    spec);
    565   if (GNUNET_OK != res)
    566   {
    567     GNUNET_break_op (0);
    568     use_finalize_parse (uc,
    569                         res);
    570     return GNUNET_SYSERR;
    571   }
    572   if (NULL != choice_amounts)
    573   {
    574     if (! uc->parse_request.no_amount)
    575     {
    576       /* 'amount' is the shorthand for a template with a single
    577          editable choice; using both is ambiguous. */
    578       GNUNET_break_op (0);
    579       use_reply_with_error (
    580         uc,
    581         MHD_HTTP_CONFLICT,
    582         TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
    583         "amount and choice_amounts are mutually exclusive");
    584       return GNUNET_SYSERR;
    585     }
    586     for (size_t i = 0; i < json_array_size (choice_amounts); i++)
    587     {
    588       struct PaivanaChoiceAmount ca;
    589       struct GNUNET_JSON_Specification ispec[] = {
    590         GNUNET_JSON_spec_uint32 ("choice_index",
    591                                  &ca.choice_index),
    592         TALER_JSON_spec_amount_any ("amount",
    593                                     &ca.amount),
    594         GNUNET_JSON_spec_end ()
    595       };
    596       const char *err_name;
    597       unsigned int err_line;
    598 
    599       if (GNUNET_OK !=
    600           GNUNET_JSON_parse (json_array_get (choice_amounts,
    601                                              i),
    602                              ispec,
    603                              &err_name,
    604                              &err_line))
    605       {
    606         GNUNET_break_op (0);
    607         use_reply_with_error (uc,
    608                               MHD_HTTP_BAD_REQUEST,
    609                               TALER_EC_GENERIC_PARAMETER_MALFORMED,
    610                               "choice_amounts");
    611         return GNUNET_SYSERR;
    612       }
    613       for (unsigned int j = 0;
    614            j < uc->parse_request.paivana.choice_amounts_len;
    615            j++)
    616       {
    617         if (uc->parse_request.paivana.choice_amounts[j].choice_index !=
    618             ca.choice_index)
    619           continue;
    620         GNUNET_break_op (0);
    621         use_reply_with_error (uc,
    622                               MHD_HTTP_BAD_REQUEST,
    623                               TALER_EC_GENERIC_PARAMETER_MALFORMED,
    624                               "choice_amounts::choice_index is not unique");
    625         return GNUNET_SYSERR;
    626       }
    627       /* The currency does not need to be checked against our
    628          configuration here: it must match the currency of the
    629          choice in the template, which the merchant picked. */
    630       GNUNET_array_append (uc->parse_request.paivana.choice_amounts,
    631                            uc->parse_request.paivana.choice_amounts_len,
    632                            ca);
    633     }
    634   }
    635   if (! TALER_is_session_id (uc->parse_request.paivana.paivana_id))
    636   {
    637     /* The Paivana ID becomes the session ID of the order, and thus
    638        ends up as a path component of the "taler://pay/" URI; the
    639        base64url decoding below is too lenient to ensure this. */
    640     GNUNET_break_op (0);
    641     use_reply_with_error (uc,
    642                           MHD_HTTP_BAD_REQUEST,
    643                           TALER_EC_GENERIC_PARAMETER_MALFORMED,
    644                           "paivana_id");
    645     return GNUNET_SYSERR;
    646   }
    647   if (1 !=
    648       sscanf (uc->parse_request.paivana.paivana_id,
    649               "%llu-",
    650               &tv))
    651   {
    652     GNUNET_break_op (0);
    653     use_reply_with_error (uc,
    654                           MHD_HTTP_BAD_REQUEST,
    655                           TALER_EC_GENERIC_PARAMETER_MALFORMED,
    656                           "paivana_id");
    657     return GNUNET_SYSERR;
    658   }
    659   dash = strchr (uc->parse_request.paivana.paivana_id,
    660                  '-');
    661   if (NULL == dash)
    662   {
    663     GNUNET_break_op (0);
    664     use_reply_with_error (uc,
    665                           MHD_HTTP_BAD_REQUEST,
    666                           TALER_EC_GENERIC_PARAMETER_MALFORMED,
    667                           "paivana_id");
    668     return GNUNET_SYSERR;
    669   }
    670   {
    671     size_t olen;
    672     void *out = NULL;
    673 
    674     olen = GNUNET_STRINGS_base64url_decode (dash + 1,
    675                                             strlen (dash + 1),
    676                                             &out);
    677     GNUNET_free (out);
    678     if (sizeof (struct GNUNET_ShortHashCode) != olen)
    679     {
    680       GNUNET_break_op (0);
    681       use_reply_with_error (uc,
    682                             MHD_HTTP_BAD_REQUEST,
    683                             TALER_EC_GENERIC_PARAMETER_MALFORMED,
    684                             "paivana_id");
    685       return GNUNET_SYSERR;
    686     }
    687   }
    688   return GNUNET_OK;
    689 }
    690 
    691 
    692 /**
    693  * Main function for the #USE_PHASE_PARSE_REQUEST.
    694  *
    695  * @param[in,out] uc context to update
    696  */
    697 static void
    698 handle_phase_parse_request (
    699   struct UseContext *uc)
    700 {
    701   const char *template_type = NULL;
    702   struct GNUNET_JSON_Specification spec[] = {
    703     GNUNET_JSON_spec_mark_optional (
    704       GNUNET_JSON_spec_string ("template_type",
    705                                &template_type),
    706       NULL),
    707     GNUNET_JSON_spec_mark_optional (
    708       TALER_JSON_spec_amount_any ("tip",
    709                                   &uc->parse_request.tip),
    710       &uc->parse_request.no_tip),
    711     GNUNET_JSON_spec_mark_optional (
    712       GNUNET_JSON_spec_string ("summary",
    713                                &uc->parse_request.summary),
    714       NULL),
    715     GNUNET_JSON_spec_mark_optional (
    716       TALER_JSON_spec_amount_any ("amount",
    717                                   &uc->parse_request.amount),
    718       &uc->parse_request.no_amount),
    719     GNUNET_JSON_spec_mark_optional (
    720       GNUNET_JSON_spec_fixed_auto ("challenge",
    721                                    &uc->parse_request.challenge),
    722       &uc->parse_request.no_challenge),
    723     GNUNET_JSON_spec_end ()
    724   };
    725   enum GNUNET_GenericReturnValue res;
    726 
    727   res = TALER_MHD_parse_json_data (uc->hc->connection,
    728                                    uc->hc->request_body,
    729                                    spec);
    730   if (GNUNET_OK != res)
    731   {
    732     GNUNET_break_op (0);
    733     use_finalize_parse (uc,
    734                         res);
    735     return;
    736   }
    737   if (NULL == template_type)
    738     template_type = "fixed-order";
    739   uc->template_type
    740     = TALER_MERCHANT_template_type_from_string (
    741         template_type);
    742   switch (uc->template_type)
    743   {
    744   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
    745     /* nothig left to do */
    746     uc->phase++;
    747     return;
    748   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
    749     res = parse_using_templates_paivana_request (uc);
    750     if (GNUNET_OK == res)
    751       uc->phase++;
    752     return;
    753   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
    754     res = parse_using_templates_inventory_request (uc);
    755     if (GNUNET_OK == res)
    756       uc->phase++;
    757     return;
    758   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
    759     break;
    760   }
    761   GNUNET_break (0);
    762   use_reply_with_error (
    763     uc,
    764     MHD_HTTP_BAD_REQUEST,
    765     TALER_EC_GENERIC_PARAMETER_MALFORMED,
    766     "template_type");
    767 }
    768 
    769 
    770 /* ***************** USE_PHASE_LOOKUP_TEMPLATE **************** */
    771 
    772 /**
    773  * Main function for the #USE_PHASE_LOOKUP_TEMPLATE.
    774  *
    775  * @param[in,out] uc context to update
    776  */
    777 static void
    778 handle_phase_lookup_template (
    779   struct UseContext *uc)
    780 {
    781   struct TMH_MerchantInstance *mi = uc->hc->instance;
    782   const char *template_id = uc->hc->infix;
    783   enum GNUNET_DB_QueryStatus qs;
    784 
    785   qs = TALER_MERCHANTDB_get_template (TMH_db,
    786                                       mi->settings.id,
    787                                       template_id,
    788                                       &uc->lookup_template.etp);
    789   switch (qs)
    790   {
    791   case GNUNET_DB_STATUS_HARD_ERROR:
    792     /* Clean up and fail hard */
    793     GNUNET_break (0);
    794     use_reply_with_error (uc,
    795                           MHD_HTTP_INTERNAL_SERVER_ERROR,
    796                           TALER_EC_GENERIC_DB_FETCH_FAILED,
    797                           "get_template");
    798     return;
    799   case GNUNET_DB_STATUS_SOFT_ERROR:
    800     /* this should be impossible (single select) */
    801     GNUNET_break (0);
    802     use_reply_with_error (uc,
    803                           MHD_HTTP_INTERNAL_SERVER_ERROR,
    804                           TALER_EC_GENERIC_DB_FETCH_FAILED,
    805                           "get_template");
    806     return;
    807   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    808     /* template not found! */
    809     use_reply_with_error (uc,
    810                           MHD_HTTP_NOT_FOUND,
    811                           TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN,
    812                           template_id);
    813     return;
    814   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    815     /* all good */
    816     break;
    817   }
    818   if (uc->template_type !=
    819       TALER_MERCHANT_template_type_from_contract (
    820         uc->lookup_template.etp.template_contract))
    821   {
    822     GNUNET_break_op (0);
    823     use_reply_with_error (
    824       uc,
    825       MHD_HTTP_CONFLICT,
    826       TALER_EC_MERCHANT_POST_USING_TEMPLATES_WRONG_TYPE,
    827       "template_contract has different type");
    828     return;
    829   }
    830   uc->phase++;
    831 }
    832 
    833 
    834 /* ***************** USE_PHASE_PARSE_TEMPLATE **************** */
    835 
    836 
    837 /**
    838  * Parse template.
    839  *
    840  * @param[in,out] uc use context
    841  */
    842 static void
    843 handle_phase_template_contract (struct UseContext *uc)
    844 {
    845   const char *err_name;
    846   enum GNUNET_GenericReturnValue res;
    847 
    848   res = TALER_MERCHANT_template_contract_parse (
    849     uc->lookup_template.etp.template_contract,
    850     &uc->template_contract,
    851     &err_name);
    852   if (GNUNET_OK != res)
    853   {
    854     GNUNET_break (0);
    855     use_reply_with_error (uc,
    856                           MHD_HTTP_INTERNAL_SERVER_ERROR,
    857                           TALER_EC_GENERIC_DB_FETCH_FAILED,
    858                           err_name);
    859     return;
    860   }
    861   uc->phase++;
    862 }
    863 
    864 
    865 /* ***************** USE_PHASE_DB_FETCH **************** */
    866 
    867 /**
    868  * Fetch DB data for inventory templates.
    869  *
    870  * @param[in,out] uc use context
    871  */
    872 static void
    873 handle_phase_db_fetch (struct UseContext *uc)
    874 {
    875   struct TMH_MerchantInstance *mi = uc->hc->instance;
    876 
    877   switch (uc->template_type)
    878   {
    879   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
    880     uc->phase++;
    881     return;
    882   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
    883     uc->phase++;
    884     return;
    885   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
    886     break;
    887   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
    888     GNUNET_assert (0);
    889   }
    890 
    891   for (unsigned int i = 0;
    892        i < uc->parse_request.inventory.items_len;
    893        i++)
    894   {
    895     struct InventoryTemplateItemContext *item =
    896       &uc->parse_request.inventory.items[i];
    897     enum GNUNET_DB_QueryStatus qs;
    898 
    899     qs = TALER_MERCHANTDB_get_product (TMH_db,
    900                                        mi->settings.id,
    901                                        item->product_id,
    902                                        &item->pd,
    903                                        &item->num_categories,
    904                                        &item->categories);
    905     switch (qs)
    906     {
    907     case GNUNET_DB_STATUS_HARD_ERROR:
    908       GNUNET_break (0);
    909       use_reply_with_error (uc,
    910                             MHD_HTTP_INTERNAL_SERVER_ERROR,
    911                             TALER_EC_GENERIC_DB_FETCH_FAILED,
    912                             "get_product");
    913       return;
    914     case GNUNET_DB_STATUS_SOFT_ERROR:
    915       GNUNET_break (0);
    916       use_reply_with_error (uc,
    917                             MHD_HTTP_INTERNAL_SERVER_ERROR,
    918                             TALER_EC_GENERIC_DB_FETCH_FAILED,
    919                             "get_product");
    920       return;
    921     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    922       use_reply_with_error (uc,
    923                             MHD_HTTP_NOT_FOUND,
    924                             TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN,
    925                             item->product_id);
    926       return;
    927     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    928       break;
    929     }
    930   }
    931   uc->phase++;
    932 }
    933 
    934 
    935 /* *************** Helpers for USE_PHASE_VERIFY ***************** */
    936 
    937 /**
    938  * Check that @a amount is within the limits (if any) the template
    939  * imposes on amounts the client had an influence on.  Amounts the
    940  * merchant hard-coded in the template are not subject to these limits.
    941  * Replies with an error if the check fails.
    942  *
    943  * @param[in,out] uc use context
    944  * @param amount amount to check, excluding any tip
    945  * @param detail hint to return to the client on failure
    946  * @return #GNUNET_OK if @a amount is acceptable
    947  */
    948 static enum GNUNET_GenericReturnValue
    949 check_amount_limits (struct UseContext *uc,
    950                      const struct TALER_Amount *amount,
    951                      const char *detail)
    952 {
    953   const struct TALER_MERCHANT_TemplateContract *tc = &uc->template_contract;
    954   const struct TALER_Amount *limit;
    955   char *msg;
    956 
    957   if (tc->no_min_amount &&
    958       tc->no_max_amount)
    959     return GNUNET_OK;
    960   /* Parsing the template guarantees both limits to be in the same
    961      currency, so it suffices to check @a amount against either one.
    962      We must check against a limit (and not merely against the
    963      currency of the template) as it is the limits that @a amount is
    964      compared to below, and comparing amounts of different currencies
    965      fails an assertion. */
    966   limit = tc->no_min_amount
    967           ? &tc->max_amount
    968           : &tc->min_amount;
    969   if (GNUNET_YES !=
    970       TALER_amount_cmp_currency (amount,
    971                                  limit))
    972   {
    973     GNUNET_break_op (0);
    974     use_reply_with_error (uc,
    975                           MHD_HTTP_CONFLICT,
    976                           TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
    977                           limit->currency);
    978     return GNUNET_SYSERR;
    979   }
    980   if ( (! tc->no_min_amount) &&
    981        (0 > TALER_amount_cmp (amount,
    982                               &tc->min_amount)) )
    983   {
    984     GNUNET_break_op (0);
    985     GNUNET_asprintf (&msg,
    986                      "%s is below the min_amount of %s",
    987                      detail,
    988                      TALER_amount2s (&tc->min_amount));
    989   }
    990   else if ( (! tc->no_max_amount) &&
    991             (0 < TALER_amount_cmp (amount,
    992                                    &tc->max_amount)) )
    993   {
    994     GNUNET_break_op (0);
    995     GNUNET_asprintf (&msg,
    996                      "%s is above the max_amount of %s",
    997                      detail,
    998                      TALER_amount2s (&tc->max_amount));
    999   }
   1000   else
   1001   {
   1002     return GNUNET_OK;
   1003   }
   1004   use_reply_with_error (
   1005     uc,
   1006     MHD_HTTP_CONFLICT,
   1007     TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
   1008     msg);
   1009   GNUNET_free (msg);
   1010   return GNUNET_SYSERR;
   1011 }
   1012 
   1013 
   1014 /**
   1015  * Check if the given product ID appears in the array of allowed_products.
   1016  *
   1017  * @param allowed_products JSON array of product IDs allowed by the template, may be NULL
   1018  * @param product_id product ID to check
   1019  * @return true if the product ID is in the list
   1020  */
   1021 static bool
   1022 product_id_allowed (const json_t *allowed_products,
   1023                     const char *product_id)
   1024 {
   1025   const json_t *entry;
   1026   size_t idx;
   1027 
   1028   if (NULL == allowed_products)
   1029     return false;
   1030   json_array_foreach ((json_t *) allowed_products, idx, entry)
   1031   {
   1032     if (! json_is_string (entry))
   1033     {
   1034       GNUNET_break (0);
   1035       continue;
   1036     }
   1037     if (0 == strcmp (json_string_value (entry),
   1038                      product_id))
   1039       return true;
   1040   }
   1041   return false;
   1042 }
   1043 
   1044 
   1045 /**
   1046  * Check if any product category is in the selected_categories list.
   1047  *
   1048  * @param allowed_categories JSON array of categories allowed by the template, may be NULL
   1049  * @param num_categories length of @a categories
   1050  * @param categories list of categories of the selected product
   1051  * @return true if any category of the product is in the list of allowed categories matches
   1052  */
   1053 static bool
   1054 category_allowed (const json_t *allowed_categories,
   1055                   size_t num_categories,
   1056                   const uint64_t categories[num_categories])
   1057 {
   1058   const json_t *entry;
   1059   size_t idx;
   1060 
   1061   if (NULL == allowed_categories)
   1062     return false;
   1063   json_array_foreach ((json_t *) allowed_categories,
   1064                       idx,
   1065                       entry)
   1066   {
   1067     uint64_t selected_id;
   1068 
   1069     if (! json_is_integer (entry))
   1070     {
   1071       GNUNET_break (0);
   1072       continue;
   1073     }
   1074     if (0 > json_integer_value (entry))
   1075     {
   1076       GNUNET_break (0);
   1077       continue;
   1078     }
   1079     selected_id = (uint64_t) json_integer_value (entry);
   1080     for (size_t i = 0; i < num_categories; i++)
   1081     {
   1082       if (categories[i] == selected_id)
   1083         return true;
   1084     }
   1085   }
   1086   return false;
   1087 }
   1088 
   1089 
   1090 /**
   1091  * Verify request data for inventory templates.
   1092  * Checks that the selected products are allowed
   1093  * for this template.
   1094  *
   1095  * @param[in,out] uc use context
   1096  * @return #GNUNET_OK on success
   1097  */
   1098 static enum GNUNET_GenericReturnValue
   1099 verify_using_templates_inventory (struct UseContext *uc)
   1100 {
   1101   if (uc->template_contract.details.inventory.choose_one &&
   1102       (1 != uc->parse_request.inventory.items_len))
   1103   {
   1104     GNUNET_break_op (0);
   1105     use_reply_with_error (uc,
   1106                           MHD_HTTP_CONFLICT,
   1107                           TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1108                           "inventory_selection");
   1109     return GNUNET_SYSERR;
   1110   }
   1111   if (uc->template_contract.details.inventory.selected_all)
   1112     return GNUNET_OK;
   1113   for (unsigned int i = 0;
   1114        i < uc->parse_request.inventory.items_len;
   1115        i++)
   1116   {
   1117     struct InventoryTemplateItemContext *item =
   1118       &uc->parse_request.inventory.items[i];
   1119     const char *eparam = NULL;
   1120 
   1121     if (GNUNET_OK !=
   1122         TALER_MERCHANT_vk_process_quantity_inputs (
   1123           TALER_MERCHANT_VK_QUANTITY,
   1124           item->pd.allow_fractional_quantity,
   1125           true,
   1126           0,
   1127           false,
   1128           item->unit_quantity,
   1129           &item->quantity_value,
   1130           &item->quantity_frac,
   1131           &eparam))
   1132     {
   1133       GNUNET_break_op (0);
   1134       use_reply_with_error (uc,
   1135                             MHD_HTTP_BAD_REQUEST,
   1136                             TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1137                             eparam);
   1138       return GNUNET_SYSERR;
   1139     }
   1140 
   1141     if (0 == item->pd.price_array_length)
   1142     {
   1143       GNUNET_break (0);
   1144       use_reply_with_error (uc,
   1145                             MHD_HTTP_INTERNAL_SERVER_ERROR,
   1146                             TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
   1147                             "price_array");
   1148       return GNUNET_SYSERR;
   1149     }
   1150 
   1151     /* The line-total computation multiplies the unit price by the
   1152        integer quantity using TALER_amount_multiply(), whose factor
   1153        argument is only a uint32_t.  A quantity that does not fit into
   1154        32 bits would be silently truncated by the (uint32_t) cast in
   1155        compute_line_total() (e.g. quantity == 2^32 truncates to 0),
   1156        producing a wrong -- and attacker-controllable, much too low --
   1157        price.  Reject such quantities rather than truncate them. */
   1158     if (item->quantity_value > UINT32_MAX)
   1159     {
   1160       GNUNET_break_op (0);
   1161       use_reply_with_error (uc,
   1162                             MHD_HTTP_BAD_REQUEST,
   1163                             TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1164                             "unit_quantity");
   1165       return GNUNET_SYSERR;
   1166     }
   1167   }
   1168 
   1169   for (unsigned int i = 0;
   1170        i < uc->parse_request.inventory.items_len;
   1171        i++)
   1172   {
   1173     struct InventoryTemplateItemContext *item =
   1174       &uc->parse_request.inventory.items[i];
   1175 
   1176     if (product_id_allowed (uc->template_contract.details.inventory.
   1177                             selected_products,
   1178                             item->product_id))
   1179       continue;
   1180     if (category_allowed (
   1181           uc->template_contract.details.inventory.selected_categories,
   1182           item->num_categories,
   1183           item->categories))
   1184       continue;
   1185     GNUNET_break_op (0);
   1186     use_reply_with_error (
   1187       uc,
   1188       MHD_HTTP_CONFLICT,
   1189       TALER_EC_MERCHANT_POST_USING_TEMPLATES_WRONG_PRODUCT,
   1190       item->product_id);
   1191     return GNUNET_SYSERR;
   1192   }
   1193   return GNUNET_OK;
   1194 }
   1195 
   1196 
   1197 /**
   1198  * Verify request data for fixed-order templates.
   1199  * As here we cannot compute the total amount, either
   1200  * the template or the client request must provide it.
   1201  *
   1202  * @param[in,out] uc use context
   1203  * @return #GNUNET_OK on success
   1204  */
   1205 static enum GNUNET_GenericReturnValue
   1206 verify_using_templates_fixed (
   1207   struct UseContext *uc)
   1208 {
   1209   if ( (! uc->parse_request.no_amount) &&
   1210        (! uc->template_contract.no_amount) )
   1211   {
   1212     GNUNET_break_op (0);
   1213     use_reply_with_error (uc,
   1214                           MHD_HTTP_CONFLICT,
   1215                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
   1216                           NULL);
   1217     return GNUNET_SYSERR;
   1218   }
   1219   if (uc->parse_request.no_amount &&
   1220       uc->template_contract.no_amount)
   1221   {
   1222     GNUNET_break_op (0);
   1223     use_reply_with_error (uc,
   1224                           MHD_HTTP_CONFLICT,
   1225                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_AMOUNT,
   1226                           NULL);
   1227     return GNUNET_SYSERR;
   1228   }
   1229   return GNUNET_OK;
   1230 }
   1231 
   1232 
   1233 /**
   1234  * Turn a plain @e amount given by the client into an entry in the
   1235  * @e choice_amounts array.  This shorthand is what the generic
   1236  * template flow (``editable_defaults`` and ``taler://pay-template``
   1237  * URIs) can express, and thus only works if the template has exactly
   1238  * one choice with an editable amount.
   1239  *
   1240  * @param[in,out] uc use context
   1241  * @return #GNUNET_OK on success
   1242  */
   1243 static enum GNUNET_GenericReturnValue
   1244 resolve_paivana_amount_shorthand (
   1245   struct UseContext *uc)
   1246 {
   1247   const struct TALER_MERCHANT_TemplateContractPaivana *tcp
   1248     = &uc->template_contract.details.paivana;
   1249   struct PaivanaChoiceAmount ca = {
   1250     .amount = uc->parse_request.amount
   1251   };
   1252   unsigned int editable = 0;
   1253 
   1254   for (unsigned int i = 0; i < tcp->choices_len; i++)
   1255   {
   1256     if (! tcp->choices[i].editable_amount)
   1257       continue;
   1258     ca.choice_index = i;
   1259     editable++;
   1260   }
   1261   if (0 == editable)
   1262   {
   1263     GNUNET_break_op (0);
   1264     use_reply_with_error (
   1265       uc,
   1266       MHD_HTTP_CONFLICT,
   1267       TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
   1268       "template does not allow the amount to be edited");
   1269     return GNUNET_SYSERR;
   1270   }
   1271   if (1 != editable)
   1272   {
   1273     GNUNET_break_op (0);
   1274     use_reply_with_error (
   1275       uc,
   1276       MHD_HTTP_CONFLICT,
   1277       TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
   1278       "template has more than one editable choice, use choice_amounts");
   1279     return GNUNET_SYSERR;
   1280   }
   1281   GNUNET_array_append (uc->parse_request.paivana.choice_amounts,
   1282                        uc->parse_request.paivana.choice_amounts_len,
   1283                        ca);
   1284   return GNUNET_OK;
   1285 }
   1286 
   1287 
   1288 /**
   1289  * Verify request data for paivana templates.
   1290  *
   1291  * @param[in,out] uc use context
   1292  * @return #GNUNET_OK on success
   1293  */
   1294 static enum GNUNET_GenericReturnValue
   1295 verify_using_templates_paivana (
   1296   struct UseContext *uc)
   1297 {
   1298   const struct TALER_MERCHANT_TemplateContractPaivana *tcp
   1299     = &uc->template_contract.details.paivana;
   1300 
   1301   if ( (! uc->parse_request.no_amount) &&
   1302        (GNUNET_OK !=
   1303         resolve_paivana_amount_shorthand (uc)) )
   1304     return GNUNET_SYSERR;
   1305   for (unsigned int i = 0;
   1306        i < uc->parse_request.paivana.choice_amounts_len;
   1307        i++)
   1308   {
   1309     const struct PaivanaChoiceAmount *ca
   1310       = &uc->parse_request.paivana.choice_amounts[i];
   1311     const struct TALER_MERCHANT_OrderChoice *choice;
   1312 
   1313     if (ca->choice_index >= tcp->choices_len)
   1314     {
   1315       GNUNET_break_op (0);
   1316       use_reply_with_error (uc,
   1317                             MHD_HTTP_BAD_REQUEST,
   1318                             TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1319                             "choice_amounts::choice_index out of range");
   1320       return GNUNET_SYSERR;
   1321     }
   1322     choice = &tcp->choices[ca->choice_index];
   1323     if (! choice->editable_amount)
   1324     {
   1325       GNUNET_break_op (0);
   1326       use_reply_with_error (
   1327         uc,
   1328         MHD_HTTP_CONFLICT,
   1329         TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
   1330         "selected choice has a fixed amount");
   1331       return GNUNET_SYSERR;
   1332     }
   1333     /* The currency is baked into the choice (say via 'max_fee'),
   1334        so the client may only change the value, not the currency. */
   1335     if (GNUNET_YES !=
   1336         TALER_amount_cmp_currency (&ca->amount,
   1337                                    &choice->amount))
   1338     {
   1339       GNUNET_break_op (0);
   1340       use_reply_with_error (uc,
   1341                             MHD_HTTP_CONFLICT,
   1342                             TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1343                             choice->amount.currency);
   1344       return GNUNET_SYSERR;
   1345     }
   1346     if (GNUNET_OK !=
   1347         check_amount_limits (uc,
   1348                              &ca->amount,
   1349                              "amount of the selected choice"))
   1350       return GNUNET_SYSERR;
   1351   }
   1352   if (NULL != uc->template_contract.details.paivana.website_regex)
   1353   {
   1354     regex_t ex;
   1355     bool allowed = false;
   1356 
   1357     if (0 != regcomp (&ex,
   1358                       uc->template_contract.details.paivana.website_regex,
   1359                       REG_NOSUB | REG_EXTENDED))
   1360     {
   1361       GNUNET_break_op (0);
   1362       return GNUNET_SYSERR;
   1363     }
   1364     if (0 ==
   1365         regexec (&ex,
   1366                  uc->parse_request.paivana.website,
   1367                  0, NULL,
   1368                  0))
   1369     {
   1370       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1371                   "Website `%s' allowed by template\n",
   1372                   uc->parse_request.paivana.website);
   1373       allowed = true;
   1374     }
   1375     regfree (&ex);
   1376     if (! allowed)
   1377     {
   1378       GNUNET_break_op (0);
   1379       use_reply_with_error (uc,
   1380                             MHD_HTTP_BAD_REQUEST,
   1381                             TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1382                             "website_regex");
   1383       return GNUNET_SYSERR;
   1384     }
   1385   }
   1386   return GNUNET_OK;
   1387 }
   1388 
   1389 
   1390 /**
   1391  * Verify that the client request is structurally acceptable for the specified
   1392  * template.  Does NOT check the total amount being reasonable.
   1393  *
   1394  * @param[in,out] uc use context
   1395  */
   1396 static void
   1397 handle_phase_verify (
   1398   struct UseContext *uc)
   1399 {
   1400   enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
   1401 
   1402   if ( (NULL != uc->parse_request.summary) &&
   1403        (NULL != uc->template_contract.summary) )
   1404   {
   1405     GNUNET_break_op (0);
   1406     use_reply_with_error (uc,
   1407                           MHD_HTTP_CONFLICT,
   1408                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_SUMMARY_CONFLICT_TEMPLATES_CONTRACT_SUBJECT,
   1409                           NULL);
   1410     return;
   1411   }
   1412   if ( (NULL == uc->parse_request.summary) &&
   1413        (NULL == uc->template_contract.summary) )
   1414   {
   1415     GNUNET_break_op (0);
   1416     use_reply_with_error (uc,
   1417                           MHD_HTTP_CONFLICT,
   1418                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_SUMMARY,
   1419                           NULL);
   1420     return;
   1421   }
   1422   if ( (! uc->parse_request.no_amount) &&
   1423        (NULL != uc->template_contract.currency) &&
   1424        (0 != strcasecmp (uc->template_contract.currency,
   1425                          uc->parse_request.amount.currency)) )
   1426   {
   1427     GNUNET_break_op (0);
   1428     use_reply_with_error (uc,
   1429                           MHD_HTTP_CONFLICT,
   1430                           TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1431                           uc->template_contract.currency);
   1432     return;
   1433   }
   1434   switch (uc->template_type)
   1435   {
   1436   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
   1437     res = verify_using_templates_fixed (uc);
   1438     break;
   1439   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
   1440     res = verify_using_templates_paivana (uc);
   1441     break;
   1442   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
   1443     res = verify_using_templates_inventory (uc);
   1444     break;
   1445   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
   1446     GNUNET_assert (0);
   1447   }
   1448   if (GNUNET_OK == res)
   1449     uc->phase++;
   1450 }
   1451 
   1452 
   1453 /* ***************** USE_PHASE_COMPUTE_PRICE **************** */
   1454 
   1455 
   1456 /**
   1457  * Find the amount the client picked for the choice at @a choice_index
   1458  * of a paivana template.
   1459  *
   1460  * @param uc use context
   1461  * @param choice_index index into the choices of the template contract
   1462  * @return NULL if the client did not pick an amount for this choice,
   1463  *         in which case the amount from the template applies
   1464  */
   1465 static const struct TALER_Amount *
   1466 find_paivana_choice_amount (const struct UseContext *uc,
   1467                             unsigned int choice_index)
   1468 {
   1469   for (unsigned int i = 0;
   1470        i < uc->parse_request.paivana.choice_amounts_len;
   1471        i++)
   1472   {
   1473     const struct PaivanaChoiceAmount *ca
   1474       = &uc->parse_request.paivana.choice_amounts[i];
   1475 
   1476     if (choice_index == ca->choice_index)
   1477       return &ca->amount;
   1478   }
   1479   return NULL;
   1480 }
   1481 
   1482 
   1483 /**
   1484  * Compute the line total for a product based on quantity.
   1485  *
   1486  * @param unit_price price per unit
   1487  * @param quantity integer quantity
   1488  * @param quantity_frac fractional quantity (0..TALER_MERCHANT_UNIT_FRAC_BASE-1)
   1489  * @param[out] line_total resulting line total
   1490  * @return #GNUNET_OK on success
   1491  */
   1492 static enum GNUNET_GenericReturnValue
   1493 compute_line_total (const struct TALER_Amount *unit_price,
   1494                     uint64_t quantity,
   1495                     uint32_t quantity_frac,
   1496                     struct TALER_Amount *line_total)
   1497 {
   1498   struct TALER_Amount tmp;
   1499 
   1500   GNUNET_assert (GNUNET_OK ==
   1501                  TALER_amount_set_zero (unit_price->currency,
   1502                                         line_total));
   1503   if ( (0 != quantity) &&
   1504        (0 >
   1505         TALER_amount_multiply (line_total,
   1506                                unit_price,
   1507                                (uint32_t) quantity)) )
   1508   {
   1509     GNUNET_break (0);
   1510     return GNUNET_SYSERR;
   1511   }
   1512   if (0 == quantity_frac)
   1513     return GNUNET_OK;
   1514   if (0 >
   1515       TALER_amount_multiply (&tmp,
   1516                              unit_price,
   1517                              quantity_frac))
   1518   {
   1519     GNUNET_break (0);
   1520     return GNUNET_SYSERR;
   1521   }
   1522   TALER_amount_divide (&tmp,
   1523                        &tmp,
   1524                        TALER_MERCHANT_UNIT_FRAC_BASE);
   1525   if (0 >
   1526       TALER_amount_add (line_total,
   1527                         line_total,
   1528                         &tmp))
   1529   {
   1530     GNUNET_break (0);
   1531     return GNUNET_SYSERR;
   1532   }
   1533   return GNUNET_OK;
   1534 }
   1535 
   1536 
   1537 /**
   1538  * Find the price of the given @a item in the specified
   1539  * @a currency.
   1540  *
   1541  * @param currency currency to search price in
   1542  * @param item item to check prices of
   1543  * @return NULL if a suitable price was not found
   1544  */
   1545 static const struct TALER_Amount *
   1546 find_item_price_in_currency (
   1547   const char *currency,
   1548   const struct InventoryTemplateItemContext *item)
   1549 {
   1550   for (size_t j = 0; j < item->pd.price_array_length; j++)
   1551   {
   1552     if (0 == strcasecmp (item->pd.price_array[j].currency,
   1553                          currency))
   1554       return &item->pd.price_array[j];
   1555   }
   1556   return NULL;
   1557 }
   1558 
   1559 
   1560 /**
   1561  * Compute totals for all currencies shared across selected products.
   1562  *
   1563  * @param[in,out] uc use context
   1564  * @return #GNUNET_OK on success (including no price due to no items)
   1565  *         #GNUNET_NO if we could not find a price in any accepted currency
   1566  *                    for all selected products
   1567  *         #GNUNET_SYSERR on arithmetic issues (internal error)
   1568  */
   1569 static enum GNUNET_GenericReturnValue
   1570 compute_totals_per_currency (struct UseContext *uc)
   1571 {
   1572   const struct InventoryTemplateItemContext *items
   1573     = uc->parse_request.inventory.items;
   1574   unsigned int items_len = uc->parse_request.inventory.items_len;
   1575 
   1576   if (0 == items_len)
   1577     return GNUNET_NO;
   1578   for (size_t i = 0; i < items[0].pd.price_array_length; i++)
   1579   {
   1580     const struct TALER_Amount *price
   1581       = &items[0].pd.price_array[i];
   1582     struct TALER_Amount zero;
   1583 
   1584     if (! TMH_test_exchange_configured_for_currency (price->currency))
   1585       continue;
   1586     GNUNET_assert (GNUNET_OK ==
   1587                    TALER_amount_set_zero (price->currency,
   1588                                           &zero));
   1589     GNUNET_array_append (uc->compute_price.totals,
   1590                          uc->compute_price.totals_len,
   1591                          zero);
   1592   }
   1593   if (0 == uc->compute_price.totals_len)
   1594   {
   1595     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1596                 "No currency supported by our configuration in which we have prices for first selected product!\n");
   1597     return GNUNET_NO;
   1598   }
   1599   /* Loop through items, ensure each currency exists and sum totals. */
   1600   for (unsigned int i = 0; i < items_len; i++)
   1601   {
   1602     const struct InventoryTemplateItemContext *item = &items[i];
   1603     unsigned int c = 0;
   1604 
   1605     while (c < uc->compute_price.totals_len)
   1606     {
   1607       struct TALER_Amount *total = &uc->compute_price.totals[c];
   1608       const struct TALER_Amount *unit_price;
   1609       struct TALER_Amount line_total;
   1610 
   1611       unit_price = find_item_price_in_currency (total->currency,
   1612                                                 item);
   1613       if (NULL == unit_price)
   1614       {
   1615         /* Drop the currency: we have no price in one of
   1616            the selected products */
   1617         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1618                     "Product `%s' has no price in %s: dropping currency\n",
   1619                     item->product_id,
   1620                     total->currency);
   1621         *total = uc->compute_price.totals[--uc->compute_price.totals_len];
   1622         continue;
   1623       }
   1624       if (GNUNET_OK !=
   1625           compute_line_total (unit_price,
   1626                               item->quantity_value,
   1627                               item->quantity_frac,
   1628                               &line_total))
   1629       {
   1630         GNUNET_break (0);
   1631         return GNUNET_SYSERR;
   1632       }
   1633       if (0 >
   1634           TALER_amount_add (total,
   1635                             total,
   1636                             &line_total))
   1637       {
   1638         GNUNET_break (0);
   1639         return GNUNET_SYSERR;
   1640       }
   1641       c++;
   1642     }
   1643   }
   1644   if (0 == uc->compute_price.totals_len)
   1645   {
   1646     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1647                 "No currency available in which we have prices for all selected products!\n");
   1648     GNUNET_free (uc->compute_price.totals);
   1649   }
   1650   return (0 == uc->compute_price.totals_len)
   1651     ? GNUNET_NO
   1652     : GNUNET_OK;
   1653 }
   1654 
   1655 
   1656 /**
   1657  * Compute total for only the given @a currency.
   1658  *
   1659  * @param items_len length of @a items
   1660  * @param items inventory items
   1661  * @param currency currency to total
   1662  * @param[out] total computed total
   1663  * @return #GNUNET_OK on success
   1664  *         #GNUNET_NO if we could not find a price in any accepted currency
   1665  *                    for all selected products
   1666  *         #GNUNET_SYSERR on arithmetic issues (internal error)
   1667  */
   1668 static enum GNUNET_GenericReturnValue
   1669 compute_inventory_total (unsigned int items_len,
   1670                          const struct InventoryTemplateItemContext *items,
   1671                          const char *currency,
   1672                          struct TALER_Amount *total)
   1673 {
   1674   GNUNET_assert (NULL != currency);
   1675   GNUNET_assert (GNUNET_OK ==
   1676                  TALER_amount_set_zero (currency,
   1677                                         total));
   1678   for (unsigned int i = 0; i < items_len; i++)
   1679   {
   1680     const struct InventoryTemplateItemContext *item = &items[i];
   1681     const struct TALER_Amount *unit_price;
   1682     struct TALER_Amount line_total;
   1683 
   1684     unit_price = find_item_price_in_currency (currency,
   1685                                               item);
   1686     if (NULL == unit_price)
   1687     {
   1688       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1689                   "compute_inventory_total: no price in %s for product `%s'\n",
   1690                   currency,
   1691                   item->product_id);
   1692       return GNUNET_NO;
   1693     }
   1694     if (GNUNET_OK !=
   1695         compute_line_total (unit_price,
   1696                             item->quantity_value,
   1697                             item->quantity_frac,
   1698                             &line_total))
   1699     {
   1700       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1701                   "compute_inventory_total: line total failed for %s in %s\n",
   1702                   item->product_id,
   1703                   currency);
   1704       return GNUNET_SYSERR;
   1705     }
   1706     if (0 >
   1707         TALER_amount_add (total,
   1708                           total,
   1709                           &line_total))
   1710     {
   1711       GNUNET_break (0);
   1712       return GNUNET_SYSERR;
   1713     }
   1714   }
   1715   return GNUNET_OK;
   1716 }
   1717 
   1718 
   1719 /**
   1720  * Compute total price.
   1721  *
   1722  * @param[in,out] uc use context
   1723  */
   1724 static void
   1725 handle_phase_compute_price (struct UseContext *uc)
   1726 {
   1727   const char *primary_currency;
   1728   enum GNUNET_GenericReturnValue ret;
   1729 
   1730   switch (uc->template_type)
   1731   {
   1732   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
   1733     uc->compute_price.totals
   1734       = GNUNET_new (struct TALER_Amount);
   1735     uc->compute_price.totals_len
   1736       = 1;
   1737     if (uc->parse_request.no_amount)
   1738     {
   1739       GNUNET_assert (! uc->template_contract.no_amount);
   1740       *uc->compute_price.totals
   1741         = uc->template_contract.amount;
   1742     }
   1743     else
   1744     {
   1745       GNUNET_assert (uc->template_contract.no_amount);
   1746       *uc->compute_price.totals
   1747         = uc->parse_request.amount;
   1748       if (! uc->parse_request.no_tip)
   1749       {
   1750         /* Per the API specification, the client's 'amount' is "the
   1751            amount to be paid, including tip", while the total we compute
   1752            in this phase excludes the tip: #handle_phase_check_tip()
   1753            adds it back on top.  Without removing it here, the total
   1754            would end up being 'amount' + 'tip' and could thus never
   1755            match the 'amount' that #handle_phase_check_total() compares
   1756            it against. */
   1757         if (GNUNET_YES !=
   1758             TALER_amount_cmp_currency (&uc->parse_request.tip,
   1759                                        uc->compute_price.totals))
   1760         {
   1761           GNUNET_break_op (0);
   1762           use_reply_with_error (uc,
   1763                                 MHD_HTTP_CONFLICT,
   1764                                 TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1765                                 uc->parse_request.tip.currency);
   1766           return;
   1767         }
   1768         if (0 >
   1769             TALER_amount_subtract (uc->compute_price.totals,
   1770                                    uc->compute_price.totals,
   1771                                    &uc->parse_request.tip))
   1772         {
   1773           GNUNET_break_op (0);
   1774           use_reply_with_error (
   1775             uc,
   1776             MHD_HTTP_CONFLICT,
   1777             TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
   1778             "tip exceeds amount");
   1779           return;
   1780         }
   1781       }
   1782       /* Only an amount the client chose is subject to the limits. */
   1783       if (GNUNET_OK !=
   1784           check_amount_limits (uc,
   1785                                uc->compute_price.totals,
   1786                                "amount"))
   1787         return;
   1788     }
   1789     uc->phase++;
   1790     return;
   1791   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
   1792     /* handled below */
   1793     break;
   1794   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
   1795     {
   1796       const struct TALER_MERCHANT_TemplateContractPaivana *tcp
   1797         = &uc->template_contract.details.paivana;
   1798       json_t *choices;
   1799 
   1800       choices = json_array ();
   1801       GNUNET_assert (NULL != choices);
   1802       for (size_t i = 0; i < tcp->choices_len; i++)
   1803       {
   1804         /* Make deep copy, we're going to MODIFY it! */
   1805         struct TALER_MERCHANT_OrderChoice choice
   1806           = tcp->choices[i];
   1807         const struct TALER_Amount *ca;
   1808 
   1809         ca = find_paivana_choice_amount (uc,
   1810                                          i);
   1811         if (NULL != ca)
   1812           choice.amount = *ca;
   1813         choice.no_tip = uc->parse_request.no_tip;
   1814         if (! uc->parse_request.no_tip)
   1815         {
   1816           if (GNUNET_YES !=
   1817               TALER_amount_cmp_currency (&choice.amount,
   1818                                          &uc->parse_request.tip))
   1819             continue; /* tip does not match choice currency */
   1820           choice.tip = uc->parse_request.tip;
   1821           if (0 >
   1822               TALER_amount_add (&choice.amount,
   1823                                 &choice.amount,
   1824                                 &uc->parse_request.tip))
   1825           {
   1826             GNUNET_break (0);
   1827             use_reply_with_error (uc,
   1828                                   MHD_HTTP_INTERNAL_SERVER_ERROR,
   1829                                   TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
   1830                                   "tip");
   1831             return;
   1832           }
   1833         }
   1834         GNUNET_assert (0 ==
   1835                        json_array_append_new (
   1836                          choices,
   1837                          TALER_MERCHANT_json_from_order_choice (&choice)));
   1838       }
   1839       if (0 == json_array_size (choices))
   1840       {
   1841         GNUNET_break_op (0);
   1842         json_decref (choices);
   1843         use_reply_with_error (uc,
   1844                               MHD_HTTP_CONFLICT,
   1845                               TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_CURRENCY,
   1846                               "tip");
   1847         return;
   1848       }
   1849       uc->compute_price.choices = choices;
   1850     }
   1851     /* Note: we already did the tip and pricing
   1852        fully here, so we skip these phases. */
   1853     uc->phase = USE_PHASE_CREATE_ORDER;
   1854     return;
   1855   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
   1856     GNUNET_assert (0);
   1857   }
   1858   primary_currency = uc->template_contract.currency;
   1859   if (! uc->parse_request.no_tip)
   1860     primary_currency = uc->parse_request.tip.currency;
   1861   /* An 'amount' given by the client takes precedence over the currency
   1862      of the 'tip': the total we compute here is later compared against
   1863      that 'amount' in #handle_phase_check_total(), which requires both
   1864      to be in the same currency.  If the tip then uses a different
   1865      currency, #handle_phase_check_tip() rejects the request with a
   1866      proper error instead. */
   1867   if (! uc->parse_request.no_amount)
   1868     primary_currency = uc->parse_request.amount.currency;
   1869   if (NULL == primary_currency)
   1870   {
   1871     ret = compute_totals_per_currency (uc);
   1872   }
   1873   else
   1874   {
   1875     uc->compute_price.totals
   1876       = GNUNET_new (struct TALER_Amount);
   1877     uc->compute_price.totals_len
   1878       = 1;
   1879     ret = compute_inventory_total (
   1880       uc->parse_request.inventory.items_len,
   1881       uc->parse_request.inventory.items,
   1882       primary_currency,
   1883       uc->compute_price.totals);
   1884   }
   1885   if (GNUNET_SYSERR == ret)
   1886   {
   1887     use_reply_with_error (
   1888       uc,
   1889       MHD_HTTP_INTERNAL_SERVER_ERROR,
   1890       TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
   1891       "calculation of currency totals failed");
   1892     return;
   1893   }
   1894   if (GNUNET_NO == ret)
   1895   {
   1896     use_reply_with_error (uc,
   1897                           MHD_HTTP_CONFLICT,
   1898                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_CURRENCY,
   1899                           NULL);
   1900     return;
   1901   }
   1902   /* The client picked the products and quantities, so the
   1903      resulting total is subject to the limits. */
   1904   for (unsigned int i = 0;
   1905        i < uc->compute_price.totals_len;
   1906        i++)
   1907   {
   1908     if (GNUNET_OK !=
   1909         check_amount_limits (uc,
   1910                              &uc->compute_price.totals[i],
   1911                              "total of the selected products"))
   1912       return;
   1913   }
   1914 
   1915   uc->phase++;
   1916 }
   1917 
   1918 
   1919 /* ***************** USE_PHASE_CHECK_TIP **************** */
   1920 
   1921 
   1922 /**
   1923  * Check that tip specified is reasonable and add to total.
   1924  *
   1925  * @param[in,out] uc use context
   1926  */
   1927 static void
   1928 handle_phase_check_tip (struct UseContext *uc)
   1929 {
   1930   struct TALER_Amount *total_amount;
   1931 
   1932   if (uc->parse_request.no_tip)
   1933   {
   1934     uc->phase++;
   1935     return;
   1936   }
   1937   if (0 == uc->compute_price.totals_len)
   1938   {
   1939     if (! TMH_test_exchange_configured_for_currency (
   1940           uc->parse_request.tip.currency))
   1941     {
   1942       GNUNET_break_op (0);
   1943       use_reply_with_error (uc,
   1944                             MHD_HTTP_CONFLICT,
   1945                             TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1946                             "Tip currency is not supported by backend");
   1947       return;
   1948     }
   1949     uc->compute_price.totals
   1950       = GNUNET_new (struct TALER_Amount);
   1951     uc->compute_price.totals_len
   1952       = 1;
   1953     *uc->compute_price.totals
   1954       = uc->parse_request.tip;
   1955     uc->phase++;
   1956     return;
   1957   }
   1958   GNUNET_assert (1 == uc->compute_price.totals_len);
   1959   total_amount = &uc->compute_price.totals[0];
   1960   if (GNUNET_YES !=
   1961       TALER_amount_cmp_currency (&uc->parse_request.tip,
   1962                                  total_amount))
   1963   {
   1964     GNUNET_break_op (0);
   1965     use_reply_with_error (uc,
   1966                           MHD_HTTP_CONFLICT,
   1967                           TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1968                           uc->parse_request.tip.currency);
   1969     return;
   1970   }
   1971   if (0 >
   1972       TALER_amount_add (total_amount,
   1973                         total_amount,
   1974                         &uc->parse_request.tip))
   1975   {
   1976     GNUNET_break (0);
   1977     use_reply_with_error (uc,
   1978                           MHD_HTTP_INTERNAL_SERVER_ERROR,
   1979                           TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
   1980                           "tip");
   1981     return;
   1982   }
   1983   uc->phase++;
   1984 }
   1985 
   1986 
   1987 /* ***************** USE_PHASE_CHECK_TOTAL **************** */
   1988 
   1989 /**
   1990  * Check that if the client specified a total,
   1991  * it matches our own calculation.
   1992  *
   1993  * @param[in,out] uc use context
   1994  */
   1995 static void
   1996 handle_phase_check_total (struct UseContext *uc)
   1997 {
   1998   GNUNET_assert (1 <= uc->compute_price.totals_len);
   1999   if (! uc->parse_request.no_amount)
   2000   {
   2001     GNUNET_assert (1 == uc->compute_price.totals_len);
   2002     if (GNUNET_YES !=
   2003         TALER_amount_cmp_currency (&uc->parse_request.amount,
   2004                                    &uc->compute_price.totals[0]))
   2005     {
   2006       /* Must not be an assertion: the currency of the total we computed
   2007          is influenced by the client (via 'tip' and the selected
   2008          products), so a mismatch here is remotely triggerable. */
   2009       GNUNET_break_op (0);
   2010       use_reply_with_error (uc,
   2011                             MHD_HTTP_CONFLICT,
   2012                             TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   2013                             uc->compute_price.totals[0].currency);
   2014       return;
   2015     }
   2016     if (0 !=
   2017         TALER_amount_cmp (&uc->parse_request.amount,
   2018                           &uc->compute_price.totals[0]))
   2019     {
   2020       GNUNET_break_op (0);
   2021       use_reply_with_error (uc,
   2022                             MHD_HTTP_CONFLICT,
   2023                             TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
   2024                             TALER_amount2s (&uc->compute_price.totals[0]));
   2025       return;
   2026     }
   2027   }
   2028   uc->phase++;
   2029 }
   2030 
   2031 
   2032 /* ***************** USE_PHASE_CREATE_ORDER **************** */
   2033 
   2034 
   2035 /**
   2036  * Create order request for inventory templates.
   2037  *
   2038  * @param[in,out] uc use context
   2039  */
   2040 static void
   2041 create_using_templates_inventory (struct UseContext *uc)
   2042 {
   2043   json_t *inventory_products;
   2044   json_t *choices;
   2045 
   2046   inventory_products = json_array ();
   2047   GNUNET_assert (NULL != inventory_products);
   2048   for (unsigned int i = 0;
   2049        i < uc->parse_request.inventory.items_len;
   2050        i++)
   2051   {
   2052     const struct InventoryTemplateItemContext *item =
   2053       &uc->parse_request.inventory.items[i];
   2054 
   2055     GNUNET_assert (0 ==
   2056                    json_array_append_new (
   2057                      inventory_products,
   2058                      GNUNET_JSON_PACK (
   2059                        GNUNET_JSON_pack_string ("product_id",
   2060                                                 item->product_id),
   2061                        GNUNET_JSON_pack_string ("unit_quantity",
   2062                                                 item->unit_quantity))));
   2063   }
   2064   choices = json_array ();
   2065   GNUNET_assert (NULL != choices);
   2066   for (unsigned int i = 0;
   2067        i < uc->compute_price.totals_len;
   2068        i++)
   2069   {
   2070     GNUNET_assert (0 ==
   2071                    json_array_append_new (
   2072                      choices,
   2073                      GNUNET_JSON_PACK (
   2074                        TALER_JSON_pack_amount ("amount",
   2075                                                &uc->compute_price.totals[i]),
   2076                        GNUNET_JSON_pack_allow_null (
   2077                          TALER_JSON_pack_amount ("tip",
   2078                                                  uc->parse_request.no_tip
   2079                                                 ? NULL
   2080                                                 : &uc->parse_request.tip))
   2081                        )));
   2082   }
   2083 
   2084   uc->ihc.request_body
   2085     = GNUNET_JSON_PACK (
   2086         GNUNET_JSON_pack_allow_null (
   2087           GNUNET_JSON_pack_string ("otp_id",
   2088                                    uc->lookup_template.etp.otp_id)),
   2089         GNUNET_JSON_pack_array_steal ("inventory_products",
   2090                                       inventory_products),
   2091         GNUNET_JSON_pack_object_steal (
   2092           "order",
   2093           GNUNET_JSON_PACK (
   2094             GNUNET_JSON_pack_uint64 ("version",
   2095                                      1),
   2096             GNUNET_JSON_pack_array_steal ("choices",
   2097                                           choices),
   2098             GNUNET_JSON_pack_string ("summary",
   2099                                      NULL == uc->parse_request.summary
   2100                                    ? uc->template_contract.summary
   2101                                    : uc->parse_request.summary))));
   2102 }
   2103 
   2104 
   2105 /**
   2106  * Create order request for fixed-order templates.
   2107  *
   2108  * @param[in,out] uc use context
   2109  */
   2110 static void
   2111 create_using_templates_fixed (struct UseContext *uc)
   2112 {
   2113   uc->ihc.request_body
   2114     = GNUNET_JSON_PACK (
   2115         GNUNET_JSON_pack_allow_null (
   2116           GNUNET_JSON_pack_string ("otp_id",
   2117                                    uc->lookup_template.etp.otp_id)),
   2118         GNUNET_JSON_pack_object_steal (
   2119           "order",
   2120           GNUNET_JSON_PACK (
   2121             TALER_JSON_pack_amount (
   2122               "amount",
   2123               &uc->compute_price.totals[0]),
   2124             GNUNET_JSON_pack_allow_null (
   2125               TALER_JSON_pack_amount ("tip",
   2126                                       uc->parse_request.no_tip
   2127                                       ? NULL
   2128                                       : &uc->parse_request.tip)),
   2129             GNUNET_JSON_pack_string (
   2130               "summary",
   2131               NULL == uc->parse_request.summary
   2132             ? uc->template_contract.summary
   2133             : uc->parse_request.summary))));
   2134 }
   2135 
   2136 
   2137 /**
   2138  * Create order request for paivana templates.
   2139  *
   2140  * @param[in,out] uc use context
   2141  */
   2142 static void
   2143 create_using_templates_paivana (struct UseContext *uc)
   2144 {
   2145   uc->ihc.request_body
   2146     = GNUNET_JSON_PACK (
   2147         GNUNET_JSON_pack_string (
   2148           "session_id",
   2149           uc->parse_request.paivana.paivana_id),
   2150         GNUNET_JSON_pack_object_steal (
   2151           "order",
   2152           GNUNET_JSON_PACK (
   2153             GNUNET_JSON_pack_uint64 ("version",
   2154                                      1),
   2155             GNUNET_JSON_pack_array_incref ("choices",
   2156                                            uc->compute_price.choices),
   2157             GNUNET_JSON_pack_string (
   2158               "summary",
   2159               NULL == uc->parse_request.summary
   2160               ? uc->template_contract.summary
   2161               : uc->parse_request.summary),
   2162             GNUNET_JSON_pack_string ("fulfillment_url",
   2163                                      uc->parse_request.paivana.website))));
   2164 }
   2165 
   2166 
   2167 static void
   2168 handle_phase_create_order (struct UseContext *uc)
   2169 {
   2170   json_t *order;
   2171 
   2172   GNUNET_assert (NULL == uc->ihc.request_body);
   2173   switch (uc->template_type)
   2174   {
   2175   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
   2176     create_using_templates_fixed (uc);
   2177     break;
   2178   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
   2179     create_using_templates_paivana (uc);
   2180     break;
   2181   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
   2182     create_using_templates_inventory (uc);
   2183     break;
   2184   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
   2185     GNUNET_assert (0);
   2186   }
   2187   order = json_object_get (uc->ihc.request_body,
   2188                            "order");
   2189   GNUNET_assert (json_is_object (order));
   2190   if (! GNUNET_TIME_relative_is_forever (
   2191         uc->template_contract.max_pickup_duration))
   2192   {
   2193     GNUNET_assert (
   2194       0 ==
   2195       json_object_set_new (
   2196         order,
   2197         "max_pickup_time",
   2198         GNUNET_JSON_from_timestamp (
   2199           GNUNET_TIME_absolute_to_timestamp (
   2200             GNUNET_TIME_relative_to_absolute (
   2201               uc->template_contract.max_pickup_duration)))));
   2202   }
   2203   /* A zero duration means that the template did not specify one.  Leave the
   2204      deadline absent in that case so the regular order handler applies the
   2205      instance default.  Older backends accepted FOREVER; also use the instance
   2206      default for such legacy templates instead of generating a forbidden
   2207      "never" deadline. */
   2208   if (GNUNET_TIME_relative_is_forever (
   2209         uc->template_contract.pay_duration))
   2210   {
   2211     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2212                 "Template uses invalid infinite pay duration; "
   2213                 "using instance default\n");
   2214   }
   2215   else if (! GNUNET_TIME_relative_is_zero (
   2216              uc->template_contract.pay_duration))
   2217   {
   2218     GNUNET_assert (
   2219       0 ==
   2220       json_object_set_new (
   2221         order,
   2222         "pay_deadline",
   2223         GNUNET_JSON_from_timestamp (
   2224           GNUNET_TIME_relative_to_timestamp (
   2225             uc->template_contract.pay_duration))));
   2226   }
   2227   if (! uc->parse_request.no_challenge)
   2228   {
   2229     /* POST /private/orders checks this against the algorithm of the
   2230        OTP device the template refers to */
   2231     GNUNET_assert (0 ==
   2232                    json_object_set_new (
   2233                      uc->ihc.request_body,
   2234                       "challenge",
   2235                       GNUNET_JSON_from_data_auto (
   2236                         &uc->parse_request.challenge)));
   2237   }
   2238   uc->phase++;
   2239 }
   2240 
   2241 
   2242 /* ***************** Main handler **************** */
   2243 
   2244 enum MHD_Result
   2245 TMH_post_using_templates_ID (
   2246   const struct TMH_RequestHandler *rh,
   2247   struct MHD_Connection *connection,
   2248   struct TMH_HandlerContext *hc)
   2249 {
   2250   struct UseContext *uc = hc->ctx;
   2251 
   2252   (void) rh;
   2253   if (NULL == uc)
   2254   {
   2255     uc = GNUNET_new (struct UseContext);
   2256     uc->hc = hc;
   2257     hc->ctx = uc;
   2258     hc->cc = &cleanup_use_context;
   2259     uc->ihc.instance = hc->instance;
   2260     uc->phase = USE_PHASE_PARSE_REQUEST;
   2261     uc->template_type = TALER_MERCHANT_TEMPLATE_TYPE_INVALID;
   2262   }
   2263 
   2264   while (1)
   2265   {
   2266     switch (uc->phase)
   2267     {
   2268     case USE_PHASE_PARSE_REQUEST:
   2269       handle_phase_parse_request (uc);
   2270       break;
   2271     case USE_PHASE_LOOKUP_TEMPLATE:
   2272       handle_phase_lookup_template (uc);
   2273       break;
   2274     case USE_PHASE_PARSE_TEMPLATE:
   2275       handle_phase_template_contract (uc);
   2276       break;
   2277     case USE_PHASE_DB_FETCH:
   2278       handle_phase_db_fetch (uc);
   2279       break;
   2280     case USE_PHASE_VERIFY:
   2281       handle_phase_verify (uc);
   2282       break;
   2283     case USE_PHASE_COMPUTE_PRICE:
   2284       handle_phase_compute_price (uc);
   2285       break;
   2286     case USE_PHASE_CHECK_TIP:
   2287       handle_phase_check_tip (uc);
   2288       break;
   2289     case USE_PHASE_CHECK_TOTAL:
   2290       handle_phase_check_total (uc);
   2291       break;
   2292     case USE_PHASE_CREATE_ORDER:
   2293       handle_phase_create_order (uc);
   2294       break;
   2295     case USE_PHASE_SUBMIT_ORDER:
   2296       return TMH_private_post_orders (
   2297         NULL,    /* not even used */
   2298         connection,
   2299         &uc->ihc);
   2300     case USE_PHASE_FINISHED_MHD_YES:
   2301       return MHD_YES;
   2302     case USE_PHASE_FINISHED_MHD_NO:
   2303       return MHD_NO;
   2304     }
   2305   }
   2306 }