merchant

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

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


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