merchant

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

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


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