merchant

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

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


      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 (unsigned int items_len,
    294                          struct InventoryTemplateItemContext items[static
    295                                                                    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   GNUNET_free (uc->compute_price.totals);
    324   uc->compute_price.totals_len = 0;
    325   json_decref (uc->compute_price.choices);
    326   if (NULL != uc->ihc.cc)
    327     uc->ihc.cc (uc->ihc.ctx);
    328   GNUNET_free (uc->ihc.infix);
    329   json_decref (uc->ihc.request_body);
    330   GNUNET_free (uc);
    331 }
    332 
    333 
    334 /**
    335  * Finalize a template use request.
    336  *
    337  * @param[in,out] uc use context
    338  * @param ret handler return value
    339  */
    340 static void
    341 use_finalize (struct UseContext *uc,
    342               enum MHD_Result ret)
    343 {
    344   uc->phase = (MHD_YES == ret)
    345     ? USE_PHASE_FINISHED_MHD_YES
    346     : USE_PHASE_FINISHED_MHD_NO;
    347 }
    348 
    349 
    350 /**
    351  * Finalize after JSON parsing result.
    352  *
    353  * @param[in,out] uc use context
    354  * @param res parse result
    355  */
    356 static void
    357 use_finalize_parse (struct UseContext *uc,
    358                     enum GNUNET_GenericReturnValue res)
    359 {
    360   GNUNET_assert (GNUNET_OK != res);
    361   use_finalize (uc,
    362                 (GNUNET_NO == res)
    363                 ? MHD_YES
    364                 : MHD_NO);
    365 }
    366 
    367 
    368 /**
    369  * Reply with error and finalize the request.
    370  *
    371  * @param[in,out] uc use context
    372  * @param http_status HTTP status code
    373  * @param ec error code
    374  * @param detail error detail
    375  */
    376 static void
    377 use_reply_with_error (struct UseContext *uc,
    378                       unsigned int http_status,
    379                       enum TALER_ErrorCode ec,
    380                       const char *detail)
    381 {
    382   enum MHD_Result mret;
    383 
    384   mret = TALER_MHD_reply_with_error (uc->hc->connection,
    385                                      http_status,
    386                                      ec,
    387                                      detail);
    388   use_finalize (uc,
    389                 mret);
    390 }
    391 
    392 
    393 /* ***************** USE_PHASE_PARSE_REQUEST **************** */
    394 
    395 /**
    396  * Parse request data for inventory templates.
    397  *
    398  * @param[in,out] uc use context
    399  * @return #GNUNET_OK on success
    400  */
    401 static enum GNUNET_GenericReturnValue
    402 parse_using_templates_inventory_request (
    403   struct UseContext *uc)
    404 {
    405   const json_t *inventory_selection;
    406   struct GNUNET_JSON_Specification spec[] = {
    407     GNUNET_JSON_spec_array_const ("inventory_selection",
    408                                   &inventory_selection),
    409     GNUNET_JSON_spec_end ()
    410   };
    411   enum GNUNET_GenericReturnValue res;
    412 
    413   GNUNET_assert (NULL == uc->ihc.request_body);
    414   res = TALER_MHD_parse_json_data (uc->hc->connection,
    415                                    uc->hc->request_body,
    416                                    spec);
    417   if (GNUNET_OK != res)
    418   {
    419     GNUNET_break_op (0);
    420     use_finalize_parse (uc,
    421                         res);
    422     return GNUNET_SYSERR;
    423   }
    424 
    425   if ( (! uc->parse_request.no_amount) &&
    426        (! TMH_test_exchange_configured_for_currency (
    427           uc->parse_request.amount.currency)) )
    428   {
    429     GNUNET_break_op (0);
    430     use_reply_with_error (uc,
    431                           MHD_HTTP_CONFLICT,
    432                           TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
    433                           "Currency is not supported by backend");
    434     return GNUNET_SYSERR;
    435   }
    436 
    437   for (size_t i = 0; i < json_array_size (inventory_selection); i++)
    438   {
    439     struct InventoryTemplateItemContext item = { 0 };
    440     struct GNUNET_JSON_Specification ispec[] = {
    441       GNUNET_JSON_spec_string ("product_id",
    442                                &item.product_id),
    443       GNUNET_JSON_spec_string ("quantity",
    444                                &item.unit_quantity),
    445       GNUNET_JSON_spec_end ()
    446     };
    447     const char *err_name;
    448     unsigned int err_line;
    449 
    450     res = GNUNET_JSON_parse (json_array_get (inventory_selection,
    451                                              i),
    452                              ispec,
    453                              &err_name,
    454                              &err_line);
    455     if (GNUNET_OK != res)
    456     {
    457       GNUNET_break_op (0);
    458       use_reply_with_error (uc,
    459                             MHD_HTTP_BAD_REQUEST,
    460                             TALER_EC_GENERIC_PARAMETER_MALFORMED,
    461                             "inventory_selection");
    462       return GNUNET_SYSERR;
    463     }
    464 
    465     GNUNET_array_append (uc->parse_request.inventory.items,
    466                          uc->parse_request.inventory.items_len,
    467                          item);
    468   }
    469   return GNUNET_OK;
    470 }
    471 
    472 
    473 /**
    474  * Parse request data for paivana templates.
    475  *
    476  * @param[in,out] uc use context
    477  * @return #GNUNET_OK on success
    478  */
    479 static enum GNUNET_GenericReturnValue
    480 parse_using_templates_paivana_request (
    481   struct UseContext *uc)
    482 {
    483   struct GNUNET_JSON_Specification spec[] = {
    484     GNUNET_JSON_spec_string ("website",
    485                              &uc->parse_request.paivana.website),
    486     GNUNET_JSON_spec_string ("paivana_id",
    487                              &uc->parse_request.paivana.paivana_id),
    488     GNUNET_JSON_spec_end ()
    489   };
    490   enum GNUNET_GenericReturnValue res;
    491   struct GNUNET_HashCode sh;
    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   GNUNET_assert (NULL != dash);
    521   if (GNUNET_OK !=
    522       GNUNET_STRINGS_string_to_data (dash + 1,
    523                                      strlen (dash + 1),
    524                                      &sh,
    525                                      sizeof (sh)))
    526   {
    527     GNUNET_break_op (0);
    528     use_reply_with_error (uc,
    529                           MHD_HTTP_BAD_REQUEST,
    530                           TALER_EC_GENERIC_PARAMETER_MALFORMED,
    531                           "paivana_id");
    532     return GNUNET_SYSERR;
    533   }
    534   return GNUNET_OK;
    535 }
    536 
    537 
    538 /**
    539  * Main function for the #USE_PHASE_PARSE_REQUEST.
    540  *
    541  * @param[in,out] uc context to update
    542  */
    543 static void
    544 handle_phase_parse_request (
    545   struct UseContext *uc)
    546 {
    547   const char *template_type = NULL;
    548   struct GNUNET_JSON_Specification spec[] = {
    549     GNUNET_JSON_spec_mark_optional (
    550       GNUNET_JSON_spec_string ("template_type",
    551                                &template_type),
    552       NULL),
    553     GNUNET_JSON_spec_mark_optional (
    554       TALER_JSON_spec_amount_any ("tip",
    555                                   &uc->parse_request.tip),
    556       &uc->parse_request.no_tip),
    557     GNUNET_JSON_spec_mark_optional (
    558       GNUNET_JSON_spec_string ("summary",
    559                                &uc->parse_request.summary),
    560       NULL),
    561     GNUNET_JSON_spec_mark_optional (
    562       TALER_JSON_spec_amount_any ("amount",
    563                                   &uc->parse_request.amount),
    564       &uc->parse_request.no_amount),
    565     GNUNET_JSON_spec_end ()
    566   };
    567   enum GNUNET_GenericReturnValue res;
    568 
    569   res = TALER_MHD_parse_json_data (uc->hc->connection,
    570                                    uc->hc->request_body,
    571                                    spec);
    572   if (GNUNET_OK != res)
    573   {
    574     GNUNET_break_op (0);
    575     use_finalize_parse (uc,
    576                         res);
    577     return;
    578   }
    579   if (NULL == template_type)
    580     template_type = "fixed-order";
    581   uc->template_type
    582     = TALER_MERCHANT_template_type_from_string (
    583         template_type);
    584   switch (uc->template_type)
    585   {
    586   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
    587     /* nothig left to do */
    588     uc->phase++;
    589     return;
    590   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
    591     res = parse_using_templates_paivana_request (uc);
    592     if (GNUNET_OK == res)
    593       uc->phase++;
    594     return;
    595   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
    596     res = parse_using_templates_inventory_request (uc);
    597     if (GNUNET_OK == res)
    598       uc->phase++;
    599     return;
    600   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
    601     break;
    602   }
    603   GNUNET_break (0);
    604   use_reply_with_error (
    605     uc,
    606     MHD_HTTP_BAD_REQUEST,
    607     TALER_EC_GENERIC_PARAMETER_MALFORMED,
    608     "template_type");
    609 }
    610 
    611 
    612 /* ***************** USE_PHASE_LOOKUP_TEMPLATE **************** */
    613 
    614 /**
    615  * Main function for the #USE_PHASE_LOOKUP_TEMPLATE.
    616  *
    617  * @param[in,out] uc context to update
    618  */
    619 static void
    620 handle_phase_lookup_template (
    621   struct UseContext *uc)
    622 {
    623   struct TMH_MerchantInstance *mi = uc->hc->instance;
    624   const char *template_id = uc->hc->infix;
    625   enum GNUNET_DB_QueryStatus qs;
    626 
    627   qs = TALER_MERCHANTDB_lookup_template (TMH_db,
    628                                          mi->settings.id,
    629                                          template_id,
    630                                          &uc->lookup_template.etp);
    631   switch (qs)
    632   {
    633   case GNUNET_DB_STATUS_HARD_ERROR:
    634     /* Clean up and fail hard */
    635     GNUNET_break (0);
    636     use_reply_with_error (uc,
    637                           MHD_HTTP_INTERNAL_SERVER_ERROR,
    638                           TALER_EC_GENERIC_DB_FETCH_FAILED,
    639                           "lookup_template");
    640     return;
    641   case GNUNET_DB_STATUS_SOFT_ERROR:
    642     /* this should be impossible (single select) */
    643     GNUNET_break (0);
    644     use_reply_with_error (uc,
    645                           MHD_HTTP_INTERNAL_SERVER_ERROR,
    646                           TALER_EC_GENERIC_DB_FETCH_FAILED,
    647                           "lookup_template");
    648     return;
    649   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    650     /* template not found! */
    651     use_reply_with_error (uc,
    652                           MHD_HTTP_NOT_FOUND,
    653                           TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN,
    654                           template_id);
    655     return;
    656   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    657     /* all good */
    658     break;
    659   }
    660   if (uc->template_type !=
    661       TALER_MERCHANT_template_type_from_contract (
    662         uc->lookup_template.etp.template_contract))
    663   {
    664     GNUNET_break_op (0);
    665     use_reply_with_error (
    666       uc,
    667       MHD_HTTP_CONFLICT,
    668       TALER_EC_MERCHANT_POST_USING_TEMPLATES_WRONG_TYPE,
    669       "template_contract has different type");
    670     return;
    671   }
    672   uc->phase++;
    673 }
    674 
    675 
    676 /* ***************** USE_PHASE_PARSE_TEMPLATE **************** */
    677 
    678 
    679 /**
    680  * Parse template.
    681  *
    682  * @param[in,out] uc use context
    683  */
    684 static void
    685 handle_phase_template_contract (struct UseContext *uc)
    686 {
    687   const char *err_name;
    688   enum GNUNET_GenericReturnValue res;
    689 
    690   res = TALER_MERCHANT_template_contract_parse (
    691     uc->lookup_template.etp.template_contract,
    692     &uc->template_contract,
    693     &err_name);
    694   if (GNUNET_OK != res)
    695   {
    696     GNUNET_break (0);
    697     use_reply_with_error (uc,
    698                           MHD_HTTP_INTERNAL_SERVER_ERROR,
    699                           TALER_EC_GENERIC_DB_FETCH_FAILED,
    700                           err_name);
    701     return;
    702   }
    703   uc->phase++;
    704 }
    705 
    706 
    707 /* ***************** USE_PHASE_DB_FETCH **************** */
    708 
    709 /**
    710  * Fetch DB data for inventory templates.
    711  *
    712  * @param[in,out] uc use context
    713  */
    714 static void
    715 handle_phase_db_fetch (struct UseContext *uc)
    716 {
    717   struct TMH_MerchantInstance *mi = uc->hc->instance;
    718 
    719   switch (uc->template_type)
    720   {
    721   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
    722     uc->phase++;
    723     return;
    724   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
    725     uc->phase++;
    726     return;
    727   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
    728     break;
    729   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
    730     GNUNET_assert (0);
    731   }
    732 
    733   for (unsigned int i = 0;
    734        i < uc->parse_request.inventory.items_len;
    735        i++)
    736   {
    737     struct InventoryTemplateItemContext *item =
    738       &uc->parse_request.inventory.items[i];
    739     enum GNUNET_DB_QueryStatus qs;
    740 
    741     qs = TALER_MERCHANTDB_lookup_product (TMH_db,
    742                                           mi->settings.id,
    743                                           item->product_id,
    744                                           &item->pd,
    745                                           &item->num_categories,
    746                                           &item->categories);
    747     switch (qs)
    748     {
    749     case GNUNET_DB_STATUS_HARD_ERROR:
    750       GNUNET_break (0);
    751       use_reply_with_error (uc,
    752                             MHD_HTTP_INTERNAL_SERVER_ERROR,
    753                             TALER_EC_GENERIC_DB_FETCH_FAILED,
    754                             "lookup_product");
    755       return;
    756     case GNUNET_DB_STATUS_SOFT_ERROR:
    757       GNUNET_break (0);
    758       use_reply_with_error (uc,
    759                             MHD_HTTP_INTERNAL_SERVER_ERROR,
    760                             TALER_EC_GENERIC_DB_FETCH_FAILED,
    761                             "lookup_product");
    762       return;
    763     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    764       use_reply_with_error (uc,
    765                             MHD_HTTP_NOT_FOUND,
    766                             TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN,
    767                             item->product_id);
    768       return;
    769     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    770       break;
    771     }
    772   }
    773   uc->phase++;
    774 }
    775 
    776 
    777 /* *************** Helpers for USE_PHASE_VERIFY ***************** */
    778 
    779 /**
    780  * Check if the given product ID appears in the array of allowed_products.
    781  *
    782  * @param allowed_products JSON array of product IDs allowed by the template, may be NULL
    783  * @param product_id product ID to check
    784  * @return true if the product ID is in the list
    785  */
    786 static bool
    787 product_id_allowed (const json_t *allowed_products,
    788                     const char *product_id)
    789 {
    790   const json_t *entry;
    791   size_t idx;
    792 
    793   if (NULL == allowed_products)
    794     return false;
    795   json_array_foreach ((json_t *) allowed_products, idx, entry)
    796   {
    797     if (! json_is_string (entry))
    798     {
    799       GNUNET_break (0);
    800       continue;
    801     }
    802     if (0 == strcmp (json_string_value (entry),
    803                      product_id))
    804       return true;
    805   }
    806   return false;
    807 }
    808 
    809 
    810 /**
    811  * Check if any product category is in the selected_categories list.
    812  *
    813  * @param allowed_categories JSON array of categories allowed by the template, may be NULL
    814  * @param num_categories length of @a categories
    815  * @param categories list of categories of the selected product
    816  * @return true if any category of the product is in the list of allowed categories matches
    817  */
    818 static bool
    819 category_allowed (const json_t *allowed_categories,
    820                   size_t num_categories,
    821                   const uint64_t categories[num_categories])
    822 {
    823   const json_t *entry;
    824   size_t idx;
    825 
    826   if (NULL == allowed_categories)
    827     return false;
    828   json_array_foreach ((json_t *) allowed_categories,
    829                       idx,
    830                       entry)
    831   {
    832     uint64_t selected_id;
    833 
    834     if (! json_is_integer (entry))
    835     {
    836       GNUNET_break (0);
    837       continue;
    838     }
    839     if (0 > json_integer_value (entry))
    840     {
    841       GNUNET_break (0);
    842       continue;
    843     }
    844     selected_id = (uint64_t) json_integer_value (entry);
    845     for (size_t i = 0; i < num_categories; i++)
    846     {
    847       if (categories[i] == selected_id)
    848         return true;
    849     }
    850   }
    851   return false;
    852 }
    853 
    854 
    855 /**
    856  * Verify request data for inventory templates.
    857  * Checks that the selected products are allowed
    858  * for this template.
    859  *
    860  * @param[in,out] uc use context
    861  * @return #GNUNET_OK on success
    862  */
    863 static enum GNUNET_GenericReturnValue
    864 verify_using_templates_inventory (struct UseContext *uc)
    865 {
    866   if (uc->template_contract.details.inventory.choose_one &&
    867       (1 != uc->parse_request.inventory.items_len))
    868   {
    869     GNUNET_break_op (0);
    870     use_reply_with_error (uc,
    871                           MHD_HTTP_CONFLICT,
    872                           TALER_EC_GENERIC_PARAMETER_MALFORMED,
    873                           "inventory_selection");
    874     return GNUNET_SYSERR;
    875   }
    876   if (uc->template_contract.details.inventory.selected_all)
    877     return GNUNET_OK;
    878   for (unsigned int i = 0;
    879        i < uc->parse_request.inventory.items_len;
    880        i++)
    881   {
    882     struct InventoryTemplateItemContext *item =
    883       &uc->parse_request.inventory.items[i];
    884     const char *eparam = NULL;
    885 
    886     if (GNUNET_OK !=
    887         TALER_MERCHANT_vk_process_quantity_inputs (
    888           TALER_MERCHANT_VK_QUANTITY,
    889           item->pd.allow_fractional_quantity,
    890           true,
    891           0,
    892           false,
    893           item->unit_quantity,
    894           &item->quantity_value,
    895           &item->quantity_frac,
    896           &eparam))
    897     {
    898       GNUNET_break_op (0);
    899       use_reply_with_error (uc,
    900                             MHD_HTTP_BAD_REQUEST,
    901                             TALER_EC_GENERIC_PARAMETER_MALFORMED,
    902                             eparam);
    903       return GNUNET_SYSERR;
    904     }
    905 
    906     if (0 == item->pd.price_array_length)
    907     {
    908       GNUNET_break (0);
    909       use_reply_with_error (uc,
    910                             MHD_HTTP_INTERNAL_SERVER_ERROR,
    911                             TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    912                             "price_array");
    913       return GNUNET_SYSERR;
    914     }
    915   }
    916 
    917   for (unsigned int i = 0;
    918        i < uc->parse_request.inventory.items_len;
    919        i++)
    920   {
    921     struct InventoryTemplateItemContext *item =
    922       &uc->parse_request.inventory.items[i];
    923 
    924     if (product_id_allowed (uc->template_contract.details.inventory.
    925                             selected_products,
    926                             item->product_id))
    927       continue;
    928     if (category_allowed (
    929           uc->template_contract.details.inventory.selected_categories,
    930           item->num_categories,
    931           item->categories))
    932       continue;
    933     GNUNET_break_op (0);
    934     use_reply_with_error (
    935       uc,
    936       MHD_HTTP_CONFLICT,
    937       TALER_EC_MERCHANT_POST_USING_TEMPLATES_WRONG_PRODUCT,
    938       item->product_id);
    939     return GNUNET_SYSERR;
    940   }
    941   return GNUNET_OK;
    942 }
    943 
    944 
    945 /**
    946  * Verify request data for fixed-order templates.
    947  * As here we cannot compute the total amount, either
    948  * the template or the client request must provide it.
    949  *
    950  * @param[in,out] uc use context
    951  * @return #GNUNET_OK on success
    952  */
    953 static enum GNUNET_GenericReturnValue
    954 verify_using_templates_fixed (
    955   struct UseContext *uc)
    956 {
    957   if ( (! uc->parse_request.no_amount) &&
    958        (! uc->template_contract.no_amount) )
    959   {
    960     GNUNET_break_op (0);
    961     use_reply_with_error (uc,
    962                           MHD_HTTP_CONFLICT,
    963                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
    964                           NULL);
    965     return GNUNET_SYSERR;
    966   }
    967   if (uc->parse_request.no_amount &&
    968       uc->template_contract.no_amount)
    969   {
    970     GNUNET_break_op (0);
    971     use_reply_with_error (uc,
    972                           MHD_HTTP_CONFLICT,
    973                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_AMOUNT,
    974                           NULL);
    975     return GNUNET_SYSERR;
    976   }
    977   return GNUNET_OK;
    978 }
    979 
    980 
    981 /**
    982  * Verify request data for paivana templates.
    983  *
    984  * @param[in,out] uc use context
    985  * @return #GNUNET_OK on success
    986  */
    987 static enum GNUNET_GenericReturnValue
    988 verify_using_templates_paivana (
    989   struct UseContext *uc)
    990 {
    991   if (NULL != uc->template_contract.details.paivana.website_regex)
    992   {
    993     regex_t ex;
    994     bool allowed = false;
    995 
    996     if (0 != regcomp (&ex,
    997                       uc->template_contract.details.paivana.website_regex,
    998                       REG_NOSUB | REG_EXTENDED))
    999     {
   1000       GNUNET_break_op (0);
   1001       return GNUNET_SYSERR;
   1002     }
   1003     if (0 ==
   1004         regexec (&ex,
   1005                  uc->parse_request.paivana.website,
   1006                  0, NULL,
   1007                  0))
   1008     {
   1009       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1010                   "Website `%s' allowed by template\n",
   1011                   uc->parse_request.paivana.website);
   1012       allowed = true;
   1013     }
   1014     regfree (&ex);
   1015     if (! allowed)
   1016       return GNUNET_SYSERR;
   1017   }
   1018   return GNUNET_OK;
   1019 }
   1020 
   1021 
   1022 /**
   1023  * Verify that the client request is structurally acceptable for the specified
   1024  * template.  Does NOT check the total amount being reasonable.
   1025  *
   1026  * @param[in,out] uc use context
   1027  */
   1028 static void
   1029 handle_phase_verify (
   1030   struct UseContext *uc)
   1031 {
   1032   enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
   1033 
   1034   if ( (NULL != uc->parse_request.summary) &&
   1035        (NULL != uc->template_contract.summary) )
   1036   {
   1037     GNUNET_break_op (0);
   1038     use_reply_with_error (uc,
   1039                           MHD_HTTP_CONFLICT,
   1040                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_SUMMARY_CONFLICT_TEMPLATES_CONTRACT_SUBJECT,
   1041                           NULL);
   1042     return;
   1043   }
   1044   if ( (NULL == uc->parse_request.summary) &&
   1045        (NULL == uc->template_contract.summary) )
   1046   {
   1047     GNUNET_break_op (0);
   1048     use_reply_with_error (uc,
   1049                           MHD_HTTP_CONFLICT,
   1050                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_SUMMARY,
   1051                           NULL);
   1052     return;
   1053   }
   1054   if ( (! uc->parse_request.no_amount) &&
   1055        (NULL != uc->template_contract.currency) &&
   1056        (0 != strcasecmp (uc->template_contract.currency,
   1057                          uc->parse_request.amount.currency)) )
   1058   {
   1059     GNUNET_break_op (0);
   1060     use_reply_with_error (uc,
   1061                           MHD_HTTP_CONFLICT,
   1062                           TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1063                           uc->template_contract.currency);
   1064     return;
   1065   }
   1066   switch (uc->template_type)
   1067   {
   1068   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
   1069     res = verify_using_templates_fixed (uc);
   1070     break;
   1071   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
   1072     res = verify_using_templates_paivana (uc);
   1073     break;
   1074   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
   1075     res = verify_using_templates_inventory (uc);
   1076     break;
   1077   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
   1078     GNUNET_assert (0);
   1079   }
   1080   if (GNUNET_OK == res)
   1081     uc->phase++;
   1082 }
   1083 
   1084 
   1085 /* ***************** USE_PHASE_COMPUTE_PRICE **************** */
   1086 
   1087 
   1088 /**
   1089  * Compute the line total for a product based on quantity.
   1090  *
   1091  * @param unit_price price per unit
   1092  * @param quantity integer quantity
   1093  * @param quantity_frac fractional quantity (0..TALER_MERCHANT_UNIT_FRAC_BASE-1)
   1094  * @param[out] line_total resulting line total
   1095  * @return #GNUNET_OK on success
   1096  */
   1097 static enum GNUNET_GenericReturnValue
   1098 compute_line_total (const struct TALER_Amount *unit_price,
   1099                     uint64_t quantity,
   1100                     uint32_t quantity_frac,
   1101                     struct TALER_Amount *line_total)
   1102 {
   1103   struct TALER_Amount tmp;
   1104 
   1105   GNUNET_assert (GNUNET_OK ==
   1106                  TALER_amount_set_zero (unit_price->currency,
   1107                                         line_total));
   1108   if ( (0 != quantity) &&
   1109        (0 >
   1110         TALER_amount_multiply (line_total,
   1111                                unit_price,
   1112                                (uint32_t) quantity)) )
   1113   {
   1114     GNUNET_break (0);
   1115     return GNUNET_SYSERR;
   1116   }
   1117   if (0 == quantity_frac)
   1118     return GNUNET_OK;
   1119   if (0 >
   1120       TALER_amount_multiply (&tmp,
   1121                              unit_price,
   1122                              quantity_frac))
   1123   {
   1124     GNUNET_break (0);
   1125     return GNUNET_SYSERR;
   1126   }
   1127   TALER_amount_divide (&tmp,
   1128                        &tmp,
   1129                        TALER_MERCHANT_UNIT_FRAC_BASE);
   1130   if (0 >
   1131       TALER_amount_add (line_total,
   1132                         line_total,
   1133                         &tmp))
   1134   {
   1135     GNUNET_break (0);
   1136     return GNUNET_SYSERR;
   1137   }
   1138   return GNUNET_OK;
   1139 }
   1140 
   1141 
   1142 /**
   1143  * Find the price of the given @a item in the specified
   1144  * @a currency.
   1145  *
   1146  * @param currency currency to search price in
   1147  * @param item item to check prices of
   1148  * @return NULL if a suitable price was not found
   1149  */
   1150 static const struct TALER_Amount *
   1151 find_item_price_in_currency (
   1152   const char *currency,
   1153   const struct InventoryTemplateItemContext *item)
   1154 {
   1155   for (size_t j = 0; j < item->pd.price_array_length; j++)
   1156   {
   1157     if (0 == strcasecmp (item->pd.price_array[j].currency,
   1158                          currency))
   1159       return &item->pd.price_array[j];
   1160   }
   1161   return NULL;
   1162 }
   1163 
   1164 
   1165 /**
   1166  * Compute totals for all currencies shared across selected products.
   1167  *
   1168  * @param[in,out] uc use context
   1169  * @return #GNUNET_OK on success (including no price due to no items)
   1170  *         #GNUNET_NO if we could not find a price in any accepted currency
   1171  *                    for all selected products
   1172  *         #GNUNET_SYSERR on arithmetic issues (internal error)
   1173  */
   1174 static enum GNUNET_GenericReturnValue
   1175 compute_totals_per_currency (struct UseContext *uc)
   1176 {
   1177   const struct InventoryTemplateItemContext *items
   1178     = uc->parse_request.inventory.items;
   1179   unsigned int items_len = uc->parse_request.inventory.items_len;
   1180 
   1181   if (0 == items_len)
   1182     return GNUNET_OK;
   1183   for (size_t i = 0; i < items[0].pd.price_array_length; i++)
   1184   {
   1185     const struct TALER_Amount *price
   1186       = &items[0].pd.price_array[i];
   1187     struct TALER_Amount zero;
   1188 
   1189     if (! TMH_test_exchange_configured_for_currency (price->currency))
   1190       continue;
   1191     GNUNET_assert (GNUNET_OK ==
   1192                    TALER_amount_set_zero (price->currency,
   1193                                           &zero));
   1194     GNUNET_array_append (uc->compute_price.totals,
   1195                          uc->compute_price.totals_len,
   1196                          zero);
   1197   }
   1198   if (0 == uc->compute_price.totals_len)
   1199   {
   1200     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1201                 "No currency supported by our configuration in which we have prices for first selected product!\n");
   1202     return GNUNET_NO;
   1203   }
   1204   /* Loop through items, ensure each currency exists and sum totals. */
   1205   for (unsigned int i = 0; i < items_len; i++)
   1206   {
   1207     const struct InventoryTemplateItemContext *item = &items[i];
   1208     unsigned int c = 0;
   1209 
   1210     while (c < uc->compute_price.totals_len)
   1211     {
   1212       struct TALER_Amount *total = &uc->compute_price.totals[c];
   1213       const struct TALER_Amount *unit_price;
   1214       struct TALER_Amount line_total;
   1215 
   1216       unit_price = find_item_price_in_currency (total->currency,
   1217                                                 item);
   1218       if (NULL == unit_price)
   1219       {
   1220         /* Drop the currency: we have no price in one of
   1221            the selected products */
   1222         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1223                     "Product `%s' has no price in %s: dropping currency\n",
   1224                     item->product_id,
   1225                     total->currency);
   1226         *total = uc->compute_price.totals[--uc->compute_price.totals_len];
   1227         continue;
   1228       }
   1229       if (GNUNET_OK !=
   1230           compute_line_total (unit_price,
   1231                               item->quantity_value,
   1232                               item->quantity_frac,
   1233                               &line_total))
   1234       {
   1235         GNUNET_break (0);
   1236         return GNUNET_SYSERR;
   1237       }
   1238       if (0 >
   1239           TALER_amount_add (total,
   1240                             total,
   1241                             &line_total))
   1242       {
   1243         GNUNET_break (0);
   1244         return GNUNET_SYSERR;
   1245       }
   1246       c++;
   1247     }
   1248   }
   1249   if (0 == uc->compute_price.totals_len)
   1250   {
   1251     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1252                 "No currency available in which we have prices for all selected products!\n");
   1253     GNUNET_free (uc->compute_price.totals);
   1254   }
   1255   return (0 == uc->compute_price.totals_len)
   1256     ? GNUNET_NO
   1257     : GNUNET_OK;
   1258 }
   1259 
   1260 
   1261 /**
   1262  * Compute total for only the given @a currency.
   1263  *
   1264  * @param items_len length of @a items
   1265  * @param items inventory items
   1266  * @param currency currency to total
   1267  * @param[out] total computed total
   1268  * @return #GNUNET_OK on success
   1269  *         #GNUNET_NO if we could not find a price in any accepted currency
   1270  *                    for all selected products
   1271  *         #GNUNET_SYSERR on arithmetic issues (internal error)
   1272  */
   1273 static enum GNUNET_GenericReturnValue
   1274 compute_inventory_total (unsigned int items_len,
   1275                          const struct InventoryTemplateItemContext *items,
   1276                          const char *currency,
   1277                          struct TALER_Amount *total)
   1278 {
   1279   GNUNET_assert (NULL != currency);
   1280   GNUNET_assert (GNUNET_OK ==
   1281                  TALER_amount_set_zero (currency,
   1282                                         total));
   1283   for (unsigned int i = 0; i < items_len; i++)
   1284   {
   1285     const struct InventoryTemplateItemContext *item = &items[i];
   1286     const struct TALER_Amount *unit_price;
   1287     struct TALER_Amount line_total;
   1288 
   1289     unit_price = find_item_price_in_currency (currency,
   1290                                               item);
   1291     if (NULL == unit_price)
   1292     {
   1293       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1294                   "compute_inventory_total: no price in %s for product `%s'\n",
   1295                   currency,
   1296                   item->product_id);
   1297       return GNUNET_NO;
   1298     }
   1299     if (GNUNET_OK !=
   1300         compute_line_total (unit_price,
   1301                             item->quantity_value,
   1302                             item->quantity_frac,
   1303                             &line_total))
   1304     {
   1305       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1306                   "compute_inventory_total: line total failed for %s in %s\n",
   1307                   item->product_id,
   1308                   currency);
   1309       return GNUNET_SYSERR;
   1310     }
   1311     if (0 >
   1312         TALER_amount_add (total,
   1313                           total,
   1314                           &line_total))
   1315     {
   1316       GNUNET_break (0);
   1317       return GNUNET_SYSERR;
   1318     }
   1319   }
   1320   return GNUNET_OK;
   1321 }
   1322 
   1323 
   1324 /**
   1325  * Compute total price.
   1326  *
   1327  * @param[in,out] uc use context
   1328  */
   1329 static void
   1330 handle_phase_compute_price (struct UseContext *uc)
   1331 {
   1332   const char *primary_currency;
   1333   enum GNUNET_GenericReturnValue ret;
   1334 
   1335   switch (uc->template_type)
   1336   {
   1337   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
   1338     uc->compute_price.totals
   1339       = GNUNET_new (struct TALER_Amount);
   1340     uc->compute_price.totals_len
   1341       = 1;
   1342     if (uc->parse_request.no_amount)
   1343     {
   1344       GNUNET_assert (! uc->template_contract.no_amount);
   1345       *uc->compute_price.totals
   1346         = uc->template_contract.amount;
   1347     }
   1348     else
   1349     {
   1350       GNUNET_assert (uc->template_contract.no_amount);
   1351       *uc->compute_price.totals
   1352         = uc->parse_request.amount;
   1353     }
   1354     uc->phase++;
   1355     return;
   1356   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
   1357     /* handled below */
   1358     break;
   1359   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
   1360     {
   1361       const struct TALER_MERCHANT_TemplateContractPaivana *tcp
   1362         = &uc->template_contract.details.paivana;
   1363       json_t *choices;
   1364 
   1365       choices = json_array ();
   1366       GNUNET_assert (NULL != choices);
   1367       for (size_t i = 0; i < tcp->choices_len; i++)
   1368       {
   1369         /* Make deep copy, we're going to MODIFY it! */
   1370         struct TALER_MERCHANT_ContractChoice choice
   1371           = tcp->choices[i];
   1372 
   1373         choice.no_tip = uc->parse_request.no_tip;
   1374         if (! uc->parse_request.no_tip)
   1375         {
   1376           if (GNUNET_YES !=
   1377               TALER_amount_cmp_currency (&choice.amount,
   1378                                          &uc->parse_request.tip))
   1379             continue; /* tip does not match choice currency */
   1380           choice.tip = uc->parse_request.tip;
   1381           if (0 >
   1382               TALER_amount_add (&choice.amount,
   1383                                 &choice.amount,
   1384                                 &uc->parse_request.tip))
   1385           {
   1386             GNUNET_break (0);
   1387             use_reply_with_error (uc,
   1388                                   MHD_HTTP_INTERNAL_SERVER_ERROR,
   1389                                   TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
   1390                                   "tip");
   1391             return;
   1392           }
   1393         }
   1394         GNUNET_assert (0 ==
   1395                        json_array_append_new (
   1396                          choices,
   1397                          TALER_MERCHANT_json_from_contract_choice (&choice,
   1398                                                                    true)));
   1399       }
   1400       if (0 == json_array_size (choices))
   1401       {
   1402         GNUNET_break_op (0);
   1403         json_decref (choices);
   1404         use_reply_with_error (uc,
   1405                               MHD_HTTP_CONFLICT,
   1406                               TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_CURRENCY,
   1407                               "tip");
   1408         return;
   1409       }
   1410       uc->compute_price.choices = choices;
   1411     }
   1412     /* Note: we already did the tip and pricing
   1413        fully here, so we skip these phases. */
   1414     uc->phase = USE_PHASE_CREATE_ORDER;
   1415     return;
   1416   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
   1417     GNUNET_assert (0);
   1418   }
   1419   primary_currency = uc->template_contract.currency;
   1420   if (! uc->parse_request.no_amount)
   1421     primary_currency = uc->parse_request.amount.currency;
   1422   if (! uc->parse_request.no_tip)
   1423     primary_currency = uc->parse_request.tip.currency;
   1424   if (NULL == primary_currency)
   1425   {
   1426     ret = compute_totals_per_currency (uc);
   1427   }
   1428   else
   1429   {
   1430     uc->compute_price.totals
   1431       = GNUNET_new (struct TALER_Amount);
   1432     uc->compute_price.totals_len
   1433       = 1;
   1434     ret = compute_inventory_total (
   1435       uc->parse_request.inventory.items_len,
   1436       uc->parse_request.inventory.items,
   1437       primary_currency,
   1438       uc->compute_price.totals);
   1439   }
   1440   if (GNUNET_SYSERR == ret)
   1441   {
   1442     use_reply_with_error (
   1443       uc,
   1444       MHD_HTTP_INTERNAL_SERVER_ERROR,
   1445       TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
   1446       "calculation of currency totals failed");
   1447     return;
   1448   }
   1449   if (GNUNET_NO == ret)
   1450   {
   1451     use_reply_with_error (uc,
   1452                           MHD_HTTP_CONFLICT,
   1453                           TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_CURRENCY,
   1454                           NULL);
   1455     return;
   1456   }
   1457 
   1458   uc->phase++;
   1459 }
   1460 
   1461 
   1462 /* ***************** USE_PHASE_CHECK_TIP **************** */
   1463 
   1464 
   1465 /**
   1466  * Check that tip specified is reasonable and add to total.
   1467  *
   1468  * @param[in,out] uc use context
   1469  */
   1470 static void
   1471 handle_phase_check_tip (struct UseContext *uc)
   1472 {
   1473   struct TALER_Amount *total_amount;
   1474 
   1475   if (uc->parse_request.no_tip)
   1476   {
   1477     uc->phase++;
   1478     return;
   1479   }
   1480   if (0 == uc->compute_price.totals_len)
   1481   {
   1482     if (! TMH_test_exchange_configured_for_currency (
   1483           uc->parse_request.tip.currency))
   1484     {
   1485       GNUNET_break_op (0);
   1486       use_reply_with_error (uc,
   1487                             MHD_HTTP_CONFLICT,
   1488                             TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1489                             "Tip currency is not supported by backend");
   1490       return;
   1491     }
   1492     uc->compute_price.totals
   1493       = GNUNET_new (struct TALER_Amount);
   1494     uc->compute_price.totals_len
   1495       = 1;
   1496     *uc->compute_price.totals
   1497       = uc->parse_request.tip;
   1498     uc->phase++;
   1499     return;
   1500   }
   1501   GNUNET_assert (1 == uc->compute_price.totals_len);
   1502   total_amount = &uc->compute_price.totals[0];
   1503   if (GNUNET_YES !=
   1504       TALER_amount_cmp_currency (&uc->parse_request.tip,
   1505                                  total_amount))
   1506   {
   1507     GNUNET_break_op (0);
   1508     use_reply_with_error (uc,
   1509                           MHD_HTTP_CONFLICT,
   1510                           TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
   1511                           uc->parse_request.tip.currency);
   1512     return;
   1513   }
   1514   if (0 >
   1515       TALER_amount_add (total_amount,
   1516                         total_amount,
   1517                         &uc->parse_request.tip))
   1518   {
   1519     GNUNET_break (0);
   1520     use_reply_with_error (uc,
   1521                           MHD_HTTP_INTERNAL_SERVER_ERROR,
   1522                           TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
   1523                           "tip");
   1524     return;
   1525   }
   1526   uc->phase++;
   1527 }
   1528 
   1529 
   1530 /* ***************** USE_PHASE_CHECK_TOTAL **************** */
   1531 
   1532 /**
   1533  * Check that if the client specified a total,
   1534  * it matches our own calculation.
   1535  *
   1536  * @param[in,out] uc use context
   1537  */
   1538 static void
   1539 handle_phase_check_total (struct UseContext *uc)
   1540 {
   1541   GNUNET_assert (1 <= uc->compute_price.totals_len);
   1542   if (! uc->parse_request.no_amount)
   1543   {
   1544     GNUNET_assert (1 == uc->compute_price.totals_len);
   1545     GNUNET_assert (GNUNET_YES ==
   1546                    TALER_amount_cmp_currency (&uc->parse_request.amount,
   1547                                               &uc->compute_price.totals[0]));
   1548     if (0 !=
   1549         TALER_amount_cmp (&uc->parse_request.amount,
   1550                           &uc->compute_price.totals[0]))
   1551     {
   1552       GNUNET_break_op (0);
   1553       use_reply_with_error (uc,
   1554                             MHD_HTTP_CONFLICT,
   1555                             TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
   1556                             TALER_amount2s (&uc->compute_price.totals[0]));
   1557       return;
   1558     }
   1559   }
   1560   uc->phase++;
   1561 }
   1562 
   1563 
   1564 /* ***************** USE_PHASE_CREATE_ORDER **************** */
   1565 
   1566 
   1567 /**
   1568  * Create order request for inventory templates.
   1569  *
   1570  * @param[in,out] uc use context
   1571  */
   1572 static void
   1573 create_using_templates_inventory (struct UseContext *uc)
   1574 {
   1575   json_t *inventory_products;
   1576   json_t *choices;
   1577 
   1578   inventory_products = json_array ();
   1579   GNUNET_assert (NULL != inventory_products);
   1580   for (unsigned int i = 0;
   1581        i < uc->parse_request.inventory.items_len;
   1582        i++)
   1583   {
   1584     const struct InventoryTemplateItemContext *item =
   1585       &uc->parse_request.inventory.items[i];
   1586 
   1587     GNUNET_assert (0 ==
   1588                    json_array_append_new (
   1589                      inventory_products,
   1590                      GNUNET_JSON_PACK (
   1591                        GNUNET_JSON_pack_string ("product_id",
   1592                                                 item->product_id),
   1593                        GNUNET_JSON_pack_string ("unit_quantity",
   1594                                                 item->unit_quantity))));
   1595   }
   1596   choices = json_array ();
   1597   GNUNET_assert (NULL != choices);
   1598   for (unsigned int i = 0;
   1599        i < uc->compute_price.totals_len;
   1600        i++)
   1601   {
   1602     GNUNET_assert (0 ==
   1603                    json_array_append_new (
   1604                      choices,
   1605                      GNUNET_JSON_PACK (
   1606                        TALER_JSON_pack_amount ("amount",
   1607                                                &uc->compute_price.totals[i]),
   1608                        GNUNET_JSON_pack_allow_null (
   1609                          TALER_JSON_pack_amount ("tip",
   1610                                                  uc->parse_request.no_tip
   1611                                                 ? NULL
   1612                                                 : &uc->parse_request.tip))
   1613                        )));
   1614   }
   1615 
   1616   uc->ihc.request_body
   1617     = GNUNET_JSON_PACK (
   1618         GNUNET_JSON_pack_allow_null (
   1619           GNUNET_JSON_pack_string ("otp_id",
   1620                                    uc->lookup_template.etp.otp_id)),
   1621         GNUNET_JSON_pack_array_steal ("inventory_products",
   1622                                       inventory_products),
   1623         GNUNET_JSON_pack_object_steal (
   1624           "order",
   1625           GNUNET_JSON_PACK (
   1626             GNUNET_JSON_pack_uint64 ("version",
   1627                                      1),
   1628             GNUNET_JSON_pack_array_steal ("choices",
   1629                                           choices),
   1630             GNUNET_JSON_pack_string ("summary",
   1631                                      NULL == uc->parse_request.summary
   1632                                    ? uc->template_contract.summary
   1633                                    : uc->parse_request.summary))));
   1634 }
   1635 
   1636 
   1637 /**
   1638  * Create order request for fixed-order templates.
   1639  *
   1640  * @param[in,out] uc use context
   1641  */
   1642 static void
   1643 create_using_templates_fixed (struct UseContext *uc)
   1644 {
   1645   uc->ihc.request_body
   1646     = GNUNET_JSON_PACK (
   1647         GNUNET_JSON_pack_allow_null (
   1648           GNUNET_JSON_pack_string ("otp_id",
   1649                                    uc->lookup_template.etp.otp_id)),
   1650         GNUNET_JSON_pack_object_steal (
   1651           "order",
   1652           GNUNET_JSON_PACK (
   1653             TALER_JSON_pack_amount (
   1654               "amount",
   1655               &uc->compute_price.totals[0]),
   1656             GNUNET_JSON_pack_allow_null (
   1657               TALER_JSON_pack_amount ("tip",
   1658                                       uc->parse_request.no_tip
   1659                                       ? NULL
   1660                                       : &uc->parse_request.tip)),
   1661             GNUNET_JSON_pack_string (
   1662               "summary",
   1663               NULL == uc->parse_request.summary
   1664             ? uc->template_contract.summary
   1665             : uc->parse_request.summary))));
   1666 }
   1667 
   1668 
   1669 /**
   1670  * Create order request for paivana templates.
   1671  *
   1672  * @param[in,out] uc use context
   1673  */
   1674 static void
   1675 create_using_templates_paivana (struct UseContext *uc)
   1676 {
   1677   uc->ihc.request_body
   1678     = GNUNET_JSON_PACK (
   1679         GNUNET_JSON_pack_string (
   1680           "session_id",
   1681           uc->parse_request.paivana.paivana_id),
   1682         GNUNET_JSON_pack_object_steal (
   1683           "order",
   1684           GNUNET_JSON_PACK (
   1685             GNUNET_JSON_pack_uint64 ("version",
   1686                                      1),
   1687             GNUNET_JSON_pack_array_incref ("choices",
   1688                                            uc->compute_price.choices),
   1689             GNUNET_JSON_pack_string (
   1690               "summary",
   1691               NULL == uc->parse_request.summary
   1692               ? uc->template_contract.summary
   1693               : uc->parse_request.summary),
   1694             GNUNET_JSON_pack_string ("fulfillment_url",
   1695                                      uc->parse_request.paivana.website))));
   1696 }
   1697 
   1698 
   1699 static void
   1700 handle_phase_create_order (struct UseContext *uc)
   1701 {
   1702   GNUNET_assert (NULL == uc->ihc.request_body);
   1703   switch (uc->template_type)
   1704   {
   1705   case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
   1706     create_using_templates_fixed (uc);
   1707     break;
   1708   case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
   1709     create_using_templates_paivana (uc);
   1710     break;
   1711   case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
   1712     create_using_templates_inventory (uc);
   1713     break;
   1714   case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
   1715     GNUNET_assert (0);
   1716   }
   1717   uc->phase++;
   1718 }
   1719 
   1720 
   1721 /* ***************** Main handler **************** */
   1722 
   1723 enum MHD_Result
   1724 TMH_post_using_templates_ID (
   1725   const struct TMH_RequestHandler *rh,
   1726   struct MHD_Connection *connection,
   1727   struct TMH_HandlerContext *hc)
   1728 {
   1729   struct UseContext *uc = hc->ctx;
   1730 
   1731   (void) rh;
   1732   if (NULL == uc)
   1733   {
   1734     uc = GNUNET_new (struct UseContext);
   1735     uc->hc = hc;
   1736     hc->ctx = uc;
   1737     hc->cc = &cleanup_use_context;
   1738     uc->ihc.instance = hc->instance;
   1739     uc->phase = USE_PHASE_PARSE_REQUEST;
   1740     uc->template_type = TALER_MERCHANT_TEMPLATE_TYPE_INVALID;
   1741   }
   1742 
   1743   while (1)
   1744   {
   1745     switch (uc->phase)
   1746     {
   1747     case USE_PHASE_PARSE_REQUEST:
   1748       handle_phase_parse_request (uc);
   1749       break;
   1750     case USE_PHASE_LOOKUP_TEMPLATE:
   1751       handle_phase_lookup_template (uc);
   1752       break;
   1753     case USE_PHASE_PARSE_TEMPLATE:
   1754       handle_phase_template_contract (uc);
   1755       break;
   1756     case USE_PHASE_DB_FETCH:
   1757       handle_phase_db_fetch (uc);
   1758       break;
   1759     case USE_PHASE_VERIFY:
   1760       handle_phase_verify (uc);
   1761       break;
   1762     case USE_PHASE_COMPUTE_PRICE:
   1763       handle_phase_compute_price (uc);
   1764       break;
   1765     case USE_PHASE_CHECK_TIP:
   1766       handle_phase_check_tip (uc);
   1767       break;
   1768     case USE_PHASE_CHECK_TOTAL:
   1769       handle_phase_check_total (uc);
   1770       break;
   1771     case USE_PHASE_CREATE_ORDER:
   1772       handle_phase_create_order (uc);
   1773       break;
   1774     case USE_PHASE_SUBMIT_ORDER:
   1775       return TMH_private_post_orders (
   1776         NULL,    /* not even used */
   1777         connection,
   1778         &uc->ihc);
   1779     case USE_PHASE_FINISHED_MHD_YES:
   1780       return MHD_YES;
   1781     case USE_PHASE_FINISHED_MHD_NO:
   1782       return MHD_NO;
   1783     }
   1784   }
   1785 }