merchant

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

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


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