merchant

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

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


      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 
    930   for (unsigned int i = 0;
    931        i < uc->parse_request.inventory.items_len;
    932        i++)
    933   {
    934     struct InventoryTemplateItemContext *item =
    935       &uc->parse_request.inventory.items[i];
    936 
    937     if (product_id_allowed (uc->template_contract.details.inventory.
    938                             selected_products,
    939                             item->product_id))
    940       continue;
    941     if (category_allowed (
    942           uc->template_contract.details.inventory.selected_categories,
    943           item->num_categories,
    944           item->categories))
    945       continue;
    946     GNUNET_break_op (0);
    947     use_reply_with_error (
    948       uc,
    949       MHD_HTTP_CONFLICT,
    950       TALER_EC_MERCHANT_POST_USING_TEMPLATES_WRONG_PRODUCT,
    951       item->product_id);
    952     return GNUNET_SYSERR;
    953   }
    954   return GNUNET_OK;
    955 }
    956 
    957 
    958 /**
    959  * Verify request data for fixed-order templates.
    960  * As here we cannot compute the total amount, either
    961  * the template or the client request must provide it.
    962  *
    963  * @param[in,out] uc use context
    964  * @return #GNUNET_OK on success
    965  */
    966 static enum GNUNET_GenericReturnValue
    967 verify_using_templates_fixed (
    968   struct UseContext *uc)
    969 {
    970   if ( (! uc->parse_request.no_amount) &&
    971        (! uc->template_contract.no_amount) )
    972   {
    973     GNUNET_break_op (0);
    974     use_reply_with_error (uc,
    975                           MHD_HTTP_CONFLICT,
    976                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
    977                           NULL);
    978     return GNUNET_SYSERR;
    979   }
    980   if (uc->parse_request.no_amount &&
    981       uc->template_contract.no_amount)
    982   {
    983     GNUNET_break_op (0);
    984     use_reply_with_error (uc,
    985                           MHD_HTTP_CONFLICT,
    986                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_AMOUNT,
    987                           NULL);
    988     return GNUNET_SYSERR;
    989   }
    990   return GNUNET_OK;
    991 }
    992 
    993 
    994 /**
    995  * Verify request data for paivana templates.
    996  *
    997  * @param[in,out] uc use context
    998  * @return #GNUNET_OK on success
    999  */
   1000 static enum GNUNET_GenericReturnValue
   1001 verify_using_templates_paivana (
   1002   struct UseContext *uc)
   1003 {
   1004   if (NULL != uc->template_contract.details.paivana.website_regex)
   1005   {
   1006     regex_t ex;
   1007     bool allowed = false;
   1008 
   1009     if (0 != regcomp (&ex,
   1010                       uc->template_contract.details.paivana.website_regex,
   1011                       REG_NOSUB | REG_EXTENDED))
   1012     {
   1013       GNUNET_break_op (0);
   1014       return GNUNET_SYSERR;
   1015     }
   1016     if (0 ==
   1017         regexec (&ex,
   1018                  uc->parse_request.paivana.website,
   1019                  0, NULL,
   1020                  0))
   1021     {
   1022       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1023                   "Website `%s' allowed by template\n",
   1024                   uc->parse_request.paivana.website);
   1025       allowed = true;
   1026     }
   1027     regfree (&ex);
   1028     if (! allowed)
   1029     {
   1030       GNUNET_break_op (0);
   1031       use_reply_with_error (uc,
   1032                             MHD_HTTP_BAD_REQUEST,
   1033                             TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1034                             "website_regex");
   1035       return GNUNET_SYSERR;
   1036     }
   1037   }
   1038   return GNUNET_OK;
   1039 }
   1040 
   1041 
   1042 /**
   1043  * Verify that the client request is structurally acceptable for the specified
   1044  * template.  Does NOT check the total amount being reasonable.
   1045  *
   1046  * @param[in,out] uc use context
   1047  */
   1048 static void
   1049 handle_phase_verify (
   1050   struct UseContext *uc)
   1051 {
   1052   enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
   1053 
   1054   if ( (NULL != uc->parse_request.summary) &&
   1055        (NULL != uc->template_contract.summary) )
   1056   {
   1057     GNUNET_break_op (0);
   1058     use_reply_with_error (uc,
   1059                           MHD_HTTP_CONFLICT,
   1060                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_SUMMARY_CONFLICT_TEMPLATES_CONTRACT_SUBJECT,
   1061                           NULL);
   1062     return;
   1063   }
   1064   if ( (NULL == uc->parse_request.summary) &&
   1065        (NULL == uc->template_contract.summary) )
   1066   {
   1067     GNUNET_break_op (0);
   1068     use_reply_with_error (uc,
   1069                           MHD_HTTP_CONFLICT,
   1070                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_SUMMARY,
   1071                           NULL);
   1072     return;
   1073   }
   1074   if ( (! uc->parse_request.no_amount) &&
   1075        (NULL != uc->template_contract.currency) &&
   1076        (0 != strcasecmp (uc->template_contract.currency,
   1077                          uc->parse_request.amount.currency)) )
   1078   {
   1079     GNUNET_break_op (0);
   1080     use_reply_with_error (uc,
   1081                           MHD_HTTP_CONFLICT,
   1082                           TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1083                           uc->template_contract.currency);
   1084     return;
   1085   }
   1086   switch (uc->template_type)
   1087   {
   1088   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
   1089     res = verify_using_templates_fixed (uc);
   1090     break;
   1091   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
   1092     res = verify_using_templates_paivana (uc);
   1093     break;
   1094   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
   1095     res = verify_using_templates_inventory (uc);
   1096     break;
   1097   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
   1098     GNUNET_assert (0);
   1099   }
   1100   if (GNUNET_OK == res)
   1101     uc->phase++;
   1102 }
   1103 
   1104 
   1105 /* ***************** USE_PHASE_COMPUTE_PRICE **************** */
   1106 
   1107 
   1108 /**
   1109  * Compute the line total for a product based on quantity.
   1110  *
   1111  * @param unit_price price per unit
   1112  * @param quantity integer quantity
   1113  * @param quantity_frac fractional quantity (0..TALER_MERCHANT_UNIT_FRAC_BASE-1)
   1114  * @param[out] line_total resulting line total
   1115  * @return #GNUNET_OK on success
   1116  */
   1117 static enum GNUNET_GenericReturnValue
   1118 compute_line_total (const struct TALER_Amount *unit_price,
   1119                     uint64_t quantity,
   1120                     uint32_t quantity_frac,
   1121                     struct TALER_Amount *line_total)
   1122 {
   1123   struct TALER_Amount tmp;
   1124 
   1125   GNUNET_assert (GNUNET_OK ==
   1126                  TALER_amount_set_zero (unit_price->currency,
   1127                                         line_total));
   1128   if ( (0 != quantity) &&
   1129        (0 >
   1130         TALER_amount_multiply (line_total,
   1131                                unit_price,
   1132                                (uint32_t) quantity)) )
   1133   {
   1134     GNUNET_break (0);
   1135     return GNUNET_SYSERR;
   1136   }
   1137   if (0 == quantity_frac)
   1138     return GNUNET_OK;
   1139   if (0 >
   1140       TALER_amount_multiply (&tmp,
   1141                              unit_price,
   1142                              quantity_frac))
   1143   {
   1144     GNUNET_break (0);
   1145     return GNUNET_SYSERR;
   1146   }
   1147   TALER_amount_divide (&tmp,
   1148                        &tmp,
   1149                        TALER_MERCHANT_UNIT_FRAC_BASE);
   1150   if (0 >
   1151       TALER_amount_add (line_total,
   1152                         line_total,
   1153                         &tmp))
   1154   {
   1155     GNUNET_break (0);
   1156     return GNUNET_SYSERR;
   1157   }
   1158   return GNUNET_OK;
   1159 }
   1160 
   1161 
   1162 /**
   1163  * Find the price of the given @a item in the specified
   1164  * @a currency.
   1165  *
   1166  * @param currency currency to search price in
   1167  * @param item item to check prices of
   1168  * @return NULL if a suitable price was not found
   1169  */
   1170 static const struct TALER_Amount *
   1171 find_item_price_in_currency (
   1172   const char *currency,
   1173   const struct InventoryTemplateItemContext *item)
   1174 {
   1175   for (size_t j = 0; j < item->pd.price_array_length; j++)
   1176   {
   1177     if (0 == strcasecmp (item->pd.price_array[j].currency,
   1178                          currency))
   1179       return &item->pd.price_array[j];
   1180   }
   1181   return NULL;
   1182 }
   1183 
   1184 
   1185 /**
   1186  * Compute totals for all currencies shared across selected products.
   1187  *
   1188  * @param[in,out] uc use context
   1189  * @return #GNUNET_OK on success (including no price due to no items)
   1190  *         #GNUNET_NO if we could not find a price in any accepted currency
   1191  *                    for all selected products
   1192  *         #GNUNET_SYSERR on arithmetic issues (internal error)
   1193  */
   1194 static enum GNUNET_GenericReturnValue
   1195 compute_totals_per_currency (struct UseContext *uc)
   1196 {
   1197   const struct InventoryTemplateItemContext *items
   1198     = uc->parse_request.inventory.items;
   1199   unsigned int items_len = uc->parse_request.inventory.items_len;
   1200 
   1201   if (0 == items_len)
   1202     return GNUNET_OK;
   1203   for (size_t i = 0; i < items[0].pd.price_array_length; i++)
   1204   {
   1205     const struct TALER_Amount *price
   1206       = &items[0].pd.price_array[i];
   1207     struct TALER_Amount zero;
   1208 
   1209     if (! TMH_test_exchange_configured_for_currency (price->currency))
   1210       continue;
   1211     GNUNET_assert (GNUNET_OK ==
   1212                    TALER_amount_set_zero (price->currency,
   1213                                           &zero));
   1214     GNUNET_array_append (uc->compute_price.totals,
   1215                          uc->compute_price.totals_len,
   1216                          zero);
   1217   }
   1218   if (0 == uc->compute_price.totals_len)
   1219   {
   1220     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1221                 "No currency supported by our configuration in which we have prices for first selected product!\n");
   1222     return GNUNET_NO;
   1223   }
   1224   /* Loop through items, ensure each currency exists and sum totals. */
   1225   for (unsigned int i = 0; i < items_len; i++)
   1226   {
   1227     const struct InventoryTemplateItemContext *item = &items[i];
   1228     unsigned int c = 0;
   1229 
   1230     while (c < uc->compute_price.totals_len)
   1231     {
   1232       struct TALER_Amount *total = &uc->compute_price.totals[c];
   1233       const struct TALER_Amount *unit_price;
   1234       struct TALER_Amount line_total;
   1235 
   1236       unit_price = find_item_price_in_currency (total->currency,
   1237                                                 item);
   1238       if (NULL == unit_price)
   1239       {
   1240         /* Drop the currency: we have no price in one of
   1241            the selected products */
   1242         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1243                     "Product `%s' has no price in %s: dropping currency\n",
   1244                     item->product_id,
   1245                     total->currency);
   1246         *total = uc->compute_price.totals[--uc->compute_price.totals_len];
   1247         continue;
   1248       }
   1249       if (GNUNET_OK !=
   1250           compute_line_total (unit_price,
   1251                               item->quantity_value,
   1252                               item->quantity_frac,
   1253                               &line_total))
   1254       {
   1255         GNUNET_break (0);
   1256         return GNUNET_SYSERR;
   1257       }
   1258       if (0 >
   1259           TALER_amount_add (total,
   1260                             total,
   1261                             &line_total))
   1262       {
   1263         GNUNET_break (0);
   1264         return GNUNET_SYSERR;
   1265       }
   1266       c++;
   1267     }
   1268   }
   1269   if (0 == uc->compute_price.totals_len)
   1270   {
   1271     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1272                 "No currency available in which we have prices for all selected products!\n");
   1273     GNUNET_free (uc->compute_price.totals);
   1274   }
   1275   return (0 == uc->compute_price.totals_len)
   1276     ? GNUNET_NO
   1277     : GNUNET_OK;
   1278 }
   1279 
   1280 
   1281 /**
   1282  * Compute total for only the given @a currency.
   1283  *
   1284  * @param items_len length of @a items
   1285  * @param items inventory items
   1286  * @param currency currency to total
   1287  * @param[out] total computed total
   1288  * @return #GNUNET_OK on success
   1289  *         #GNUNET_NO if we could not find a price in any accepted currency
   1290  *                    for all selected products
   1291  *         #GNUNET_SYSERR on arithmetic issues (internal error)
   1292  */
   1293 static enum GNUNET_GenericReturnValue
   1294 compute_inventory_total (unsigned int items_len,
   1295                          const struct InventoryTemplateItemContext *items,
   1296                          const char *currency,
   1297                          struct TALER_Amount *total)
   1298 {
   1299   GNUNET_assert (NULL != currency);
   1300   GNUNET_assert (GNUNET_OK ==
   1301                  TALER_amount_set_zero (currency,
   1302                                         total));
   1303   for (unsigned int i = 0; i < items_len; i++)
   1304   {
   1305     const struct InventoryTemplateItemContext *item = &items[i];
   1306     const struct TALER_Amount *unit_price;
   1307     struct TALER_Amount line_total;
   1308 
   1309     unit_price = find_item_price_in_currency (currency,
   1310                                               item);
   1311     if (NULL == unit_price)
   1312     {
   1313       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1314                   "compute_inventory_total: no price in %s for product `%s'\n",
   1315                   currency,
   1316                   item->product_id);
   1317       return GNUNET_NO;
   1318     }
   1319     if (GNUNET_OK !=
   1320         compute_line_total (unit_price,
   1321                             item->quantity_value,
   1322                             item->quantity_frac,
   1323                             &line_total))
   1324     {
   1325       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1326                   "compute_inventory_total: line total failed for %s in %s\n",
   1327                   item->product_id,
   1328                   currency);
   1329       return GNUNET_SYSERR;
   1330     }
   1331     if (0 >
   1332         TALER_amount_add (total,
   1333                           total,
   1334                           &line_total))
   1335     {
   1336       GNUNET_break (0);
   1337       return GNUNET_SYSERR;
   1338     }
   1339   }
   1340   return GNUNET_OK;
   1341 }
   1342 
   1343 
   1344 /**
   1345  * Compute total price.
   1346  *
   1347  * @param[in,out] uc use context
   1348  */
   1349 static void
   1350 handle_phase_compute_price (struct UseContext *uc)
   1351 {
   1352   const char *primary_currency;
   1353   enum GNUNET_GenericReturnValue ret;
   1354 
   1355   switch (uc->template_type)
   1356   {
   1357   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
   1358     uc->compute_price.totals
   1359       = GNUNET_new (struct TALER_Amount);
   1360     uc->compute_price.totals_len
   1361       = 1;
   1362     if (uc->parse_request.no_amount)
   1363     {
   1364       GNUNET_assert (! uc->template_contract.no_amount);
   1365       *uc->compute_price.totals
   1366         = uc->template_contract.amount;
   1367     }
   1368     else
   1369     {
   1370       GNUNET_assert (uc->template_contract.no_amount);
   1371       *uc->compute_price.totals
   1372         = uc->parse_request.amount;
   1373     }
   1374     uc->phase++;
   1375     return;
   1376   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
   1377     /* handled below */
   1378     break;
   1379   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
   1380     {
   1381       const struct TALER_MERCHANT_TemplateContractPaivana *tcp
   1382         = &uc->template_contract.details.paivana;
   1383       json_t *choices;
   1384 
   1385       choices = json_array ();
   1386       GNUNET_assert (NULL != choices);
   1387       for (size_t i = 0; i < tcp->choices_len; i++)
   1388       {
   1389         /* Make deep copy, we're going to MODIFY it! */
   1390         struct TALER_MERCHANT_OrderChoice choice
   1391           = tcp->choices[i];
   1392 
   1393         choice.no_tip = uc->parse_request.no_tip;
   1394         if (! uc->parse_request.no_tip)
   1395         {
   1396           if (GNUNET_YES !=
   1397               TALER_amount_cmp_currency (&choice.amount,
   1398                                          &uc->parse_request.tip))
   1399             continue; /* tip does not match choice currency */
   1400           choice.tip = uc->parse_request.tip;
   1401           if (0 >
   1402               TALER_amount_add (&choice.amount,
   1403                                 &choice.amount,
   1404                                 &uc->parse_request.tip))
   1405           {
   1406             GNUNET_break (0);
   1407             use_reply_with_error (uc,
   1408                                   MHD_HTTP_INTERNAL_SERVER_ERROR,
   1409                                   TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
   1410                                   "tip");
   1411             return;
   1412           }
   1413         }
   1414         GNUNET_assert (0 ==
   1415                        json_array_append_new (
   1416                          choices,
   1417                          TALER_MERCHANT_json_from_order_choice (&choice)));
   1418       }
   1419       if (0 == json_array_size (choices))
   1420       {
   1421         GNUNET_break_op (0);
   1422         json_decref (choices);
   1423         use_reply_with_error (uc,
   1424                               MHD_HTTP_CONFLICT,
   1425                               TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_CURRENCY,
   1426                               "tip");
   1427         return;
   1428       }
   1429       uc->compute_price.choices = choices;
   1430     }
   1431     /* Note: we already did the tip and pricing
   1432        fully here, so we skip these phases. */
   1433     uc->phase = USE_PHASE_CREATE_ORDER;
   1434     return;
   1435   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
   1436     GNUNET_assert (0);
   1437   }
   1438   primary_currency = uc->template_contract.currency;
   1439   if (! uc->parse_request.no_amount)
   1440     primary_currency = uc->parse_request.amount.currency;
   1441   if (! uc->parse_request.no_tip)
   1442     primary_currency = uc->parse_request.tip.currency;
   1443   if (NULL == primary_currency)
   1444   {
   1445     ret = compute_totals_per_currency (uc);
   1446   }
   1447   else
   1448   {
   1449     uc->compute_price.totals
   1450       = GNUNET_new (struct TALER_Amount);
   1451     uc->compute_price.totals_len
   1452       = 1;
   1453     ret = compute_inventory_total (
   1454       uc->parse_request.inventory.items_len,
   1455       uc->parse_request.inventory.items,
   1456       primary_currency,
   1457       uc->compute_price.totals);
   1458   }
   1459   if (GNUNET_SYSERR == ret)
   1460   {
   1461     use_reply_with_error (
   1462       uc,
   1463       MHD_HTTP_INTERNAL_SERVER_ERROR,
   1464       TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
   1465       "calculation of currency totals failed");
   1466     return;
   1467   }
   1468   if (GNUNET_NO == ret)
   1469   {
   1470     use_reply_with_error (uc,
   1471                           MHD_HTTP_CONFLICT,
   1472                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_CURRENCY,
   1473                           NULL);
   1474     return;
   1475   }
   1476 
   1477   uc->phase++;
   1478 }
   1479 
   1480 
   1481 /* ***************** USE_PHASE_CHECK_TIP **************** */
   1482 
   1483 
   1484 /**
   1485  * Check that tip specified is reasonable and add to total.
   1486  *
   1487  * @param[in,out] uc use context
   1488  */
   1489 static void
   1490 handle_phase_check_tip (struct UseContext *uc)
   1491 {
   1492   struct TALER_Amount *total_amount;
   1493 
   1494   if (uc->parse_request.no_tip)
   1495   {
   1496     uc->phase++;
   1497     return;
   1498   }
   1499   if (0 == uc->compute_price.totals_len)
   1500   {
   1501     if (! TMH_test_exchange_configured_for_currency (
   1502           uc->parse_request.tip.currency))
   1503     {
   1504       GNUNET_break_op (0);
   1505       use_reply_with_error (uc,
   1506                             MHD_HTTP_CONFLICT,
   1507                             TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1508                             "Tip currency is not supported by backend");
   1509       return;
   1510     }
   1511     uc->compute_price.totals
   1512       = GNUNET_new (struct TALER_Amount);
   1513     uc->compute_price.totals_len
   1514       = 1;
   1515     *uc->compute_price.totals
   1516       = uc->parse_request.tip;
   1517     uc->phase++;
   1518     return;
   1519   }
   1520   GNUNET_assert (1 == uc->compute_price.totals_len);
   1521   total_amount = &uc->compute_price.totals[0];
   1522   if (GNUNET_YES !=
   1523       TALER_amount_cmp_currency (&uc->parse_request.tip,
   1524                                  total_amount))
   1525   {
   1526     GNUNET_break_op (0);
   1527     use_reply_with_error (uc,
   1528                           MHD_HTTP_CONFLICT,
   1529                           TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1530                           uc->parse_request.tip.currency);
   1531     return;
   1532   }
   1533   if (0 >
   1534       TALER_amount_add (total_amount,
   1535                         total_amount,
   1536                         &uc->parse_request.tip))
   1537   {
   1538     GNUNET_break (0);
   1539     use_reply_with_error (uc,
   1540                           MHD_HTTP_INTERNAL_SERVER_ERROR,
   1541                           TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
   1542                           "tip");
   1543     return;
   1544   }
   1545   uc->phase++;
   1546 }
   1547 
   1548 
   1549 /* ***************** USE_PHASE_CHECK_TOTAL **************** */
   1550 
   1551 /**
   1552  * Check that if the client specified a total,
   1553  * it matches our own calculation.
   1554  *
   1555  * @param[in,out] uc use context
   1556  */
   1557 static void
   1558 handle_phase_check_total (struct UseContext *uc)
   1559 {
   1560   GNUNET_assert (1 <= uc->compute_price.totals_len);
   1561   if (! uc->parse_request.no_amount)
   1562   {
   1563     GNUNET_assert (1 == uc->compute_price.totals_len);
   1564     GNUNET_assert (GNUNET_YES ==
   1565                    TALER_amount_cmp_currency (&uc->parse_request.amount,
   1566                                               &uc->compute_price.totals[0]));
   1567     if (0 !=
   1568         TALER_amount_cmp (&uc->parse_request.amount,
   1569                           &uc->compute_price.totals[0]))
   1570     {
   1571       GNUNET_break_op (0);
   1572       use_reply_with_error (uc,
   1573                             MHD_HTTP_CONFLICT,
   1574                             TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
   1575                             TALER_amount2s (&uc->compute_price.totals[0]));
   1576       return;
   1577     }
   1578   }
   1579   uc->phase++;
   1580 }
   1581 
   1582 
   1583 /* ***************** USE_PHASE_CREATE_ORDER **************** */
   1584 
   1585 
   1586 /**
   1587  * Create order request for inventory templates.
   1588  *
   1589  * @param[in,out] uc use context
   1590  */
   1591 static void
   1592 create_using_templates_inventory (struct UseContext *uc)
   1593 {
   1594   json_t *inventory_products;
   1595   json_t *choices;
   1596 
   1597   inventory_products = json_array ();
   1598   GNUNET_assert (NULL != inventory_products);
   1599   for (unsigned int i = 0;
   1600        i < uc->parse_request.inventory.items_len;
   1601        i++)
   1602   {
   1603     const struct InventoryTemplateItemContext *item =
   1604       &uc->parse_request.inventory.items[i];
   1605 
   1606     GNUNET_assert (0 ==
   1607                    json_array_append_new (
   1608                      inventory_products,
   1609                      GNUNET_JSON_PACK (
   1610                        GNUNET_JSON_pack_string ("product_id",
   1611                                                 item->product_id),
   1612                        GNUNET_JSON_pack_string ("unit_quantity",
   1613                                                 item->unit_quantity))));
   1614   }
   1615   choices = json_array ();
   1616   GNUNET_assert (NULL != choices);
   1617   for (unsigned int i = 0;
   1618        i < uc->compute_price.totals_len;
   1619        i++)
   1620   {
   1621     GNUNET_assert (0 ==
   1622                    json_array_append_new (
   1623                      choices,
   1624                      GNUNET_JSON_PACK (
   1625                        TALER_JSON_pack_amount ("amount",
   1626                                                &uc->compute_price.totals[i]),
   1627                        GNUNET_JSON_pack_allow_null (
   1628                          TALER_JSON_pack_amount ("tip",
   1629                                                  uc->parse_request.no_tip
   1630                                                 ? NULL
   1631                                                 : &uc->parse_request.tip))
   1632                        )));
   1633   }
   1634 
   1635   uc->ihc.request_body
   1636     = GNUNET_JSON_PACK (
   1637         GNUNET_JSON_pack_allow_null (
   1638           GNUNET_JSON_pack_string ("otp_id",
   1639                                    uc->lookup_template.etp.otp_id)),
   1640         GNUNET_JSON_pack_array_steal ("inventory_products",
   1641                                       inventory_products),
   1642         GNUNET_JSON_pack_object_steal (
   1643           "order",
   1644           GNUNET_JSON_PACK (
   1645             GNUNET_JSON_pack_uint64 ("version",
   1646                                      1),
   1647             GNUNET_JSON_pack_array_steal ("choices",
   1648                                           choices),
   1649             GNUNET_JSON_pack_string ("summary",
   1650                                      NULL == uc->parse_request.summary
   1651                                    ? uc->template_contract.summary
   1652                                    : uc->parse_request.summary))));
   1653   if (! GNUNET_TIME_relative_is_forever (
   1654         uc->template_contract.max_pickup_duration))
   1655   {
   1656     GNUNET_assert (
   1657       0 ==
   1658       json_object_set_new (
   1659         uc->ihc.request_body,
   1660         "max_pickup_time",
   1661         GNUNET_JSON_from_timestamp (
   1662           GNUNET_TIME_absolute_to_timestamp (
   1663             GNUNET_TIME_relative_to_absolute (
   1664               uc->template_contract.max_pickup_duration)))));
   1665   }
   1666 }
   1667 
   1668 
   1669 /**
   1670  * Create order request for fixed-order templates.
   1671  *
   1672  * @param[in,out] uc use context
   1673  */
   1674 static void
   1675 create_using_templates_fixed (struct UseContext *uc)
   1676 {
   1677   uc->ihc.request_body
   1678     = GNUNET_JSON_PACK (
   1679         GNUNET_JSON_pack_allow_null (
   1680           GNUNET_JSON_pack_string ("otp_id",
   1681                                    uc->lookup_template.etp.otp_id)),
   1682         GNUNET_JSON_pack_object_steal (
   1683           "order",
   1684           GNUNET_JSON_PACK (
   1685             TALER_JSON_pack_amount (
   1686               "amount",
   1687               &uc->compute_price.totals[0]),
   1688             GNUNET_JSON_pack_allow_null (
   1689               TALER_JSON_pack_amount ("tip",
   1690                                       uc->parse_request.no_tip
   1691                                       ? NULL
   1692                                       : &uc->parse_request.tip)),
   1693             GNUNET_JSON_pack_string (
   1694               "summary",
   1695               NULL == uc->parse_request.summary
   1696             ? uc->template_contract.summary
   1697             : uc->parse_request.summary))));
   1698 }
   1699 
   1700 
   1701 /**
   1702  * Create order request for paivana templates.
   1703  *
   1704  * @param[in,out] uc use context
   1705  */
   1706 static void
   1707 create_using_templates_paivana (struct UseContext *uc)
   1708 {
   1709   uc->ihc.request_body
   1710     = GNUNET_JSON_PACK (
   1711         GNUNET_JSON_pack_string (
   1712           "session_id",
   1713           uc->parse_request.paivana.paivana_id),
   1714         GNUNET_JSON_pack_object_steal (
   1715           "order",
   1716           GNUNET_JSON_PACK (
   1717             GNUNET_JSON_pack_uint64 ("version",
   1718                                      1),
   1719             GNUNET_JSON_pack_array_incref ("choices",
   1720                                            uc->compute_price.choices),
   1721             GNUNET_JSON_pack_string (
   1722               "summary",
   1723               NULL == uc->parse_request.summary
   1724               ? uc->template_contract.summary
   1725               : uc->parse_request.summary),
   1726             GNUNET_JSON_pack_string ("fulfillment_url",
   1727                                      uc->parse_request.paivana.website))));
   1728 }
   1729 
   1730 
   1731 static void
   1732 handle_phase_create_order (struct UseContext *uc)
   1733 {
   1734   GNUNET_assert (NULL == uc->ihc.request_body);
   1735   switch (uc->template_type)
   1736   {
   1737   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
   1738     create_using_templates_fixed (uc);
   1739     break;
   1740   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
   1741     create_using_templates_paivana (uc);
   1742     break;
   1743   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
   1744     create_using_templates_inventory (uc);
   1745     break;
   1746   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
   1747     GNUNET_assert (0);
   1748   }
   1749   uc->phase++;
   1750 }
   1751 
   1752 
   1753 /* ***************** Main handler **************** */
   1754 
   1755 enum MHD_Result
   1756 TMH_post_using_templates_ID (
   1757   const struct TMH_RequestHandler *rh,
   1758   struct MHD_Connection *connection,
   1759   struct TMH_HandlerContext *hc)
   1760 {
   1761   struct UseContext *uc = hc->ctx;
   1762 
   1763   (void) rh;
   1764   if (NULL == uc)
   1765   {
   1766     uc = GNUNET_new (struct UseContext);
   1767     uc->hc = hc;
   1768     hc->ctx = uc;
   1769     hc->cc = &cleanup_use_context;
   1770     uc->ihc.instance = hc->instance;
   1771     uc->phase = USE_PHASE_PARSE_REQUEST;
   1772     uc->template_type = TALER_MERCHANT_TEMPLATE_TYPE_INVALID;
   1773   }
   1774 
   1775   while (1)
   1776   {
   1777     switch (uc->phase)
   1778     {
   1779     case USE_PHASE_PARSE_REQUEST:
   1780       handle_phase_parse_request (uc);
   1781       break;
   1782     case USE_PHASE_LOOKUP_TEMPLATE:
   1783       handle_phase_lookup_template (uc);
   1784       break;
   1785     case USE_PHASE_PARSE_TEMPLATE:
   1786       handle_phase_template_contract (uc);
   1787       break;
   1788     case USE_PHASE_DB_FETCH:
   1789       handle_phase_db_fetch (uc);
   1790       break;
   1791     case USE_PHASE_VERIFY:
   1792       handle_phase_verify (uc);
   1793       break;
   1794     case USE_PHASE_COMPUTE_PRICE:
   1795       handle_phase_compute_price (uc);
   1796       break;
   1797     case USE_PHASE_CHECK_TIP:
   1798       handle_phase_check_tip (uc);
   1799       break;
   1800     case USE_PHASE_CHECK_TOTAL:
   1801       handle_phase_check_total (uc);
   1802       break;
   1803     case USE_PHASE_CREATE_ORDER:
   1804       handle_phase_create_order (uc);
   1805       break;
   1806     case USE_PHASE_SUBMIT_ORDER:
   1807       return TMH_private_post_orders (
   1808         NULL,    /* not even used */
   1809         connection,
   1810         &uc->ihc);
   1811     case USE_PHASE_FINISHED_MHD_YES:
   1812       return MHD_YES;
   1813     case USE_PHASE_FINISHED_MHD_NO:
   1814       return MHD_NO;
   1815     }
   1816   }
   1817 }