merchant

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

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


      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   GNUNET_assert (NULL == uc->ihc.request_body);
   2169   switch (uc->template_type)
   2170   {
   2171   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
   2172     create_using_templates_fixed (uc);
   2173     break;
   2174   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
   2175     create_using_templates_paivana (uc);
   2176     break;
   2177   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
   2178     create_using_templates_inventory (uc);
   2179     break;
   2180   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
   2181     GNUNET_assert (0);
   2182   }
   2183   uc->phase++;
   2184 }
   2185 
   2186 
   2187 /* ***************** Main handler **************** */
   2188 
   2189 enum MHD_Result
   2190 TMH_post_using_templates_ID (
   2191   const struct TMH_RequestHandler *rh,
   2192   struct MHD_Connection *connection,
   2193   struct TMH_HandlerContext *hc)
   2194 {
   2195   struct UseContext *uc = hc->ctx;
   2196 
   2197   (void) rh;
   2198   if (NULL == uc)
   2199   {
   2200     uc = GNUNET_new (struct UseContext);
   2201     uc->hc = hc;
   2202     hc->ctx = uc;
   2203     hc->cc = &cleanup_use_context;
   2204     uc->ihc.instance = hc->instance;
   2205     uc->phase = USE_PHASE_PARSE_REQUEST;
   2206     uc->template_type = TALER_MERCHANT_TEMPLATE_TYPE_INVALID;
   2207   }
   2208 
   2209   while (1)
   2210   {
   2211     switch (uc->phase)
   2212     {
   2213     case USE_PHASE_PARSE_REQUEST:
   2214       handle_phase_parse_request (uc);
   2215       break;
   2216     case USE_PHASE_LOOKUP_TEMPLATE:
   2217       handle_phase_lookup_template (uc);
   2218       break;
   2219     case USE_PHASE_PARSE_TEMPLATE:
   2220       handle_phase_template_contract (uc);
   2221       break;
   2222     case USE_PHASE_DB_FETCH:
   2223       handle_phase_db_fetch (uc);
   2224       break;
   2225     case USE_PHASE_VERIFY:
   2226       handle_phase_verify (uc);
   2227       break;
   2228     case USE_PHASE_COMPUTE_PRICE:
   2229       handle_phase_compute_price (uc);
   2230       break;
   2231     case USE_PHASE_CHECK_TIP:
   2232       handle_phase_check_tip (uc);
   2233       break;
   2234     case USE_PHASE_CHECK_TOTAL:
   2235       handle_phase_check_total (uc);
   2236       break;
   2237     case USE_PHASE_CREATE_ORDER:
   2238       handle_phase_create_order (uc);
   2239       break;
   2240     case USE_PHASE_SUBMIT_ORDER:
   2241       return TMH_private_post_orders (
   2242         NULL,    /* not even used */
   2243         connection,
   2244         &uc->ihc);
   2245     case USE_PHASE_FINISHED_MHD_YES:
   2246       return MHD_YES;
   2247     case USE_PHASE_FINISHED_MHD_NO:
   2248       return MHD_NO;
   2249     }
   2250   }
   2251 }