merchant

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

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


      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/lookup_product.h"
     36 #include "merchant-database/lookup_template.h"
     37 
     38 
     39 /**
     40  * Item selected from inventory_selection.
     41  */
     42 struct InventoryTemplateItemContext
     43 {
     44   /**
     45    * Product ID as referenced in inventory.
     46    */
     47   const char *product_id;
     48 
     49   /**
     50    * Unit quantity string as provided by the client.
     51    */
     52   const char *unit_quantity;
     53 
     54   /**
     55    * Parsed integer quantity.
     56    */
     57   uint64_t quantity_value;
     58 
     59   /**
     60    * Parsed fractional quantity.
     61    */
     62   uint32_t quantity_frac;
     63 
     64   /**
     65    * Product details from the DB (includes price array).
     66    */
     67   struct TALER_MERCHANTDB_ProductDetails pd;
     68 
     69   /**
     70    * Categories referenced by the product.
     71    */
     72   uint64_t *categories;
     73 
     74   /**
     75    * Length of @e categories.
     76    */
     77   size_t num_categories;
     78 };
     79 
     80 
     81 /**
     82  * Our context.
     83  */
     84 enum UsePhase
     85 {
     86   /**
     87    * Parse request payload into context fields.
     88    */
     89   USE_PHASE_PARSE_REQUEST,
     90 
     91   /**
     92    * Fetch template details from the database.
     93    */
     94   USE_PHASE_LOOKUP_TEMPLATE,
     95 
     96   /**
     97    * Parse template.
     98    */
     99   USE_PHASE_PARSE_TEMPLATE,
    100 
    101   /**
    102    * Load additional details (like products and
    103    * categories) needed for verification and
    104    * price computation.
    105    */
    106   USE_PHASE_DB_FETCH,
    107 
    108   /**
    109    * Validate request and template compatibility.
    110    */
    111   USE_PHASE_VERIFY,
    112 
    113   /**
    114    * Compute price of the order.
    115    */
    116   USE_PHASE_COMPUTE_PRICE,
    117 
    118   /**
    119    * Handle tip.
    120    */
    121   USE_PHASE_CHECK_TIP,
    122 
    123   /**
    124    * Check if client-supplied total amount matches
    125    * our calculation (if we did any).
    126    */
    127   USE_PHASE_CHECK_TOTAL,
    128 
    129   /**
    130    * Construct the internal order request body.
    131    */
    132   USE_PHASE_CREATE_ORDER,
    133 
    134   /**
    135    * Submit the order to the shared order handler.
    136    */
    137   USE_PHASE_SUBMIT_ORDER,
    138 
    139   /**
    140    * Finished successfully with MHD_YES.
    141    */
    142   USE_PHASE_FINISHED_MHD_YES,
    143 
    144   /**
    145    * Finished with MHD_NO.
    146    */
    147   USE_PHASE_FINISHED_MHD_NO
    148 };
    149 
    150 struct UseContext
    151 {
    152   /**
    153    * Context for our handler.
    154    */
    155   struct TMH_HandlerContext *hc;
    156 
    157   /**
    158    * Internal handler context we are passing into the
    159    * POST /private/orders handler.
    160    */
    161   struct TMH_HandlerContext ihc;
    162 
    163   /**
    164    * Phase we are currently in.
    165    */
    166   enum UsePhase phase;
    167 
    168   /**
    169    * Template type from the contract.
    170    */
    171   enum TALER_MERCHANT_TemplateType template_type;
    172 
    173   /**
    174    * Information set in the #USE_PHASE_PARSE_REQUEST phase.
    175    */
    176   struct
    177   {
    178     /**
    179      * Summary override from request, if any.
    180      */
    181     const char *summary;
    182 
    183     /**
    184      * Amount provided by the client.
    185      */
    186     struct TALER_Amount amount;
    187 
    188     /**
    189      * Tip provided by the client.
    190      */
    191     struct TALER_Amount tip;
    192 
    193     /**
    194      * True if @e amount was not provided.
    195      */
    196     bool no_amount;
    197 
    198     /**
    199      * True if @e tip was not provided.
    200      */
    201     bool no_tip;
    202 
    203     /**
    204      * Parsed fields for inventory templates.
    205      */
    206     struct
    207     {
    208       /**
    209        * Selected products from inventory_selection.
    210        */
    211       struct InventoryTemplateItemContext *items;
    212 
    213       /**
    214        * Length of @e items.
    215        */
    216       unsigned int items_len;
    217 
    218     } inventory;
    219 
    220     /**
    221      * Request details if this is a paivana instantiation.
    222      */
    223     struct
    224     {
    225 
    226       /**
    227        * Target website for the request.
    228        */
    229       const char *website;
    230 
    231       /**
    232        * Unique client identifier, consisting of
    233        * current time, "-", and the hash of a nonce,
    234        * the website and the current time.
    235        */
    236       const char *paivana_id;
    237 
    238     } paivana;
    239 
    240   } parse_request;
    241 
    242   /**
    243    * Information set in the #USE_PHASE_LOOKUP_TEMPLATE phase.
    244    */
    245   struct
    246   {
    247 
    248     /**
    249      * Our template details from the DB.
    250      */
    251     struct TALER_MERCHANTDB_TemplateDetails etp;
    252 
    253   } lookup_template;
    254 
    255   /**
    256    * Information set in the #USE_PHASE_PARSE_TEMPLATE phase.
    257    */
    258   struct TALER_MERCHANT_TemplateContract template_contract;
    259 
    260   /**
    261    * Information set in the #USE_PHASE_COMPUTE_PRICE phase.
    262    */
    263   struct
    264   {
    265 
    266     /**
    267      * Per-currency totals across selected products (without tips).
    268      */
    269     struct TALER_Amount *totals;
    270 
    271     /**
    272      * Length of @e totals.
    273      */
    274     unsigned int totals_len;
    275 
    276     /**
    277      * Array of payment choices, used with Paviana.
    278      */
    279     json_t *choices;
    280 
    281   } compute_price;
    282 
    283 };
    284 
    285 
    286 /**
    287  * Clean up inventory items.
    288  *
    289  * @param items_len length of @a items
    290  * @param[in] items item array to free
    291  */
    292 static void
    293 cleanup_inventory_items (
    294   unsigned int items_len,
    295   struct InventoryTemplateItemContext items[static items_len])
    296 {
    297   for (unsigned int i = 0; i < items_len; i++)
    298   {
    299     struct InventoryTemplateItemContext *item = &items[i];
    300 
    301     TALER_MERCHANTDB_product_details_free (&item->pd);
    302     GNUNET_free (item->categories);
    303   }
    304   GNUNET_free (items);
    305 }
    306 
    307 
    308 /**
    309  * Clean up a `struct UseContext *`
    310  *
    311  * @param[in] cls a `struct UseContext *`
    312  */
    313 static void
    314 cleanup_use_context (void *cls)
    315 {
    316   struct UseContext *uc = cls;
    317 
    318   TALER_MERCHANTDB_template_details_free (&uc->lookup_template.etp);
    319   if (NULL !=
    320       uc->parse_request.inventory.items)
    321     cleanup_inventory_items (uc->parse_request.inventory.items_len,
    322                              uc->parse_request.inventory.items);
    323   TALER_MERCHANT_template_contract_free (&uc->template_contract);
    324   GNUNET_free (uc->compute_price.totals);
    325   uc->compute_price.totals_len = 0;
    326   json_decref (uc->compute_price.choices);
    327   if (NULL != uc->ihc.cc)
    328     uc->ihc.cc (uc->ihc.ctx);
    329   GNUNET_free (uc->ihc.infix);
    330   json_decref (uc->ihc.request_body);
    331   GNUNET_free (uc);
    332 }
    333 
    334 
    335 /**
    336  * Finalize a template use request.
    337  *
    338  * @param[in,out] uc use context
    339  * @param ret handler return value
    340  */
    341 static void
    342 use_finalize (struct UseContext *uc,
    343               enum MHD_Result ret)
    344 {
    345   uc->phase = (MHD_YES == ret)
    346     ? USE_PHASE_FINISHED_MHD_YES
    347     : USE_PHASE_FINISHED_MHD_NO;
    348 }
    349 
    350 
    351 /**
    352  * Finalize after JSON parsing result.
    353  *
    354  * @param[in,out] uc use context
    355  * @param res parse result
    356  */
    357 static void
    358 use_finalize_parse (struct UseContext *uc,
    359                     enum GNUNET_GenericReturnValue res)
    360 {
    361   GNUNET_assert (GNUNET_OK != res);
    362   use_finalize (uc,
    363                 (GNUNET_NO == res)
    364                 ? MHD_YES
    365                 : MHD_NO);
    366 }
    367 
    368 
    369 /**
    370  * Reply with error and finalize the request.
    371  *
    372  * @param[in,out] uc use context
    373  * @param http_status HTTP status code
    374  * @param ec error code
    375  * @param detail error detail
    376  */
    377 static void
    378 use_reply_with_error (struct UseContext *uc,
    379                       unsigned int http_status,
    380                       enum TALER_ErrorCode ec,
    381                       const char *detail)
    382 {
    383   enum MHD_Result mret;
    384 
    385   mret = TALER_MHD_reply_with_error (uc->hc->connection,
    386                                      http_status,
    387                                      ec,
    388                                      detail);
    389   use_finalize (uc,
    390                 mret);
    391 }
    392 
    393 
    394 /* ***************** USE_PHASE_PARSE_REQUEST **************** */
    395 
    396 /**
    397  * Parse request data for inventory templates.
    398  *
    399  * @param[in,out] uc use context
    400  * @return #GNUNET_OK on success
    401  */
    402 static enum GNUNET_GenericReturnValue
    403 parse_using_templates_inventory_request (
    404   struct UseContext *uc)
    405 {
    406   const json_t *inventory_selection;
    407   struct GNUNET_JSON_Specification spec[] = {
    408     GNUNET_JSON_spec_array_const ("inventory_selection",
    409                                   &inventory_selection),
    410     GNUNET_JSON_spec_end ()
    411   };
    412   enum GNUNET_GenericReturnValue res;
    413 
    414   GNUNET_assert (NULL == uc->ihc.request_body);
    415   res = TALER_MHD_parse_json_data (uc->hc->connection,
    416                                    uc->hc->request_body,
    417                                    spec);
    418   if (GNUNET_OK != res)
    419   {
    420     GNUNET_break_op (0);
    421     use_finalize_parse (uc,
    422                         res);
    423     return GNUNET_SYSERR;
    424   }
    425 
    426   if ( (! uc->parse_request.no_amount) &&
    427        (! TMH_test_exchange_configured_for_currency (
    428           uc->parse_request.amount.currency)) )
    429   {
    430     GNUNET_break_op (0);
    431     use_reply_with_error (uc,
    432                           MHD_HTTP_CONFLICT,
    433                           TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
    434                           "Currency is not supported by backend");
    435     return GNUNET_SYSERR;
    436   }
    437 
    438   for (size_t i = 0; i < json_array_size (inventory_selection); i++)
    439   {
    440     struct InventoryTemplateItemContext item = { 0 };
    441     struct GNUNET_JSON_Specification ispec[] = {
    442       GNUNET_JSON_spec_string ("product_id",
    443                                &item.product_id),
    444       GNUNET_JSON_spec_string ("quantity",
    445                                &item.unit_quantity),
    446       GNUNET_JSON_spec_end ()
    447     };
    448     const char *err_name;
    449     unsigned int err_line;
    450 
    451     res = GNUNET_JSON_parse (json_array_get (inventory_selection,
    452                                              i),
    453                              ispec,
    454                              &err_name,
    455                              &err_line);
    456     if (GNUNET_OK != res)
    457     {
    458       GNUNET_break_op (0);
    459       use_reply_with_error (uc,
    460                             MHD_HTTP_BAD_REQUEST,
    461                             TALER_EC_GENERIC_PARAMETER_MALFORMED,
    462                             "inventory_selection");
    463       return GNUNET_SYSERR;
    464     }
    465 
    466     GNUNET_array_append (uc->parse_request.inventory.items,
    467                          uc->parse_request.inventory.items_len,
    468                          item);
    469   }
    470   return GNUNET_OK;
    471 }
    472 
    473 
    474 /**
    475  * Parse request data for paivana templates.
    476  *
    477  * @param[in,out] uc use context
    478  * @return #GNUNET_OK on success
    479  */
    480 static enum GNUNET_GenericReturnValue
    481 parse_using_templates_paivana_request (
    482   struct UseContext *uc)
    483 {
    484   struct GNUNET_JSON_Specification spec[] = {
    485     GNUNET_JSON_spec_string ("website",
    486                              &uc->parse_request.paivana.website),
    487     GNUNET_JSON_spec_string ("paivana_id",
    488                              &uc->parse_request.paivana.paivana_id),
    489     GNUNET_JSON_spec_end ()
    490   };
    491   enum GNUNET_GenericReturnValue res;
    492   unsigned long long tv;
    493   const char *dash;
    494 
    495   GNUNET_assert (NULL == uc->ihc.request_body);
    496   res = TALER_MHD_parse_json_data (uc->hc->connection,
    497                                    uc->hc->request_body,
    498                                    spec);
    499   if (GNUNET_OK != res)
    500   {
    501     GNUNET_break_op (0);
    502     use_finalize_parse (uc,
    503                         res);
    504     return GNUNET_SYSERR;
    505   }
    506   if (1 !=
    507       sscanf (uc->parse_request.paivana.paivana_id,
    508               "%llu-",
    509               &tv))
    510   {
    511     GNUNET_break_op (0);
    512     use_reply_with_error (uc,
    513                           MHD_HTTP_BAD_REQUEST,
    514                           TALER_EC_GENERIC_PARAMETER_MALFORMED,
    515                           "paivana_id");
    516     return GNUNET_SYSERR;
    517   }
    518   dash = strchr (uc->parse_request.paivana.paivana_id,
    519                  '-');
    520   if (NULL == dash)
    521   {
    522     GNUNET_break_op (0);
    523     use_reply_with_error (uc,
    524                           MHD_HTTP_BAD_REQUEST,
    525                           TALER_EC_GENERIC_PARAMETER_MALFORMED,
    526                           "paivana_id");
    527     return GNUNET_SYSERR;
    528   }
    529   {
    530     size_t olen;
    531     void *out = NULL;
    532 
    533     olen = GNUNET_STRINGS_base64url_decode (dash + 1,
    534                                             strlen (dash + 1),
    535                                             &out);
    536     GNUNET_free (out);
    537     if (sizeof (struct GNUNET_ShortHashCode) != olen)
    538     {
    539       GNUNET_break_op (0);
    540       use_reply_with_error (uc,
    541                             MHD_HTTP_BAD_REQUEST,
    542                             TALER_EC_GENERIC_PARAMETER_MALFORMED,
    543                             "paivana_id");
    544       return GNUNET_SYSERR;
    545     }
    546   }
    547   return GNUNET_OK;
    548 }
    549 
    550 
    551 /**
    552  * Main function for the #USE_PHASE_PARSE_REQUEST.
    553  *
    554  * @param[in,out] uc context to update
    555  */
    556 static void
    557 handle_phase_parse_request (
    558   struct UseContext *uc)
    559 {
    560   const char *template_type = NULL;
    561   struct GNUNET_JSON_Specification spec[] = {
    562     GNUNET_JSON_spec_mark_optional (
    563       GNUNET_JSON_spec_string ("template_type",
    564                                &template_type),
    565       NULL),
    566     GNUNET_JSON_spec_mark_optional (
    567       TALER_JSON_spec_amount_any ("tip",
    568                                   &uc->parse_request.tip),
    569       &uc->parse_request.no_tip),
    570     GNUNET_JSON_spec_mark_optional (
    571       GNUNET_JSON_spec_string ("summary",
    572                                &uc->parse_request.summary),
    573       NULL),
    574     GNUNET_JSON_spec_mark_optional (
    575       TALER_JSON_spec_amount_any ("amount",
    576                                   &uc->parse_request.amount),
    577       &uc->parse_request.no_amount),
    578     GNUNET_JSON_spec_end ()
    579   };
    580   enum GNUNET_GenericReturnValue res;
    581 
    582   res = TALER_MHD_parse_json_data (uc->hc->connection,
    583                                    uc->hc->request_body,
    584                                    spec);
    585   if (GNUNET_OK != res)
    586   {
    587     GNUNET_break_op (0);
    588     use_finalize_parse (uc,
    589                         res);
    590     return;
    591   }
    592   if (NULL == template_type)
    593     template_type = "fixed-order";
    594   uc->template_type
    595     = TALER_MERCHANT_template_type_from_string (
    596         template_type);
    597   switch (uc->template_type)
    598   {
    599   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
    600     /* nothig left to do */
    601     uc->phase++;
    602     return;
    603   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
    604     res = parse_using_templates_paivana_request (uc);
    605     if (GNUNET_OK == res)
    606       uc->phase++;
    607     return;
    608   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
    609     res = parse_using_templates_inventory_request (uc);
    610     if (GNUNET_OK == res)
    611       uc->phase++;
    612     return;
    613   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
    614     break;
    615   }
    616   GNUNET_break (0);
    617   use_reply_with_error (
    618     uc,
    619     MHD_HTTP_BAD_REQUEST,
    620     TALER_EC_GENERIC_PARAMETER_MALFORMED,
    621     "template_type");
    622 }
    623 
    624 
    625 /* ***************** USE_PHASE_LOOKUP_TEMPLATE **************** */
    626 
    627 /**
    628  * Main function for the #USE_PHASE_LOOKUP_TEMPLATE.
    629  *
    630  * @param[in,out] uc context to update
    631  */
    632 static void
    633 handle_phase_lookup_template (
    634   struct UseContext *uc)
    635 {
    636   struct TMH_MerchantInstance *mi = uc->hc->instance;
    637   const char *template_id = uc->hc->infix;
    638   enum GNUNET_DB_QueryStatus qs;
    639 
    640   qs = TALER_MERCHANTDB_lookup_template (TMH_db,
    641                                          mi->settings.id,
    642                                          template_id,
    643                                          &uc->lookup_template.etp);
    644   switch (qs)
    645   {
    646   case GNUNET_DB_STATUS_HARD_ERROR:
    647     /* Clean up and fail hard */
    648     GNUNET_break (0);
    649     use_reply_with_error (uc,
    650                           MHD_HTTP_INTERNAL_SERVER_ERROR,
    651                           TALER_EC_GENERIC_DB_FETCH_FAILED,
    652                           "lookup_template");
    653     return;
    654   case GNUNET_DB_STATUS_SOFT_ERROR:
    655     /* this should be impossible (single select) */
    656     GNUNET_break (0);
    657     use_reply_with_error (uc,
    658                           MHD_HTTP_INTERNAL_SERVER_ERROR,
    659                           TALER_EC_GENERIC_DB_FETCH_FAILED,
    660                           "lookup_template");
    661     return;
    662   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    663     /* template not found! */
    664     use_reply_with_error (uc,
    665                           MHD_HTTP_NOT_FOUND,
    666                           TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN,
    667                           template_id);
    668     return;
    669   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    670     /* all good */
    671     break;
    672   }
    673   if (uc->template_type !=
    674       TALER_MERCHANT_template_type_from_contract (
    675         uc->lookup_template.etp.template_contract))
    676   {
    677     GNUNET_break_op (0);
    678     use_reply_with_error (
    679       uc,
    680       MHD_HTTP_CONFLICT,
    681       TALER_EC_MERCHANT_POST_USING_TEMPLATES_WRONG_TYPE,
    682       "template_contract has different type");
    683     return;
    684   }
    685   uc->phase++;
    686 }
    687 
    688 
    689 /* ***************** USE_PHASE_PARSE_TEMPLATE **************** */
    690 
    691 
    692 /**
    693  * Parse template.
    694  *
    695  * @param[in,out] uc use context
    696  */
    697 static void
    698 handle_phase_template_contract (struct UseContext *uc)
    699 {
    700   const char *err_name;
    701   enum GNUNET_GenericReturnValue res;
    702 
    703   res = TALER_MERCHANT_template_contract_parse (
    704     uc->lookup_template.etp.template_contract,
    705     &uc->template_contract,
    706     &err_name);
    707   if (GNUNET_OK != res)
    708   {
    709     GNUNET_break (0);
    710     use_reply_with_error (uc,
    711                           MHD_HTTP_INTERNAL_SERVER_ERROR,
    712                           TALER_EC_GENERIC_DB_FETCH_FAILED,
    713                           err_name);
    714     return;
    715   }
    716   uc->phase++;
    717 }
    718 
    719 
    720 /* ***************** USE_PHASE_DB_FETCH **************** */
    721 
    722 /**
    723  * Fetch DB data for inventory templates.
    724  *
    725  * @param[in,out] uc use context
    726  */
    727 static void
    728 handle_phase_db_fetch (struct UseContext *uc)
    729 {
    730   struct TMH_MerchantInstance *mi = uc->hc->instance;
    731 
    732   switch (uc->template_type)
    733   {
    734   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
    735     uc->phase++;
    736     return;
    737   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
    738     uc->phase++;
    739     return;
    740   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
    741     break;
    742   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
    743     GNUNET_assert (0);
    744   }
    745 
    746   for (unsigned int i = 0;
    747        i < uc->parse_request.inventory.items_len;
    748        i++)
    749   {
    750     struct InventoryTemplateItemContext *item =
    751       &uc->parse_request.inventory.items[i];
    752     enum GNUNET_DB_QueryStatus qs;
    753 
    754     qs = TALER_MERCHANTDB_lookup_product (TMH_db,
    755                                           mi->settings.id,
    756                                           item->product_id,
    757                                           &item->pd,
    758                                           &item->num_categories,
    759                                           &item->categories);
    760     switch (qs)
    761     {
    762     case GNUNET_DB_STATUS_HARD_ERROR:
    763       GNUNET_break (0);
    764       use_reply_with_error (uc,
    765                             MHD_HTTP_INTERNAL_SERVER_ERROR,
    766                             TALER_EC_GENERIC_DB_FETCH_FAILED,
    767                             "lookup_product");
    768       return;
    769     case GNUNET_DB_STATUS_SOFT_ERROR:
    770       GNUNET_break (0);
    771       use_reply_with_error (uc,
    772                             MHD_HTTP_INTERNAL_SERVER_ERROR,
    773                             TALER_EC_GENERIC_DB_FETCH_FAILED,
    774                             "lookup_product");
    775       return;
    776     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    777       use_reply_with_error (uc,
    778                             MHD_HTTP_NOT_FOUND,
    779                             TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN,
    780                             item->product_id);
    781       return;
    782     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    783       break;
    784     }
    785   }
    786   uc->phase++;
    787 }
    788 
    789 
    790 /* *************** Helpers for USE_PHASE_VERIFY ***************** */
    791 
    792 /**
    793  * Check if the given product ID appears in the array of allowed_products.
    794  *
    795  * @param allowed_products JSON array of product IDs allowed by the template, may be NULL
    796  * @param product_id product ID to check
    797  * @return true if the product ID is in the list
    798  */
    799 static bool
    800 product_id_allowed (const json_t *allowed_products,
    801                     const char *product_id)
    802 {
    803   const json_t *entry;
    804   size_t idx;
    805 
    806   if (NULL == allowed_products)
    807     return false;
    808   json_array_foreach ((json_t *) allowed_products, idx, entry)
    809   {
    810     if (! json_is_string (entry))
    811     {
    812       GNUNET_break (0);
    813       continue;
    814     }
    815     if (0 == strcmp (json_string_value (entry),
    816                      product_id))
    817       return true;
    818   }
    819   return false;
    820 }
    821 
    822 
    823 /**
    824  * Check if any product category is in the selected_categories list.
    825  *
    826  * @param allowed_categories JSON array of categories allowed by the template, may be NULL
    827  * @param num_categories length of @a categories
    828  * @param categories list of categories of the selected product
    829  * @return true if any category of the product is in the list of allowed categories matches
    830  */
    831 static bool
    832 category_allowed (const json_t *allowed_categories,
    833                   size_t num_categories,
    834                   const uint64_t categories[num_categories])
    835 {
    836   const json_t *entry;
    837   size_t idx;
    838 
    839   if (NULL == allowed_categories)
    840     return false;
    841   json_array_foreach ((json_t *) allowed_categories,
    842                       idx,
    843                       entry)
    844   {
    845     uint64_t selected_id;
    846 
    847     if (! json_is_integer (entry))
    848     {
    849       GNUNET_break (0);
    850       continue;
    851     }
    852     if (0 > json_integer_value (entry))
    853     {
    854       GNUNET_break (0);
    855       continue;
    856     }
    857     selected_id = (uint64_t) json_integer_value (entry);
    858     for (size_t i = 0; i < num_categories; i++)
    859     {
    860       if (categories[i] == selected_id)
    861         return true;
    862     }
    863   }
    864   return false;
    865 }
    866 
    867 
    868 /**
    869  * Verify request data for inventory templates.
    870  * Checks that the selected products are allowed
    871  * for this template.
    872  *
    873  * @param[in,out] uc use context
    874  * @return #GNUNET_OK on success
    875  */
    876 static enum GNUNET_GenericReturnValue
    877 verify_using_templates_inventory (struct UseContext *uc)
    878 {
    879   if (uc->template_contract.details.inventory.choose_one &&
    880       (1 != uc->parse_request.inventory.items_len))
    881   {
    882     GNUNET_break_op (0);
    883     use_reply_with_error (uc,
    884                           MHD_HTTP_CONFLICT,
    885                           TALER_EC_GENERIC_PARAMETER_MALFORMED,
    886                           "inventory_selection");
    887     return GNUNET_SYSERR;
    888   }
    889   if (uc->template_contract.details.inventory.selected_all)
    890     return GNUNET_OK;
    891   for (unsigned int i = 0;
    892        i < uc->parse_request.inventory.items_len;
    893        i++)
    894   {
    895     struct InventoryTemplateItemContext *item =
    896       &uc->parse_request.inventory.items[i];
    897     const char *eparam = NULL;
    898 
    899     if (GNUNET_OK !=
    900         TALER_MERCHANT_vk_process_quantity_inputs (
    901           TALER_MERCHANT_VK_QUANTITY,
    902           item->pd.allow_fractional_quantity,
    903           true,
    904           0,
    905           false,
    906           item->unit_quantity,
    907           &item->quantity_value,
    908           &item->quantity_frac,
    909           &eparam))
    910     {
    911       GNUNET_break_op (0);
    912       use_reply_with_error (uc,
    913                             MHD_HTTP_BAD_REQUEST,
    914                             TALER_EC_GENERIC_PARAMETER_MALFORMED,
    915                             eparam);
    916       return GNUNET_SYSERR;
    917     }
    918 
    919     if (0 == item->pd.price_array_length)
    920     {
    921       GNUNET_break (0);
    922       use_reply_with_error (uc,
    923                             MHD_HTTP_INTERNAL_SERVER_ERROR,
    924                             TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    925                             "price_array");
    926       return GNUNET_SYSERR;
    927     }
    928 
    929     /* The line-total computation multiplies the unit price by the
    930        integer quantity using TALER_amount_multiply(), whose factor
    931        argument is only a uint32_t.  A quantity that does not fit into
    932        32 bits would be silently truncated by the (uint32_t) cast in
    933        compute_line_total() (e.g. quantity == 2^32 truncates to 0),
    934        producing a wrong -- and attacker-controllable, much too low --
    935        price.  Reject such quantities rather than truncate them. */
    936     if (item->quantity_value > UINT32_MAX)
    937     {
    938       GNUNET_break_op (0);
    939       use_reply_with_error (uc,
    940                             MHD_HTTP_BAD_REQUEST,
    941                             TALER_EC_GENERIC_PARAMETER_MALFORMED,
    942                             "unit_quantity");
    943       return GNUNET_SYSERR;
    944     }
    945   }
    946 
    947   for (unsigned int i = 0;
    948        i < uc->parse_request.inventory.items_len;
    949        i++)
    950   {
    951     struct InventoryTemplateItemContext *item =
    952       &uc->parse_request.inventory.items[i];
    953 
    954     if (product_id_allowed (uc->template_contract.details.inventory.
    955                             selected_products,
    956                             item->product_id))
    957       continue;
    958     if (category_allowed (
    959           uc->template_contract.details.inventory.selected_categories,
    960           item->num_categories,
    961           item->categories))
    962       continue;
    963     GNUNET_break_op (0);
    964     use_reply_with_error (
    965       uc,
    966       MHD_HTTP_CONFLICT,
    967       TALER_EC_MERCHANT_POST_USING_TEMPLATES_WRONG_PRODUCT,
    968       item->product_id);
    969     return GNUNET_SYSERR;
    970   }
    971   return GNUNET_OK;
    972 }
    973 
    974 
    975 /**
    976  * Verify request data for fixed-order templates.
    977  * As here we cannot compute the total amount, either
    978  * the template or the client request must provide it.
    979  *
    980  * @param[in,out] uc use context
    981  * @return #GNUNET_OK on success
    982  */
    983 static enum GNUNET_GenericReturnValue
    984 verify_using_templates_fixed (
    985   struct UseContext *uc)
    986 {
    987   if ( (! uc->parse_request.no_amount) &&
    988        (! uc->template_contract.no_amount) )
    989   {
    990     GNUNET_break_op (0);
    991     use_reply_with_error (uc,
    992                           MHD_HTTP_CONFLICT,
    993                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
    994                           NULL);
    995     return GNUNET_SYSERR;
    996   }
    997   if (uc->parse_request.no_amount &&
    998       uc->template_contract.no_amount)
    999   {
   1000     GNUNET_break_op (0);
   1001     use_reply_with_error (uc,
   1002                           MHD_HTTP_CONFLICT,
   1003                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_AMOUNT,
   1004                           NULL);
   1005     return GNUNET_SYSERR;
   1006   }
   1007   return GNUNET_OK;
   1008 }
   1009 
   1010 
   1011 /**
   1012  * Verify request data for paivana templates.
   1013  *
   1014  * @param[in,out] uc use context
   1015  * @return #GNUNET_OK on success
   1016  */
   1017 static enum GNUNET_GenericReturnValue
   1018 verify_using_templates_paivana (
   1019   struct UseContext *uc)
   1020 {
   1021   if (NULL != uc->template_contract.details.paivana.website_regex)
   1022   {
   1023     regex_t ex;
   1024     bool allowed = false;
   1025 
   1026     if (0 != regcomp (&ex,
   1027                       uc->template_contract.details.paivana.website_regex,
   1028                       REG_NOSUB | REG_EXTENDED))
   1029     {
   1030       GNUNET_break_op (0);
   1031       return GNUNET_SYSERR;
   1032     }
   1033     if (0 ==
   1034         regexec (&ex,
   1035                  uc->parse_request.paivana.website,
   1036                  0, NULL,
   1037                  0))
   1038     {
   1039       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1040                   "Website `%s' allowed by template\n",
   1041                   uc->parse_request.paivana.website);
   1042       allowed = true;
   1043     }
   1044     regfree (&ex);
   1045     if (! allowed)
   1046     {
   1047       GNUNET_break_op (0);
   1048       use_reply_with_error (uc,
   1049                             MHD_HTTP_BAD_REQUEST,
   1050                             TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1051                             "website_regex");
   1052       return GNUNET_SYSERR;
   1053     }
   1054   }
   1055   return GNUNET_OK;
   1056 }
   1057 
   1058 
   1059 /**
   1060  * Verify that the client request is structurally acceptable for the specified
   1061  * template.  Does NOT check the total amount being reasonable.
   1062  *
   1063  * @param[in,out] uc use context
   1064  */
   1065 static void
   1066 handle_phase_verify (
   1067   struct UseContext *uc)
   1068 {
   1069   enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
   1070 
   1071   if ( (NULL != uc->parse_request.summary) &&
   1072        (NULL != uc->template_contract.summary) )
   1073   {
   1074     GNUNET_break_op (0);
   1075     use_reply_with_error (uc,
   1076                           MHD_HTTP_CONFLICT,
   1077                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_SUMMARY_CONFLICT_TEMPLATES_CONTRACT_SUBJECT,
   1078                           NULL);
   1079     return;
   1080   }
   1081   if ( (NULL == uc->parse_request.summary) &&
   1082        (NULL == uc->template_contract.summary) )
   1083   {
   1084     GNUNET_break_op (0);
   1085     use_reply_with_error (uc,
   1086                           MHD_HTTP_CONFLICT,
   1087                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_SUMMARY,
   1088                           NULL);
   1089     return;
   1090   }
   1091   if ( (! uc->parse_request.no_amount) &&
   1092        (NULL != uc->template_contract.currency) &&
   1093        (0 != strcasecmp (uc->template_contract.currency,
   1094                          uc->parse_request.amount.currency)) )
   1095   {
   1096     GNUNET_break_op (0);
   1097     use_reply_with_error (uc,
   1098                           MHD_HTTP_CONFLICT,
   1099                           TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1100                           uc->template_contract.currency);
   1101     return;
   1102   }
   1103   switch (uc->template_type)
   1104   {
   1105   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
   1106     res = verify_using_templates_fixed (uc);
   1107     break;
   1108   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
   1109     res = verify_using_templates_paivana (uc);
   1110     break;
   1111   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
   1112     res = verify_using_templates_inventory (uc);
   1113     break;
   1114   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
   1115     GNUNET_assert (0);
   1116   }
   1117   if (GNUNET_OK == res)
   1118     uc->phase++;
   1119 }
   1120 
   1121 
   1122 /* ***************** USE_PHASE_COMPUTE_PRICE **************** */
   1123 
   1124 
   1125 /**
   1126  * Compute the line total for a product based on quantity.
   1127  *
   1128  * @param unit_price price per unit
   1129  * @param quantity integer quantity
   1130  * @param quantity_frac fractional quantity (0..TALER_MERCHANT_UNIT_FRAC_BASE-1)
   1131  * @param[out] line_total resulting line total
   1132  * @return #GNUNET_OK on success
   1133  */
   1134 static enum GNUNET_GenericReturnValue
   1135 compute_line_total (const struct TALER_Amount *unit_price,
   1136                     uint64_t quantity,
   1137                     uint32_t quantity_frac,
   1138                     struct TALER_Amount *line_total)
   1139 {
   1140   struct TALER_Amount tmp;
   1141 
   1142   GNUNET_assert (GNUNET_OK ==
   1143                  TALER_amount_set_zero (unit_price->currency,
   1144                                         line_total));
   1145   if ( (0 != quantity) &&
   1146        (0 >
   1147         TALER_amount_multiply (line_total,
   1148                                unit_price,
   1149                                (uint32_t) quantity)) )
   1150   {
   1151     GNUNET_break (0);
   1152     return GNUNET_SYSERR;
   1153   }
   1154   if (0 == quantity_frac)
   1155     return GNUNET_OK;
   1156   if (0 >
   1157       TALER_amount_multiply (&tmp,
   1158                              unit_price,
   1159                              quantity_frac))
   1160   {
   1161     GNUNET_break (0);
   1162     return GNUNET_SYSERR;
   1163   }
   1164   TALER_amount_divide (&tmp,
   1165                        &tmp,
   1166                        TALER_MERCHANT_UNIT_FRAC_BASE);
   1167   if (0 >
   1168       TALER_amount_add (line_total,
   1169                         line_total,
   1170                         &tmp))
   1171   {
   1172     GNUNET_break (0);
   1173     return GNUNET_SYSERR;
   1174   }
   1175   return GNUNET_OK;
   1176 }
   1177 
   1178 
   1179 /**
   1180  * Find the price of the given @a item in the specified
   1181  * @a currency.
   1182  *
   1183  * @param currency currency to search price in
   1184  * @param item item to check prices of
   1185  * @return NULL if a suitable price was not found
   1186  */
   1187 static const struct TALER_Amount *
   1188 find_item_price_in_currency (
   1189   const char *currency,
   1190   const struct InventoryTemplateItemContext *item)
   1191 {
   1192   for (size_t j = 0; j < item->pd.price_array_length; j++)
   1193   {
   1194     if (0 == strcasecmp (item->pd.price_array[j].currency,
   1195                          currency))
   1196       return &item->pd.price_array[j];
   1197   }
   1198   return NULL;
   1199 }
   1200 
   1201 
   1202 /**
   1203  * Compute totals for all currencies shared across selected products.
   1204  *
   1205  * @param[in,out] uc use context
   1206  * @return #GNUNET_OK on success (including no price due to no items)
   1207  *         #GNUNET_NO if we could not find a price in any accepted currency
   1208  *                    for all selected products
   1209  *         #GNUNET_SYSERR on arithmetic issues (internal error)
   1210  */
   1211 static enum GNUNET_GenericReturnValue
   1212 compute_totals_per_currency (struct UseContext *uc)
   1213 {
   1214   const struct InventoryTemplateItemContext *items
   1215     = uc->parse_request.inventory.items;
   1216   unsigned int items_len = uc->parse_request.inventory.items_len;
   1217 
   1218   if (0 == items_len)
   1219     return GNUNET_NO;
   1220   for (size_t i = 0; i < items[0].pd.price_array_length; i++)
   1221   {
   1222     const struct TALER_Amount *price
   1223       = &items[0].pd.price_array[i];
   1224     struct TALER_Amount zero;
   1225 
   1226     if (! TMH_test_exchange_configured_for_currency (price->currency))
   1227       continue;
   1228     GNUNET_assert (GNUNET_OK ==
   1229                    TALER_amount_set_zero (price->currency,
   1230                                           &zero));
   1231     GNUNET_array_append (uc->compute_price.totals,
   1232                          uc->compute_price.totals_len,
   1233                          zero);
   1234   }
   1235   if (0 == uc->compute_price.totals_len)
   1236   {
   1237     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1238                 "No currency supported by our configuration in which we have prices for first selected product!\n");
   1239     return GNUNET_NO;
   1240   }
   1241   /* Loop through items, ensure each currency exists and sum totals. */
   1242   for (unsigned int i = 0; i < items_len; i++)
   1243   {
   1244     const struct InventoryTemplateItemContext *item = &items[i];
   1245     unsigned int c = 0;
   1246 
   1247     while (c < uc->compute_price.totals_len)
   1248     {
   1249       struct TALER_Amount *total = &uc->compute_price.totals[c];
   1250       const struct TALER_Amount *unit_price;
   1251       struct TALER_Amount line_total;
   1252 
   1253       unit_price = find_item_price_in_currency (total->currency,
   1254                                                 item);
   1255       if (NULL == unit_price)
   1256       {
   1257         /* Drop the currency: we have no price in one of
   1258            the selected products */
   1259         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1260                     "Product `%s' has no price in %s: dropping currency\n",
   1261                     item->product_id,
   1262                     total->currency);
   1263         *total = uc->compute_price.totals[--uc->compute_price.totals_len];
   1264         continue;
   1265       }
   1266       if (GNUNET_OK !=
   1267           compute_line_total (unit_price,
   1268                               item->quantity_value,
   1269                               item->quantity_frac,
   1270                               &line_total))
   1271       {
   1272         GNUNET_break (0);
   1273         return GNUNET_SYSERR;
   1274       }
   1275       if (0 >
   1276           TALER_amount_add (total,
   1277                             total,
   1278                             &line_total))
   1279       {
   1280         GNUNET_break (0);
   1281         return GNUNET_SYSERR;
   1282       }
   1283       c++;
   1284     }
   1285   }
   1286   if (0 == uc->compute_price.totals_len)
   1287   {
   1288     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1289                 "No currency available in which we have prices for all selected products!\n");
   1290     GNUNET_free (uc->compute_price.totals);
   1291   }
   1292   return (0 == uc->compute_price.totals_len)
   1293     ? GNUNET_NO
   1294     : GNUNET_OK;
   1295 }
   1296 
   1297 
   1298 /**
   1299  * Compute total for only the given @a currency.
   1300  *
   1301  * @param items_len length of @a items
   1302  * @param items inventory items
   1303  * @param currency currency to total
   1304  * @param[out] total computed total
   1305  * @return #GNUNET_OK on success
   1306  *         #GNUNET_NO if we could not find a price in any accepted currency
   1307  *                    for all selected products
   1308  *         #GNUNET_SYSERR on arithmetic issues (internal error)
   1309  */
   1310 static enum GNUNET_GenericReturnValue
   1311 compute_inventory_total (unsigned int items_len,
   1312                          const struct InventoryTemplateItemContext *items,
   1313                          const char *currency,
   1314                          struct TALER_Amount *total)
   1315 {
   1316   GNUNET_assert (NULL != currency);
   1317   GNUNET_assert (GNUNET_OK ==
   1318                  TALER_amount_set_zero (currency,
   1319                                         total));
   1320   for (unsigned int i = 0; i < items_len; i++)
   1321   {
   1322     const struct InventoryTemplateItemContext *item = &items[i];
   1323     const struct TALER_Amount *unit_price;
   1324     struct TALER_Amount line_total;
   1325 
   1326     unit_price = find_item_price_in_currency (currency,
   1327                                               item);
   1328     if (NULL == unit_price)
   1329     {
   1330       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1331                   "compute_inventory_total: no price in %s for product `%s'\n",
   1332                   currency,
   1333                   item->product_id);
   1334       return GNUNET_NO;
   1335     }
   1336     if (GNUNET_OK !=
   1337         compute_line_total (unit_price,
   1338                             item->quantity_value,
   1339                             item->quantity_frac,
   1340                             &line_total))
   1341     {
   1342       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1343                   "compute_inventory_total: line total failed for %s in %s\n",
   1344                   item->product_id,
   1345                   currency);
   1346       return GNUNET_SYSERR;
   1347     }
   1348     if (0 >
   1349         TALER_amount_add (total,
   1350                           total,
   1351                           &line_total))
   1352     {
   1353       GNUNET_break (0);
   1354       return GNUNET_SYSERR;
   1355     }
   1356   }
   1357   return GNUNET_OK;
   1358 }
   1359 
   1360 
   1361 /**
   1362  * Compute total price.
   1363  *
   1364  * @param[in,out] uc use context
   1365  */
   1366 static void
   1367 handle_phase_compute_price (struct UseContext *uc)
   1368 {
   1369   const char *primary_currency;
   1370   enum GNUNET_GenericReturnValue ret;
   1371 
   1372   switch (uc->template_type)
   1373   {
   1374   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
   1375     uc->compute_price.totals
   1376       = GNUNET_new (struct TALER_Amount);
   1377     uc->compute_price.totals_len
   1378       = 1;
   1379     if (uc->parse_request.no_amount)
   1380     {
   1381       GNUNET_assert (! uc->template_contract.no_amount);
   1382       *uc->compute_price.totals
   1383         = uc->template_contract.amount;
   1384     }
   1385     else
   1386     {
   1387       GNUNET_assert (uc->template_contract.no_amount);
   1388       *uc->compute_price.totals
   1389         = uc->parse_request.amount;
   1390     }
   1391     uc->phase++;
   1392     return;
   1393   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
   1394     /* handled below */
   1395     break;
   1396   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
   1397     {
   1398       const struct TALER_MERCHANT_TemplateContractPaivana *tcp
   1399         = &uc->template_contract.details.paivana;
   1400       json_t *choices;
   1401 
   1402       choices = json_array ();
   1403       GNUNET_assert (NULL != choices);
   1404       for (size_t i = 0; i < tcp->choices_len; i++)
   1405       {
   1406         /* Make deep copy, we're going to MODIFY it! */
   1407         struct TALER_MERCHANT_OrderChoice choice
   1408           = tcp->choices[i];
   1409 
   1410         choice.no_tip = uc->parse_request.no_tip;
   1411         if (! uc->parse_request.no_tip)
   1412         {
   1413           if (GNUNET_YES !=
   1414               TALER_amount_cmp_currency (&choice.amount,
   1415                                          &uc->parse_request.tip))
   1416             continue; /* tip does not match choice currency */
   1417           choice.tip = uc->parse_request.tip;
   1418           if (0 >
   1419               TALER_amount_add (&choice.amount,
   1420                                 &choice.amount,
   1421                                 &uc->parse_request.tip))
   1422           {
   1423             GNUNET_break (0);
   1424             use_reply_with_error (uc,
   1425                                   MHD_HTTP_INTERNAL_SERVER_ERROR,
   1426                                   TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
   1427                                   "tip");
   1428             return;
   1429           }
   1430         }
   1431         GNUNET_assert (0 ==
   1432                        json_array_append_new (
   1433                          choices,
   1434                          TALER_MERCHANT_json_from_order_choice (&choice)));
   1435       }
   1436       if (0 == json_array_size (choices))
   1437       {
   1438         GNUNET_break_op (0);
   1439         json_decref (choices);
   1440         use_reply_with_error (uc,
   1441                               MHD_HTTP_CONFLICT,
   1442                               TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_CURRENCY,
   1443                               "tip");
   1444         return;
   1445       }
   1446       uc->compute_price.choices = choices;
   1447     }
   1448     /* Note: we already did the tip and pricing
   1449        fully here, so we skip these phases. */
   1450     uc->phase = USE_PHASE_CREATE_ORDER;
   1451     return;
   1452   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
   1453     GNUNET_assert (0);
   1454   }
   1455   primary_currency = uc->template_contract.currency;
   1456   if (! uc->parse_request.no_amount)
   1457     primary_currency = uc->parse_request.amount.currency;
   1458   if (! uc->parse_request.no_tip)
   1459     primary_currency = uc->parse_request.tip.currency;
   1460   if (NULL == primary_currency)
   1461   {
   1462     ret = compute_totals_per_currency (uc);
   1463   }
   1464   else
   1465   {
   1466     uc->compute_price.totals
   1467       = GNUNET_new (struct TALER_Amount);
   1468     uc->compute_price.totals_len
   1469       = 1;
   1470     ret = compute_inventory_total (
   1471       uc->parse_request.inventory.items_len,
   1472       uc->parse_request.inventory.items,
   1473       primary_currency,
   1474       uc->compute_price.totals);
   1475   }
   1476   if (GNUNET_SYSERR == ret)
   1477   {
   1478     use_reply_with_error (
   1479       uc,
   1480       MHD_HTTP_INTERNAL_SERVER_ERROR,
   1481       TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
   1482       "calculation of currency totals failed");
   1483     return;
   1484   }
   1485   if (GNUNET_NO == ret)
   1486   {
   1487     use_reply_with_error (uc,
   1488                           MHD_HTTP_CONFLICT,
   1489                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_CURRENCY,
   1490                           NULL);
   1491     return;
   1492   }
   1493 
   1494   uc->phase++;
   1495 }
   1496 
   1497 
   1498 /* ***************** USE_PHASE_CHECK_TIP **************** */
   1499 
   1500 
   1501 /**
   1502  * Check that tip specified is reasonable and add to total.
   1503  *
   1504  * @param[in,out] uc use context
   1505  */
   1506 static void
   1507 handle_phase_check_tip (struct UseContext *uc)
   1508 {
   1509   struct TALER_Amount *total_amount;
   1510 
   1511   if (uc->parse_request.no_tip)
   1512   {
   1513     uc->phase++;
   1514     return;
   1515   }
   1516   if (0 == uc->compute_price.totals_len)
   1517   {
   1518     if (! TMH_test_exchange_configured_for_currency (
   1519           uc->parse_request.tip.currency))
   1520     {
   1521       GNUNET_break_op (0);
   1522       use_reply_with_error (uc,
   1523                             MHD_HTTP_CONFLICT,
   1524                             TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1525                             "Tip currency is not supported by backend");
   1526       return;
   1527     }
   1528     uc->compute_price.totals
   1529       = GNUNET_new (struct TALER_Amount);
   1530     uc->compute_price.totals_len
   1531       = 1;
   1532     *uc->compute_price.totals
   1533       = uc->parse_request.tip;
   1534     uc->phase++;
   1535     return;
   1536   }
   1537   GNUNET_assert (1 == uc->compute_price.totals_len);
   1538   total_amount = &uc->compute_price.totals[0];
   1539   if (GNUNET_YES !=
   1540       TALER_amount_cmp_currency (&uc->parse_request.tip,
   1541                                  total_amount))
   1542   {
   1543     GNUNET_break_op (0);
   1544     use_reply_with_error (uc,
   1545                           MHD_HTTP_CONFLICT,
   1546                           TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1547                           uc->parse_request.tip.currency);
   1548     return;
   1549   }
   1550   if (0 >
   1551       TALER_amount_add (total_amount,
   1552                         total_amount,
   1553                         &uc->parse_request.tip))
   1554   {
   1555     GNUNET_break (0);
   1556     use_reply_with_error (uc,
   1557                           MHD_HTTP_INTERNAL_SERVER_ERROR,
   1558                           TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
   1559                           "tip");
   1560     return;
   1561   }
   1562   uc->phase++;
   1563 }
   1564 
   1565 
   1566 /* ***************** USE_PHASE_CHECK_TOTAL **************** */
   1567 
   1568 /**
   1569  * Check that if the client specified a total,
   1570  * it matches our own calculation.
   1571  *
   1572  * @param[in,out] uc use context
   1573  */
   1574 static void
   1575 handle_phase_check_total (struct UseContext *uc)
   1576 {
   1577   GNUNET_assert (1 <= uc->compute_price.totals_len);
   1578   if (! uc->parse_request.no_amount)
   1579   {
   1580     GNUNET_assert (1 == uc->compute_price.totals_len);
   1581     GNUNET_assert (GNUNET_YES ==
   1582                    TALER_amount_cmp_currency (&uc->parse_request.amount,
   1583                                               &uc->compute_price.totals[0]));
   1584     if (0 !=
   1585         TALER_amount_cmp (&uc->parse_request.amount,
   1586                           &uc->compute_price.totals[0]))
   1587     {
   1588       GNUNET_break_op (0);
   1589       use_reply_with_error (uc,
   1590                             MHD_HTTP_CONFLICT,
   1591                             TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
   1592                             TALER_amount2s (&uc->compute_price.totals[0]));
   1593       return;
   1594     }
   1595   }
   1596   uc->phase++;
   1597 }
   1598 
   1599 
   1600 /* ***************** USE_PHASE_CREATE_ORDER **************** */
   1601 
   1602 
   1603 /**
   1604  * Create order request for inventory templates.
   1605  *
   1606  * @param[in,out] uc use context
   1607  */
   1608 static void
   1609 create_using_templates_inventory (struct UseContext *uc)
   1610 {
   1611   json_t *inventory_products;
   1612   json_t *choices;
   1613 
   1614   inventory_products = json_array ();
   1615   GNUNET_assert (NULL != inventory_products);
   1616   for (unsigned int i = 0;
   1617        i < uc->parse_request.inventory.items_len;
   1618        i++)
   1619   {
   1620     const struct InventoryTemplateItemContext *item =
   1621       &uc->parse_request.inventory.items[i];
   1622 
   1623     GNUNET_assert (0 ==
   1624                    json_array_append_new (
   1625                      inventory_products,
   1626                      GNUNET_JSON_PACK (
   1627                        GNUNET_JSON_pack_string ("product_id",
   1628                                                 item->product_id),
   1629                        GNUNET_JSON_pack_string ("unit_quantity",
   1630                                                 item->unit_quantity))));
   1631   }
   1632   choices = json_array ();
   1633   GNUNET_assert (NULL != choices);
   1634   for (unsigned int i = 0;
   1635        i < uc->compute_price.totals_len;
   1636        i++)
   1637   {
   1638     GNUNET_assert (0 ==
   1639                    json_array_append_new (
   1640                      choices,
   1641                      GNUNET_JSON_PACK (
   1642                        TALER_JSON_pack_amount ("amount",
   1643                                                &uc->compute_price.totals[i]),
   1644                        GNUNET_JSON_pack_allow_null (
   1645                          TALER_JSON_pack_amount ("tip",
   1646                                                  uc->parse_request.no_tip
   1647                                                 ? NULL
   1648                                                 : &uc->parse_request.tip))
   1649                        )));
   1650   }
   1651 
   1652   uc->ihc.request_body
   1653     = GNUNET_JSON_PACK (
   1654         GNUNET_JSON_pack_allow_null (
   1655           GNUNET_JSON_pack_string ("otp_id",
   1656                                    uc->lookup_template.etp.otp_id)),
   1657         GNUNET_JSON_pack_array_steal ("inventory_products",
   1658                                       inventory_products),
   1659         GNUNET_JSON_pack_object_steal (
   1660           "order",
   1661           GNUNET_JSON_PACK (
   1662             GNUNET_JSON_pack_uint64 ("version",
   1663                                      1),
   1664             GNUNET_JSON_pack_array_steal ("choices",
   1665                                           choices),
   1666             GNUNET_JSON_pack_string ("summary",
   1667                                      NULL == uc->parse_request.summary
   1668                                    ? uc->template_contract.summary
   1669                                    : uc->parse_request.summary))));
   1670   if (! GNUNET_TIME_relative_is_forever (
   1671         uc->template_contract.max_pickup_duration))
   1672   {
   1673     GNUNET_assert (
   1674       0 ==
   1675       json_object_set_new (
   1676         uc->ihc.request_body,
   1677         "max_pickup_time",
   1678         GNUNET_JSON_from_timestamp (
   1679           GNUNET_TIME_absolute_to_timestamp (
   1680             GNUNET_TIME_relative_to_absolute (
   1681               uc->template_contract.max_pickup_duration)))));
   1682   }
   1683 }
   1684 
   1685 
   1686 /**
   1687  * Create order request for fixed-order templates.
   1688  *
   1689  * @param[in,out] uc use context
   1690  */
   1691 static void
   1692 create_using_templates_fixed (struct UseContext *uc)
   1693 {
   1694   uc->ihc.request_body
   1695     = GNUNET_JSON_PACK (
   1696         GNUNET_JSON_pack_allow_null (
   1697           GNUNET_JSON_pack_string ("otp_id",
   1698                                    uc->lookup_template.etp.otp_id)),
   1699         GNUNET_JSON_pack_object_steal (
   1700           "order",
   1701           GNUNET_JSON_PACK (
   1702             TALER_JSON_pack_amount (
   1703               "amount",
   1704               &uc->compute_price.totals[0]),
   1705             GNUNET_JSON_pack_allow_null (
   1706               TALER_JSON_pack_amount ("tip",
   1707                                       uc->parse_request.no_tip
   1708                                       ? NULL
   1709                                       : &uc->parse_request.tip)),
   1710             GNUNET_JSON_pack_string (
   1711               "summary",
   1712               NULL == uc->parse_request.summary
   1713             ? uc->template_contract.summary
   1714             : uc->parse_request.summary))));
   1715 }
   1716 
   1717 
   1718 /**
   1719  * Create order request for paivana templates.
   1720  *
   1721  * @param[in,out] uc use context
   1722  */
   1723 static void
   1724 create_using_templates_paivana (struct UseContext *uc)
   1725 {
   1726   uc->ihc.request_body
   1727     = GNUNET_JSON_PACK (
   1728         GNUNET_JSON_pack_string (
   1729           "session_id",
   1730           uc->parse_request.paivana.paivana_id),
   1731         GNUNET_JSON_pack_object_steal (
   1732           "order",
   1733           GNUNET_JSON_PACK (
   1734             GNUNET_JSON_pack_uint64 ("version",
   1735                                      1),
   1736             GNUNET_JSON_pack_array_incref ("choices",
   1737                                            uc->compute_price.choices),
   1738             GNUNET_JSON_pack_string (
   1739               "summary",
   1740               NULL == uc->parse_request.summary
   1741               ? uc->template_contract.summary
   1742               : uc->parse_request.summary),
   1743             GNUNET_JSON_pack_string ("fulfillment_url",
   1744                                      uc->parse_request.paivana.website))));
   1745 }
   1746 
   1747 
   1748 static void
   1749 handle_phase_create_order (struct UseContext *uc)
   1750 {
   1751   GNUNET_assert (NULL == uc->ihc.request_body);
   1752   switch (uc->template_type)
   1753   {
   1754   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
   1755     create_using_templates_fixed (uc);
   1756     break;
   1757   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
   1758     create_using_templates_paivana (uc);
   1759     break;
   1760   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
   1761     create_using_templates_inventory (uc);
   1762     break;
   1763   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
   1764     GNUNET_assert (0);
   1765   }
   1766   uc->phase++;
   1767 }
   1768 
   1769 
   1770 /* ***************** Main handler **************** */
   1771 
   1772 enum MHD_Result
   1773 TMH_post_using_templates_ID (
   1774   const struct TMH_RequestHandler *rh,
   1775   struct MHD_Connection *connection,
   1776   struct TMH_HandlerContext *hc)
   1777 {
   1778   struct UseContext *uc = hc->ctx;
   1779 
   1780   (void) rh;
   1781   if (NULL == uc)
   1782   {
   1783     uc = GNUNET_new (struct UseContext);
   1784     uc->hc = hc;
   1785     hc->ctx = uc;
   1786     hc->cc = &cleanup_use_context;
   1787     uc->ihc.instance = hc->instance;
   1788     uc->phase = USE_PHASE_PARSE_REQUEST;
   1789     uc->template_type = TALER_MERCHANT_TEMPLATE_TYPE_INVALID;
   1790   }
   1791 
   1792   while (1)
   1793   {
   1794     switch (uc->phase)
   1795     {
   1796     case USE_PHASE_PARSE_REQUEST:
   1797       handle_phase_parse_request (uc);
   1798       break;
   1799     case USE_PHASE_LOOKUP_TEMPLATE:
   1800       handle_phase_lookup_template (uc);
   1801       break;
   1802     case USE_PHASE_PARSE_TEMPLATE:
   1803       handle_phase_template_contract (uc);
   1804       break;
   1805     case USE_PHASE_DB_FETCH:
   1806       handle_phase_db_fetch (uc);
   1807       break;
   1808     case USE_PHASE_VERIFY:
   1809       handle_phase_verify (uc);
   1810       break;
   1811     case USE_PHASE_COMPUTE_PRICE:
   1812       handle_phase_compute_price (uc);
   1813       break;
   1814     case USE_PHASE_CHECK_TIP:
   1815       handle_phase_check_tip (uc);
   1816       break;
   1817     case USE_PHASE_CHECK_TOTAL:
   1818       handle_phase_check_total (uc);
   1819       break;
   1820     case USE_PHASE_CREATE_ORDER:
   1821       handle_phase_create_order (uc);
   1822       break;
   1823     case USE_PHASE_SUBMIT_ORDER:
   1824       return TMH_private_post_orders (
   1825         NULL,    /* not even used */
   1826         connection,
   1827         &uc->ihc);
   1828     case USE_PHASE_FINISHED_MHD_YES:
   1829       return MHD_YES;
   1830     case USE_PHASE_FINISHED_MHD_NO:
   1831       return MHD_NO;
   1832     }
   1833   }
   1834 }