LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-post-orders.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 0 471 0.0 %
Date: 2022-08-25 06:17:04 Functions: 0 8 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (C) 2014, 2015, 2016, 2018, 2020, 2021 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_private-post-orders.c
      22             :  * @brief the POST /orders handler
      23             :  * @author Christian Grothoff
      24             :  * @author Marcello Stanisci
      25             :  */
      26             : #include "platform.h"
      27             : #include <jansson.h>
      28             : #include <taler/taler_signatures.h>
      29             : #include <taler/taler_json_lib.h>
      30             : #include "taler-merchant-httpd_private-post-orders.h"
      31             : #include "taler-merchant-httpd_auditors.h"
      32             : #include "taler-merchant-httpd_exchanges.h"
      33             : #include "taler-merchant-httpd_helper.h"
      34             : #include "taler-merchant-httpd_private-get-orders.h"
      35             : 
      36             : 
      37             : /**
      38             :  * How often do we retry the simple INSERT database transaction?
      39             :  */
      40             : #define MAX_RETRIES 3
      41             : 
      42             : /**
      43             :  * What is the label under which we find/place the merchant's
      44             :  * jurisdiction in the locations list by default?
      45             :  */
      46             : #define STANDARD_LABEL_MERCHANT_JURISDICTION "_mj"
      47             : 
      48             : /**
      49             :  * What is the label under which we find/place the merchant's
      50             :  * address in the locations list by default?
      51             :  */
      52             : #define STANDARD_LABEL_MERCHANT_ADDRESS "_ma"
      53             : 
      54             : 
      55             : /**
      56             :  * Check that the given JSON array of products is well-formed.
      57             :  *
      58             :  * @param products JSON array to check
      59             :  * @return #GNUNET_OK if all is fine
      60             :  */
      61             : static enum GNUNET_GenericReturnValue
      62           0 : check_products (const json_t *products)
      63             : {
      64             :   size_t index;
      65             :   json_t *value;
      66             : 
      67           0 :   if (! json_is_array (products))
      68             :   {
      69           0 :     GNUNET_break (0);
      70           0 :     return GNUNET_SYSERR;
      71             :   }
      72           0 :   json_array_foreach (products, index, value) {
      73             :     const char *description;
      74           0 :     const char *product_id = NULL;
      75             :     uint64_t quantity;
      76           0 :     const char *unit = NULL;
      77             :     struct TALER_Amount price;
      78           0 :     const char *image = NULL;
      79           0 :     json_t *taxes = NULL;
      80             :     struct GNUNET_TIME_Timestamp delivery_date;
      81             :     const char *error_name;
      82             :     unsigned int error_line;
      83             :     enum GNUNET_GenericReturnValue res;
      84             :     struct GNUNET_JSON_Specification spec[] = {
      85           0 :       GNUNET_JSON_spec_mark_optional (
      86             :         GNUNET_JSON_spec_string ("product_id",
      87             :                                  &product_id),
      88             :         NULL),
      89           0 :       TALER_JSON_spec_i18n_str ("description",
      90             :                                 &description),
      91           0 :       GNUNET_JSON_spec_mark_optional (
      92             :         GNUNET_JSON_spec_uint64 ("quantity",
      93             :                                  &quantity),
      94             :         NULL),
      95           0 :       GNUNET_JSON_spec_mark_optional (
      96             :         GNUNET_JSON_spec_string ("unit",
      97             :                                  &unit),
      98             :         NULL),
      99           0 :       GNUNET_JSON_spec_mark_optional (
     100             :         TALER_JSON_spec_amount ("price",
     101             :                                 TMH_currency,
     102             :                                 &price),
     103             :         NULL),
     104           0 :       GNUNET_JSON_spec_mark_optional (
     105             :         GNUNET_JSON_spec_string ("image",
     106             :                                  &image),
     107             :         NULL),
     108           0 :       GNUNET_JSON_spec_mark_optional (
     109             :         GNUNET_JSON_spec_json ("taxes",
     110             :                                &taxes),
     111             :         NULL),
     112           0 :       GNUNET_JSON_spec_mark_optional (
     113             :         GNUNET_JSON_spec_timestamp ("delivery_date",
     114             :                                     &delivery_date),
     115             :         NULL),
     116           0 :       GNUNET_JSON_spec_end ()
     117             :     };
     118             : 
     119             :     /* extract fields we need to sign separately */
     120           0 :     res = GNUNET_JSON_parse (value,
     121             :                              spec,
     122             :                              &error_name,
     123             :                              &error_line);
     124           0 :     if (GNUNET_OK != res)
     125             :     {
     126           0 :       GNUNET_break (0);
     127           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     128             :                   "Product parsing failed at #%u: %s:%u\n",
     129             :                   (unsigned int) index,
     130             :                   error_name,
     131             :                   error_line);
     132           0 :       return GNUNET_SYSERR;
     133             :     }
     134           0 :     if ( (NULL != taxes) &&
     135           0 :          (! TMH_taxes_array_valid (taxes) ) )
     136             :     {
     137           0 :       GNUNET_break (0);
     138           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     139             :                   "Product parsing failed for taxes\n");
     140           0 :       GNUNET_JSON_parse_free (spec);
     141           0 :       return GNUNET_SYSERR;
     142             :     }
     143           0 :     if ( (NULL != image) &&
     144           0 :          (! TMH_image_data_url_valid (image) ) )
     145             :     {
     146           0 :       GNUNET_break (0);
     147           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     148             :                   "Product parsing failed for image\n");
     149           0 :       GNUNET_JSON_parse_free (spec);
     150           0 :       return GNUNET_SYSERR;
     151             :     }
     152           0 :     GNUNET_JSON_parse_free (spec);
     153             :   }
     154           0 :   return GNUNET_OK;
     155             : }
     156             : 
     157             : 
     158             : /**
     159             :  * Generate the base URL for the given merchant instance.
     160             :  *
     161             :  * @param connection the MHD connection
     162             :  * @param instance_id the merchant instance ID
     163             :  * @returns the merchant instance's base URL
     164             :  */
     165             : static char *
     166           0 : make_merchant_base_url (struct MHD_Connection *connection,
     167             :                         const char *instance_id)
     168             : {
     169             :   const char *host;
     170             :   const char *forwarded_host;
     171             :   const char *uri_path;
     172           0 :   struct GNUNET_Buffer buf = { 0 };
     173             : 
     174           0 :   if (GNUNET_YES == TALER_mhd_is_https (connection))
     175           0 :     GNUNET_buffer_write_str (&buf, "https://");
     176             :   else
     177           0 :     GNUNET_buffer_write_str (&buf, "http://");
     178           0 :   host = MHD_lookup_connection_value (connection,
     179             :                                       MHD_HEADER_KIND,
     180             :                                       MHD_HTTP_HEADER_HOST);
     181           0 :   forwarded_host = MHD_lookup_connection_value (connection,
     182             :                                                 MHD_HEADER_KIND,
     183             :                                                 "X-Forwarded-Host");
     184           0 :   if (NULL != forwarded_host)
     185             :   {
     186           0 :     GNUNET_buffer_write_str (&buf,
     187             :                              forwarded_host);
     188             :   }
     189             :   else
     190             :   {
     191           0 :     GNUNET_assert (NULL != host);
     192           0 :     GNUNET_buffer_write_str (&buf,
     193             :                              host);
     194             :   }
     195           0 :   uri_path = MHD_lookup_connection_value (connection,
     196             :                                           MHD_HEADER_KIND,
     197             :                                           "X-Forwarded-Prefix");
     198           0 :   if (NULL != uri_path)
     199           0 :     GNUNET_buffer_write_path (&buf, uri_path);
     200             : 
     201           0 :   if (0 != strcmp (instance_id,
     202             :                    "default"))
     203             :   {
     204           0 :     GNUNET_buffer_write_path (&buf,
     205             :                               "/instances/");
     206           0 :     GNUNET_buffer_write_str (&buf,
     207             :                              instance_id);
     208             :   }
     209           0 :   GNUNET_buffer_write_path (&buf,
     210             :                             "");
     211           0 :   return GNUNET_buffer_reap_str (&buf);
     212             : }
     213             : 
     214             : 
     215             : /**
     216             :  * Information about a product we are supposed to add to the order
     217             :  * based on what we know it from our inventory.
     218             :  */
     219             : struct InventoryProduct
     220             : {
     221             :   /**
     222             :    * Identifier of the product in the inventory.
     223             :    */
     224             :   const char *product_id;
     225             : 
     226             :   /**
     227             :    * Number of units of the product to add to the order.
     228             :    */
     229             :   uint32_t quantity;
     230             : };
     231             : 
     232             : 
     233             : /**
     234             :  * Execute the database transaction to setup the order.
     235             :  *
     236             :  * @param hc handler context for the request
     237             :  * @param order_id unique ID for the order
     238             :  * @param h_post_data hash of the client's POST request, for idempotency checks
     239             :  * @param pay_deadline until when does the order have to be paid
     240             :  * @param[in] order order to process (not modified)
     241             :  * @param claim_token token to use for access control
     242             :  * @param inventory_products_length length of the @a inventory_products array
     243             :  * @param inventory_products array of products to add to @a order from our inventory
     244             :  * @param uuids_length length of the @a uuids array
     245             :  * @param uuids array of UUIDs used to reserve products from @a inventory_products
     246             :  * @param[out] out_of_stock_index which product (by offset) is out of stock, UINT_MAX if all were in-stock
     247             :  * @return transaction status, 0 if @a uuids were insufficient to reserve required inventory
     248             :  */
     249             : static enum GNUNET_DB_QueryStatus
     250           0 : execute_transaction (struct TMH_HandlerContext *hc,
     251             :                      const char *order_id,
     252             :                      const struct TALER_MerchantPostDataHashP *h_post_data,
     253             :                      struct GNUNET_TIME_Timestamp pay_deadline,
     254             :                      const json_t *order,
     255             :                      const struct TALER_ClaimTokenP *claim_token,
     256             :                      unsigned int inventory_products_length,
     257             :                      const struct InventoryProduct inventory_products[],
     258             :                      unsigned int uuids_length,
     259             :                      const struct GNUNET_Uuid uuids[],
     260             :                      unsigned int *out_of_stock_index)
     261             : {
     262             :   enum GNUNET_DB_QueryStatus qs;
     263             :   struct GNUNET_TIME_Timestamp timestamp;
     264             :   uint64_t order_serial;
     265             : 
     266           0 :   if (GNUNET_OK !=
     267           0 :       TMH_db->start (TMH_db->cls,
     268             :                      "insert_order"))
     269             :   {
     270           0 :     GNUNET_break (0);
     271           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     272             :   }
     273             :   /* Setup order */
     274           0 :   qs = TMH_db->insert_order (TMH_db->cls,
     275           0 :                              hc->instance->settings.id,
     276             :                              order_id,
     277             :                              h_post_data,
     278             :                              pay_deadline,
     279             :                              claim_token,
     280             :                              order); // called 'contract terms' at database.
     281           0 :   if (qs <= 0)
     282             :   {
     283             :     /* qs == 0: probably instance does not exist (anymore) */
     284           0 :     TMH_db->rollback (TMH_db->cls);
     285           0 :     return qs;
     286             :   }
     287             :   /* Migrate locks from UUIDs to new order: first release old locks */
     288           0 :   for (unsigned int i = 0; i<uuids_length; i++)
     289             :   {
     290           0 :     qs = TMH_db->unlock_inventory (TMH_db->cls,
     291           0 :                                    &uuids[i]);
     292           0 :     if (qs < 0)
     293             :     {
     294           0 :       TMH_db->rollback (TMH_db->cls);
     295           0 :       return qs;
     296             :     }
     297             :     /* qs == 0 is OK here, that just means we did not HAVE any lock under this
     298             :        UUID */
     299             :   }
     300             :   /* Migrate locks from UUIDs to new order: acquire new locks
     301             :      (note: this can basically ONLY fail on serializability OR
     302             :      because the UUID locks were insufficient for the desired
     303             :      quantities). */
     304           0 :   for (unsigned int i = 0; i<inventory_products_length; i++)
     305             :   {
     306           0 :     qs = TMH_db->insert_order_lock (TMH_db->cls,
     307           0 :                                     hc->instance->settings.id,
     308             :                                     order_id,
     309           0 :                                     inventory_products[i].product_id,
     310           0 :                                     inventory_products[i].quantity);
     311           0 :     if (qs < 0)
     312             :     {
     313           0 :       TMH_db->rollback (TMH_db->cls);
     314           0 :       return qs;
     315             :     }
     316           0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     317             :     {
     318             :       /* qs == 0: lock acquisition failed due to insufficient stocks */
     319           0 :       TMH_db->rollback (TMH_db->cls);
     320           0 :       *out_of_stock_index = i; /* indicate which product is causing the issue */
     321           0 :       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     322             :     }
     323             :   }
     324           0 :   *out_of_stock_index = UINT_MAX;
     325             : 
     326             :   /* Get the order serial and timestamp for the order we just created to
     327             :      update long-poll clients. */
     328           0 :   qs = TMH_db->lookup_order_summary (TMH_db->cls,
     329           0 :                                      hc->instance->settings.id,
     330             :                                      order_id,
     331             :                                      &timestamp,
     332             :                                      &order_serial);
     333           0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     334             :   {
     335           0 :     TMH_db->rollback (TMH_db->cls);
     336           0 :     return qs;
     337             :   }
     338           0 :   TMH_notify_order_change (hc->instance,
     339             :                            TMH_OSF_NONE,
     340             :                            timestamp,
     341             :                            order_serial);
     342             :   /* finally, commit transaction (note: if it fails, we ALSO re-acquire
     343             :      the UUID locks, which is exactly what we want) */
     344           0 :   qs = TMH_db->commit (TMH_db->cls);
     345           0 :   if (0 > qs)
     346           0 :     return qs;
     347           0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;   /* 1 == success! */
     348             : }
     349             : 
     350             : 
     351             : /**
     352             :  * Transform an order into a proposal and store it in the
     353             :  * database. Write the resulting proposal or an error message
     354             :  * of a MHD connection.
     355             :  *
     356             :  * @param connection connection to write the result or error to
     357             :  * @param hc handler context for the request
     358             :  * @param h_post_data hash of the client's POST request, for idempotency checks
     359             :  * @param[in,out] order order to process (can be modified)
     360             :  * @param claim_token token to use for access control
     361             :  * @param inventory_products_length length of the @a inventory_products array
     362             :  * @param inventory_products array of products to add to @a order from our inventory
     363             :  * @param uuids_length length of the @a uuids array
     364             :  * @param uuids array of UUIDs used to reserve products from @a inventory_products
     365             :  * @return MHD result code
     366             :  */
     367             : static MHD_RESULT
     368           0 : execute_order (struct MHD_Connection *connection,
     369             :                struct TMH_HandlerContext *hc,
     370             :                const struct TALER_MerchantPostDataHashP *h_post_data,
     371             :                json_t *order,
     372             :                const struct TALER_ClaimTokenP *claim_token,
     373             :                unsigned int inventory_products_length,
     374             :                const struct InventoryProduct inventory_products[],
     375             :                unsigned int uuids_length,
     376             :                const struct GNUNET_Uuid uuids[])
     377             : {
     378           0 :   const struct TALER_MERCHANTDB_InstanceSettings *settings =
     379           0 :     &hc->instance->settings;
     380             :   struct TALER_Amount total;
     381             :   const char *order_id;
     382             :   const char *summary;
     383           0 :   const char *fulfillment_msg = NULL;
     384             :   json_t *products;
     385             :   json_t *merchant;
     386           0 :   json_t *summary_i18n = NULL;
     387           0 :   json_t *fulfillment_i18n = NULL;
     388             :   struct GNUNET_TIME_Timestamp timestamp;
     389           0 :   struct GNUNET_TIME_Timestamp refund_deadline = { 0 };
     390             :   struct GNUNET_TIME_Timestamp wire_transfer_deadline;
     391             :   struct GNUNET_TIME_Timestamp pay_deadline;
     392             :   struct GNUNET_JSON_Specification spec[] = {
     393           0 :     TALER_JSON_spec_amount ("amount",
     394             :                             TMH_currency,
     395             :                             &total),
     396           0 :     GNUNET_JSON_spec_string ("order_id",
     397             :                              &order_id),
     398           0 :     GNUNET_JSON_spec_string ("summary",
     399             :                              &summary),
     400             :     /**
     401             :      * The following entries we don't actually need,
     402             :      * except to check that the order is well-formed */
     403           0 :     GNUNET_JSON_spec_json ("products",
     404             :                            &products),
     405           0 :     GNUNET_JSON_spec_json ("merchant",
     406             :                            &merchant),
     407           0 :     GNUNET_JSON_spec_mark_optional (
     408             :       GNUNET_JSON_spec_json ("summary_i18n",
     409             :                              &summary_i18n),
     410             :       NULL),
     411           0 :     GNUNET_JSON_spec_mark_optional (
     412             :       GNUNET_JSON_spec_string ("fulfillment_message",
     413             :                                &fulfillment_msg),
     414             :       NULL),
     415           0 :     GNUNET_JSON_spec_mark_optional (
     416             :       GNUNET_JSON_spec_json ("fulfillment_message_i18n",
     417             :                              &fulfillment_i18n),
     418             :       NULL),
     419           0 :     GNUNET_JSON_spec_timestamp ("timestamp",
     420             :                                 &timestamp),
     421           0 :     GNUNET_JSON_spec_mark_optional (
     422             :       GNUNET_JSON_spec_timestamp ("refund_deadline",
     423             :                                   &refund_deadline),
     424             :       NULL),
     425           0 :     GNUNET_JSON_spec_timestamp ("pay_deadline",
     426             :                                 &pay_deadline),
     427           0 :     GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
     428             :                                 &wire_transfer_deadline),
     429           0 :     GNUNET_JSON_spec_end ()
     430             :   };
     431             :   enum GNUNET_DB_QueryStatus qs;
     432             :   unsigned int out_of_stock_index;
     433             : 
     434             :   /* extract fields we need to sign separately */
     435             :   {
     436             :     enum GNUNET_GenericReturnValue res;
     437             : 
     438           0 :     res = TALER_MHD_parse_json_data (connection,
     439             :                                      order,
     440             :                                      spec);
     441           0 :     if (GNUNET_OK != res)
     442             :     {
     443           0 :       GNUNET_break_op (0);
     444             :       return (GNUNET_NO == res)
     445             :              ? MHD_YES
     446           0 :              : MHD_NO;
     447             :     }
     448             :   }
     449             : 
     450             :   /* check product list in contract is well-formed */
     451           0 :   if (GNUNET_OK != check_products (products))
     452             :   {
     453           0 :     GNUNET_JSON_parse_free (spec);
     454           0 :     return TALER_MHD_reply_with_error (connection,
     455             :                                        MHD_HTTP_BAD_REQUEST,
     456             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     457             :                                        "order:products");
     458             :   }
     459             : 
     460           0 :   if ( (NULL != fulfillment_i18n) &&
     461           0 :        (! TALER_JSON_check_i18n (fulfillment_i18n)) )
     462             :   {
     463           0 :     GNUNET_JSON_parse_free (spec);
     464           0 :     return TALER_MHD_reply_with_error (connection,
     465             :                                        MHD_HTTP_BAD_REQUEST,
     466             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     467             :                                        "order:fulfillment_message_i18n");
     468             :   }
     469           0 :   if ( (NULL != summary_i18n) &&
     470           0 :        (! TALER_JSON_check_i18n (summary_i18n)) )
     471             :   {
     472           0 :     GNUNET_JSON_parse_free (spec);
     473           0 :     return TALER_MHD_reply_with_error (connection,
     474             :                                        MHD_HTTP_BAD_REQUEST,
     475             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     476             :                                        "order:summary_i18n");
     477             :   }
     478             : 
     479             :   /* Test if we already have an order with this id */
     480             :   {
     481             :     struct TALER_ClaimTokenP token;
     482             :     json_t *contract_terms;
     483             :     struct TALER_MerchantPostDataHashP orig_post;
     484             : 
     485           0 :     TMH_db->preflight (TMH_db->cls);
     486           0 :     qs = TMH_db->lookup_order (TMH_db->cls,
     487           0 :                                hc->instance->settings.id,
     488             :                                order_id,
     489             :                                &token,
     490             :                                &orig_post,
     491             :                                &contract_terms);
     492             :     /* If yes, check for idempotency */
     493           0 :     if (0 > qs)
     494             :     {
     495           0 :       GNUNET_break (0);
     496           0 :       TMH_db->rollback (TMH_db->cls);
     497           0 :       GNUNET_JSON_parse_free (spec);
     498           0 :       return TALER_MHD_reply_with_error (connection,
     499             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     500             :                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
     501             :                                          "lookup_order");
     502             :     }
     503           0 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     504             :     {
     505             :       MHD_RESULT ret;
     506             : 
     507           0 :       json_decref (contract_terms);
     508             :       /* Comparing the contract terms is sufficient because all the other
     509             :          params get added to it at some point. */
     510           0 :       if (0 == GNUNET_memcmp (&orig_post,
     511             :                               h_post_data))
     512             :       {
     513           0 :         ret = TALER_MHD_REPLY_JSON_PACK (
     514             :           connection,
     515             :           MHD_HTTP_OK,
     516             :           GNUNET_JSON_pack_string ("order_id",
     517             :                                    order_id),
     518             :           GNUNET_JSON_pack_allow_null (
     519             :             GNUNET_JSON_pack_data_varsize (
     520             :               "token",
     521             :               GNUNET_is_zero (&token)
     522             :               ? NULL
     523             :               : &token,
     524             :               sizeof (token))));
     525             :       }
     526             :       else
     527             :       {
     528             :         /* This request is not idempotent */
     529           0 :         ret = TALER_MHD_reply_with_error (
     530             :           connection,
     531             :           MHD_HTTP_CONFLICT,
     532             :           TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS,
     533             :           order_id);
     534             :       }
     535           0 :       GNUNET_JSON_parse_free (spec);
     536           0 :       return ret;
     537             :     }
     538             :   }
     539           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     540             :               "Executing database transaction to create order '%s' for instance '%s'\n",
     541             :               order_id,
     542             :               settings->id);
     543           0 :   for (unsigned int i = 0; i<MAX_RETRIES; i++)
     544             :   {
     545           0 :     TMH_db->preflight (TMH_db->cls);
     546           0 :     qs = execute_transaction (hc,
     547             :                               order_id,
     548             :                               h_post_data,
     549             :                               pay_deadline,
     550             :                               order,
     551             :                               claim_token,
     552             :                               inventory_products_length,
     553             :                               inventory_products,
     554             :                               uuids_length,
     555             :                               uuids,
     556             :                               &out_of_stock_index);
     557           0 :     if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
     558           0 :       break;
     559             :   }
     560           0 :   if (0 >= qs)
     561             :   {
     562           0 :     GNUNET_JSON_parse_free (spec);
     563             :     /* Special report if retries insufficient */
     564           0 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     565             :     {
     566           0 :       GNUNET_break (0);
     567           0 :       return TALER_MHD_reply_with_error (connection,
     568             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     569             :                                          TALER_EC_GENERIC_DB_SOFT_FAILURE,
     570             :                                          NULL);
     571             :     }
     572           0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     573             :     {
     574             :       /* should be: contract (!) with same order ID
     575             :          already exists */
     576           0 :       return TALER_MHD_reply_with_error (
     577             :         connection,
     578             :         MHD_HTTP_CONFLICT,
     579             :         TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS,
     580             :         order_id);
     581             :     }
     582             :     /* Other hard transaction error (disk full, etc.) */
     583           0 :     GNUNET_break (0);
     584           0 :     return TALER_MHD_reply_with_error (
     585             :       connection,
     586             :       MHD_HTTP_INTERNAL_SERVER_ERROR,
     587             :       TALER_EC_GENERIC_DB_COMMIT_FAILED,
     588             :       NULL);
     589             :   }
     590             : 
     591             :   /* DB transaction succeeded, check for out-of-stock */
     592           0 :   if (out_of_stock_index < UINT_MAX)
     593             :   {
     594             :     /* We had a product that has insufficient quantities,
     595             :        generate the details for the response. */
     596             :     struct TALER_MERCHANTDB_ProductDetails pd;
     597             :     MHD_RESULT ret;
     598             : 
     599           0 :     memset (&pd, 0, sizeof (pd));
     600           0 :     qs = TMH_db->lookup_product (
     601           0 :       TMH_db->cls,
     602           0 :       hc->instance->settings.id,
     603           0 :       inventory_products[out_of_stock_index].product_id,
     604             :       &pd);
     605           0 :     GNUNET_JSON_parse_free (spec);
     606           0 :     switch (qs)
     607             :     {
     608           0 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     609           0 :       ret = TALER_MHD_REPLY_JSON_PACK (
     610             :         connection,
     611             :         MHD_HTTP_GONE,
     612             :         GNUNET_JSON_pack_string (
     613             :           "product_id",
     614             :           inventory_products[out_of_stock_index].product_id),
     615             :         GNUNET_JSON_pack_uint64 (
     616             :           "requested_quantity",
     617             :           inventory_products[out_of_stock_index].quantity),
     618             :         GNUNET_JSON_pack_uint64 (
     619             :           "available_quantity",
     620             :           pd.total_stock - pd.total_sold - pd.total_lost),
     621             :         GNUNET_JSON_pack_allow_null (
     622             :           GNUNET_JSON_pack_timestamp (
     623             :             "restock_expected",
     624             :             pd.next_restock)));
     625           0 :       TALER_MERCHANTDB_product_details_free (&pd);
     626           0 :       return ret;
     627           0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     628           0 :       return TALER_MHD_REPLY_JSON_PACK (
     629             :         connection,
     630             :         MHD_HTTP_GONE,
     631             :         GNUNET_JSON_pack_string (
     632             :           "product_id",
     633             :           inventory_products[out_of_stock_index].product_id),
     634             :         GNUNET_JSON_pack_uint64 (
     635             :           "requested_quantity",
     636             :           inventory_products[out_of_stock_index].quantity),
     637             :         GNUNET_JSON_pack_uint64 (
     638             :           "available_quantity",
     639             :           0));
     640           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
     641           0 :       GNUNET_break (0);
     642           0 :       return TALER_MHD_reply_with_error (
     643             :         connection,
     644             :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     645             :         TALER_EC_GENERIC_DB_SOFT_FAILURE,
     646             :         NULL);
     647           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
     648           0 :       return TALER_MHD_reply_with_error (
     649             :         connection,
     650             :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     651             :         TALER_EC_GENERIC_DB_FETCH_FAILED,
     652             :         NULL);
     653             :     }
     654           0 :     GNUNET_break (0);
     655           0 :     return MHD_NO;
     656             :   }
     657             : 
     658             :   /* Everything in-stock, generate positive response */
     659             :   {
     660             :     MHD_RESULT ret;
     661             : 
     662           0 :     ret = TALER_MHD_REPLY_JSON_PACK (
     663             :       connection,
     664             :       MHD_HTTP_OK,
     665             :       GNUNET_JSON_pack_string ("order_id",
     666             :                                order_id),
     667             :       GNUNET_JSON_pack_allow_null (
     668             :         GNUNET_JSON_pack_data_varsize (
     669             :           "token",
     670             :           GNUNET_is_zero (claim_token)
     671             :           ? NULL
     672             :           : claim_token,
     673             :           sizeof (*claim_token))));
     674           0 :     GNUNET_JSON_parse_free (spec);
     675           0 :     return ret;
     676             :   }
     677             : }
     678             : 
     679             : 
     680             : /**
     681             :  * Add missing fields to the order.  Upon success, continue
     682             :  * processing with execute_order().
     683             :  *
     684             :  * @param connection connection to write the result or error to
     685             :  * @param hc handler context for the request
     686             :  * @param h_post_data hash of the client's POST request, for idempotency checks
     687             :  * @param[in,out] order order to process (can be modified)
     688             :  * @param claim_token token to use for access control
     689             :  * @param refund_delay refund delay
     690             :  * @param inventory_products_length length of the @a inventory_products array
     691             :  * @param inventory_products array of products to add to @a order from our inventory
     692             :  * @param uuids_length length of the @a uuids array
     693             :  * @param uuids array of UUIDs used to reserve products from @a inventory_products
     694             :  * @return MHD result code
     695             :  */
     696             : static MHD_RESULT
     697           0 : patch_order (struct MHD_Connection *connection,
     698             :              struct TMH_HandlerContext *hc,
     699             :              const struct TALER_MerchantPostDataHashP *h_post_data,
     700             :              json_t *order,
     701             :              const struct TALER_ClaimTokenP *claim_token,
     702             :              struct GNUNET_TIME_Relative refund_delay,
     703             :              unsigned int inventory_products_length,
     704             :              const struct InventoryProduct inventory_products[],
     705             :              unsigned int uuids_length,
     706             :              const struct GNUNET_Uuid uuids[])
     707             : {
     708           0 :   const struct TALER_MERCHANTDB_InstanceSettings *settings =
     709           0 :     &hc->instance->settings;
     710           0 :   const char *order_id = NULL;
     711           0 :   const char *fulfillment_url = NULL;
     712           0 :   const char *merchant_base_url = NULL;
     713           0 :   json_t *jmerchant = NULL;
     714           0 :   json_t *delivery_location = NULL;
     715           0 :   struct TALER_Amount max_wire_fee = { 0 };
     716           0 :   struct TALER_Amount max_fee = { 0 };
     717           0 :   uint32_t wire_fee_amortization = 0;
     718           0 :   struct GNUNET_TIME_Timestamp timestamp
     719             :     = GNUNET_TIME_UNIT_ZERO_TS;
     720           0 :   struct GNUNET_TIME_Timestamp delivery_date
     721             :     = GNUNET_TIME_UNIT_ZERO_TS;
     722           0 :   struct GNUNET_TIME_Timestamp refund_deadline
     723             :     = GNUNET_TIME_UNIT_FOREVER_TS;
     724           0 :   struct GNUNET_TIME_Timestamp pay_deadline
     725             :     = GNUNET_TIME_UNIT_ZERO_TS;
     726           0 :   struct GNUNET_TIME_Timestamp wire_deadline
     727             :     = GNUNET_TIME_UNIT_FOREVER_TS;
     728             :   /* auto_refund only needs to be type-checked,
     729             :    * mostly because in GNUnet relative times can't
     730             :    * be negative.  */
     731             :   struct GNUNET_TIME_Relative auto_refund;
     732             :   struct GNUNET_JSON_Specification spec[] = {
     733           0 :     GNUNET_JSON_spec_mark_optional (
     734             :       GNUNET_JSON_spec_string ("merchant_base_url",
     735             :                                &merchant_base_url),
     736             :       NULL),
     737           0 :     GNUNET_JSON_spec_mark_optional (
     738             :       GNUNET_JSON_spec_json ("merchant",
     739             :                              &jmerchant),
     740             :       NULL),
     741           0 :     GNUNET_JSON_spec_mark_optional (
     742             :       GNUNET_JSON_spec_string ("order_id",
     743             :                                &order_id),
     744             :       NULL),
     745           0 :     GNUNET_JSON_spec_mark_optional (
     746             :       GNUNET_JSON_spec_string ("fulfillment_url",
     747             :                                &fulfillment_url),
     748             :       NULL),
     749           0 :     GNUNET_JSON_spec_mark_optional (
     750             :       GNUNET_JSON_spec_timestamp ("timestamp",
     751             :                                   &timestamp),
     752             :       NULL),
     753           0 :     GNUNET_JSON_spec_mark_optional (
     754             :       GNUNET_JSON_spec_timestamp ("refund_deadline",
     755             :                                   &refund_deadline),
     756             :       NULL),
     757           0 :     GNUNET_JSON_spec_mark_optional (
     758             :       GNUNET_JSON_spec_timestamp ("pay_deadline",
     759             :                                   &pay_deadline),
     760             :       NULL),
     761           0 :     GNUNET_JSON_spec_mark_optional (
     762             :       GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
     763             :                                   &wire_deadline),
     764             :       NULL),
     765           0 :     GNUNET_JSON_spec_mark_optional (
     766             :       TALER_JSON_spec_amount ("max_fee",
     767             :                               TMH_currency,
     768             :                               &max_fee),
     769             :       NULL),
     770           0 :     GNUNET_JSON_spec_mark_optional (
     771             :       TALER_JSON_spec_amount ("max_wire_fee",
     772             :                               TMH_currency,
     773             :                               &max_wire_fee),
     774             :       NULL),
     775           0 :     GNUNET_JSON_spec_mark_optional (
     776             :       GNUNET_JSON_spec_uint32 ("wire_fee_amortization",
     777             :                                &wire_fee_amortization),
     778             :       NULL),
     779           0 :     GNUNET_JSON_spec_mark_optional (
     780             :       GNUNET_JSON_spec_timestamp ("delivery_date",
     781             :                                   &delivery_date),
     782             :       NULL),
     783           0 :     GNUNET_JSON_spec_mark_optional (
     784             :       GNUNET_JSON_spec_relative_time ("auto_refund",
     785             :                                       &auto_refund),
     786             :       NULL),
     787           0 :     GNUNET_JSON_spec_mark_optional (
     788             :       GNUNET_JSON_spec_json ("delivery_location",
     789             :                              &delivery_location),
     790             :       NULL),
     791           0 :     GNUNET_JSON_spec_end ()
     792             :   };
     793             :   enum GNUNET_GenericReturnValue ret;
     794             : 
     795           0 :   ret = TALER_MHD_parse_json_data (connection,
     796             :                                    order,
     797             :                                    spec);
     798           0 :   if (GNUNET_OK != ret)
     799             :   {
     800           0 :     GNUNET_break_op (0);
     801             :     return (GNUNET_NO == ret)
     802             :            ? MHD_YES
     803           0 :            : MHD_NO;
     804             :   }
     805             : 
     806             :   /* Add order_id if it doesn't exist. */
     807           0 :   if (NULL == order_id)
     808             :   {
     809             :     char buf[256];
     810             :     time_t timer;
     811             :     struct tm *tm_info;
     812             :     size_t off;
     813             :     uint64_t rand;
     814             :     char *last;
     815             :     json_t *jbuf;
     816             : 
     817           0 :     time (&timer);
     818           0 :     tm_info = localtime (&timer);
     819           0 :     if (NULL == tm_info)
     820             :     {
     821           0 :       GNUNET_JSON_parse_free (spec);
     822           0 :       return TALER_MHD_reply_with_error (
     823             :         connection,
     824             :         MHD_HTTP_INTERNAL_SERVER_ERROR,
     825             :         TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_LOCALTIME,
     826             :         NULL);
     827             :     }
     828           0 :     off = strftime (buf,
     829             :                     sizeof (buf) - 1,
     830             :                     "%Y.%j",
     831             :                     tm_info);
     832             :     /* Check for error state of strftime */
     833           0 :     GNUNET_assert (0 != off);
     834           0 :     buf[off++] = '-';
     835           0 :     rand = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
     836             :                                      UINT64_MAX);
     837           0 :     last = GNUNET_STRINGS_data_to_string (&rand,
     838             :                                           sizeof (uint64_t),
     839             :                                           &buf[off],
     840             :                                           sizeof (buf) - off);
     841           0 :     GNUNET_assert (NULL != last);
     842           0 :     *last = '\0';
     843           0 :     jbuf = json_string (buf);
     844           0 :     GNUNET_assert (NULL != jbuf);
     845           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     846             :                 "Assigning order ID `%s' server-side\n",
     847             :                 buf);
     848           0 :     GNUNET_break (0 ==
     849             :                   json_object_set_new (order,
     850             :                                        "order_id",
     851             :                                        jbuf));
     852           0 :     order_id = json_string_value (jbuf);
     853           0 :     GNUNET_assert (NULL != order_id);
     854             :   }
     855             : 
     856             :   /* Patch fulfillment URL with order_id (implements #6467). */
     857           0 :   if (NULL != fulfillment_url)
     858             :   {
     859             :     const char *pos;
     860             : 
     861           0 :     pos = strstr (fulfillment_url,
     862             :                   "${ORDER_ID}");
     863           0 :     if (NULL != pos)
     864             :     {
     865             :       /* replace ${ORDER_ID} with the real order_id */
     866             :       char *nurl;
     867             : 
     868             :       /* We only allow one placeholder */
     869           0 :       if (strstr (pos + strlen ("${ORDER_ID}"),
     870             :                   "${ORDER_ID}"))
     871             :       {
     872           0 :         GNUNET_break_op (0);
     873           0 :         GNUNET_JSON_parse_free (spec);
     874           0 :         return TALER_MHD_reply_with_error (connection,
     875             :                                            MHD_HTTP_BAD_REQUEST,
     876             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     877             :                                            "fulfillment_url");
     878             :       }
     879             : 
     880           0 :       GNUNET_asprintf (&nurl,
     881             :                        "%.*s%s%s",
     882             :                        /* first output URL until ${ORDER_ID} */
     883           0 :                        (int) (pos - fulfillment_url),
     884             :                        fulfillment_url,
     885             :                        /* replace ${ORDER_ID} with the right order_id */
     886             :                        order_id,
     887             :                        /* append rest of original URL */
     888             :                        pos + strlen ("${ORDER_ID}"));
     889             :       /* replace in JSON of the order */
     890           0 :       GNUNET_break (0 ==
     891             :                     json_object_set_new (order,
     892             :                                          "fulfillment_url",
     893             :                                          json_string (nurl)));
     894           0 :       GNUNET_free (nurl);
     895             :     }
     896             :   }
     897             : 
     898             :   /* Check soundness of refund deadline, and that a timestamp
     899             :    * is actually present.  */
     900             :   {
     901           0 :     struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
     902             : 
     903             :     /* Add timestamp if it doesn't exist (or is zero) */
     904           0 :     if (GNUNET_TIME_absolute_is_zero (timestamp.abs_time))
     905             :     {
     906           0 :       GNUNET_assert (0 ==
     907             :                      json_object_set_new (order,
     908             :                                           "timestamp",
     909             :                                           GNUNET_JSON_from_timestamp (now)));
     910             :     }
     911             : 
     912             :     /* If no refund_deadline given, set one based on refund_delay.  */
     913           0 :     if (GNUNET_TIME_absolute_is_never (refund_deadline.abs_time))
     914             :     {
     915           0 :       if (GNUNET_TIME_relative_is_zero (refund_delay))
     916             :       {
     917           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     918             :                     "Refund delay is zero, no refunds are possible for this order\n");
     919           0 :         refund_deadline = now; /* if delay was 0, ensure that refund_deadline == timestamp */
     920             :       }
     921             :       else
     922             :       {
     923           0 :         refund_deadline = GNUNET_TIME_relative_to_timestamp (refund_delay);
     924             :       }
     925             : 
     926           0 :       GNUNET_assert (0 ==
     927             :                      json_object_set_new (order,
     928             :                                           "refund_deadline",
     929             :                                           GNUNET_JSON_from_timestamp (
     930             :                                             refund_deadline)));
     931             :     }
     932           0 :     if ( (! GNUNET_TIME_absolute_is_zero (delivery_date.abs_time)) &&
     933           0 :          (GNUNET_TIME_timestamp_cmp (delivery_date,
     934             :                                      <,
     935             :                                      now)) )
     936             :     {
     937           0 :       GNUNET_break_op (0);
     938           0 :       GNUNET_JSON_parse_free (spec);
     939           0 :       return TALER_MHD_reply_with_error (
     940             :         connection,
     941             :         MHD_HTTP_BAD_REQUEST,
     942             :         TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_DELIVERY_DATE_IN_PAST,
     943             :         NULL);
     944             :     }
     945             :   }
     946             : 
     947           0 :   if (GNUNET_TIME_absolute_is_zero (pay_deadline.abs_time))
     948             :   {
     949             :     struct GNUNET_TIME_Timestamp t;
     950             : 
     951           0 :     t = GNUNET_TIME_relative_to_timestamp (settings->default_pay_delay);
     952           0 :     GNUNET_assert (0 ==
     953             :                    json_object_set_new (order,
     954             :                                         "pay_deadline",
     955             :                                         GNUNET_JSON_from_timestamp (t)));
     956             :   }
     957             : 
     958           0 :   if (GNUNET_TIME_absolute_is_never (wire_deadline.abs_time))
     959             :   {
     960             :     struct GNUNET_TIME_Timestamp t;
     961             : 
     962           0 :     t = GNUNET_TIME_relative_to_timestamp (
     963             :       GNUNET_TIME_relative_max (settings->default_wire_transfer_delay,
     964             :                                 refund_delay));
     965           0 :     wire_deadline = GNUNET_TIME_timestamp_max (refund_deadline,
     966             :                                                t);
     967           0 :     if (GNUNET_TIME_absolute_is_never (wire_deadline.abs_time))
     968             :     {
     969           0 :       GNUNET_break_op (0);
     970           0 :       GNUNET_JSON_parse_free (spec);
     971           0 :       return TALER_MHD_reply_with_error (
     972             :         connection,
     973             :         MHD_HTTP_BAD_REQUEST,
     974             :         TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_WIRE_DEADLINE_IS_NEVER,
     975             :         "order:wire_transfer_deadline");
     976             : 
     977             :     }
     978           0 :     GNUNET_assert (0 ==
     979             :                    json_object_set_new (order,
     980             :                                         "wire_transfer_deadline",
     981             :                                         GNUNET_JSON_from_timestamp (
     982             :                                           wire_deadline)));
     983             :   }
     984           0 :   if (GNUNET_TIME_timestamp_cmp (wire_deadline,
     985             :                                  <,
     986             :                                  refund_deadline))
     987             :   {
     988           0 :     GNUNET_break_op (0);
     989           0 :     GNUNET_JSON_parse_free (spec);
     990           0 :     return TALER_MHD_reply_with_error (
     991             :       connection,
     992             :       MHD_HTTP_BAD_REQUEST,
     993             :       TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_AFTER_WIRE_DEADLINE,
     994             :       "order:wire_transfer_deadline;order:refund_deadline");
     995             :   }
     996             : 
     997             :   /* Note: total amount currency match checked
     998             :      later in execute_order() */
     999           0 :   if (GNUNET_OK !=
    1000           0 :       TALER_amount_is_valid (&max_wire_fee))
    1001             :   {
    1002           0 :     GNUNET_assert (0 ==
    1003             :                    json_object_set_new (
    1004             :                      order,
    1005             :                      "max_wire_fee",
    1006             :                      TALER_JSON_from_amount (&settings->default_max_wire_fee)));
    1007             :   }
    1008             : 
    1009           0 :   if (GNUNET_OK !=
    1010           0 :       TALER_amount_is_valid (&max_fee))
    1011             :   {
    1012           0 :     GNUNET_assert (0 ==
    1013             :                    json_object_set_new (
    1014             :                      order,
    1015             :                      "max_fee",
    1016             :                      TALER_JSON_from_amount (
    1017             :                        &settings->default_max_deposit_fee)));
    1018             :   }
    1019           0 :   if (0 == wire_fee_amortization)
    1020             :   {
    1021           0 :     GNUNET_assert (0 ==
    1022             :                    json_object_set_new (
    1023             :                      order,
    1024             :                      "wire_fee_amortization",
    1025             :                      json_integer (
    1026             :                        (json_int_t) settings->default_wire_fee_amortization)));
    1027             :   }
    1028           0 :   if (NULL == merchant_base_url)
    1029             :   {
    1030             :     char *url;
    1031             : 
    1032           0 :     url = make_merchant_base_url (connection,
    1033           0 :                                   settings->id);
    1034           0 :     GNUNET_assert (0 ==
    1035             :                    json_object_set_new (order,
    1036             :                                         "merchant_base_url",
    1037             :                                         json_string (url)));
    1038           0 :     GNUNET_free (url);
    1039             :   }
    1040           0 :   else if (('\0' == *merchant_base_url) ||
    1041           0 :            ('/' != merchant_base_url[strlen (merchant_base_url) - 1]))
    1042             :   {
    1043           0 :     GNUNET_break_op (0);
    1044           0 :     GNUNET_JSON_parse_free (spec);
    1045           0 :     return TALER_MHD_reply_with_error (
    1046             :       connection,
    1047             :       MHD_HTTP_BAD_REQUEST,
    1048             :       TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR,
    1049             :       "merchant_base_url is not valid");
    1050             :   }
    1051             : 
    1052             :   /* Fill in merchant information if necessary */
    1053           0 :   if (NULL != jmerchant)
    1054             :   {
    1055           0 :     GNUNET_break_op (0);
    1056           0 :     GNUNET_JSON_parse_free (spec);
    1057           0 :     return TALER_MHD_reply_with_error (
    1058             :       connection,
    1059             :       MHD_HTTP_BAD_REQUEST,
    1060             :       TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR,
    1061             :       "'merchant' field already set, but must be provided by backend");
    1062             :   }
    1063             : 
    1064             :   {
    1065             :     json_t *jm;
    1066             : 
    1067           0 :     jm = GNUNET_JSON_PACK (
    1068             :       GNUNET_JSON_pack_string ("name",
    1069             :                                settings->name),
    1070             :       GNUNET_JSON_pack_allow_null (
    1071             :         GNUNET_JSON_pack_string ("website",
    1072             :                                  settings->website)),
    1073             :       GNUNET_JSON_pack_allow_null (
    1074             :         GNUNET_JSON_pack_string ("email",
    1075             :                                  settings->email)),
    1076             :       GNUNET_JSON_pack_allow_null (
    1077             :         GNUNET_JSON_pack_string ("logo",
    1078             :                                  settings->logo)));
    1079           0 :     GNUNET_assert (NULL != jm);
    1080             :     {
    1081             :       json_t *loca;
    1082             : 
    1083             :       /* Handle merchant address */
    1084           0 :       loca = settings->address;
    1085           0 :       if (NULL != loca)
    1086             :       {
    1087           0 :         loca = json_deep_copy (loca);
    1088           0 :         GNUNET_assert (0 ==
    1089             :                        json_object_set_new (jm,
    1090             :                                             "address",
    1091             :                                             loca));
    1092             :       }
    1093             :     }
    1094             :     {
    1095             :       json_t *locj;
    1096             : 
    1097             :       /* Handle merchant jurisdiction */
    1098           0 :       locj = settings->jurisdiction;
    1099           0 :       if (NULL != locj)
    1100             :       {
    1101           0 :         locj = json_deep_copy (locj);
    1102           0 :         GNUNET_assert (0 ==
    1103             :                        json_object_set_new (jm,
    1104             :                                             "jurisdiction",
    1105             :                                             locj));
    1106             :       }
    1107             :     }
    1108           0 :     GNUNET_assert (0 ==
    1109             :                    json_object_set_new (order,
    1110             :                                         "merchant",
    1111             :                                         jm));
    1112             :   }
    1113             : 
    1114             :   /* add fields to the contract that the backend should provide */
    1115           0 :   GNUNET_assert (0 ==
    1116             :                  json_object_set (order,
    1117             :                                   "exchanges",
    1118             :                                   TMH_trusted_exchanges));
    1119           0 :   GNUNET_assert (0 ==
    1120             :                  json_object_set (order,
    1121             :                                   "auditors",
    1122             :                                   j_auditors));
    1123           0 :   GNUNET_assert (0 ==
    1124             :                  json_object_set_new (order,
    1125             :                                       "merchant_pub",
    1126             :                                       GNUNET_JSON_from_data_auto (
    1127             :                                         &hc->instance->merchant_pub)));
    1128           0 :   if (GNUNET_OK !=
    1129           0 :       TALER_JSON_contract_seed_forgettable (order))
    1130             :   {
    1131           0 :     GNUNET_JSON_parse_free (spec);
    1132           0 :     return TALER_MHD_reply_with_error (
    1133             :       connection,
    1134             :       MHD_HTTP_BAD_REQUEST,
    1135             :       TALER_EC_GENERIC_JSON_INVALID,
    1136             :       "could not compute hash of order due to bogus forgettable fields");
    1137             :   }
    1138             : 
    1139           0 :   if ( (NULL != delivery_location) &&
    1140           0 :        (! TMH_location_object_valid (delivery_location)) )
    1141             :   {
    1142           0 :     GNUNET_break_op (0);
    1143           0 :     GNUNET_JSON_parse_free (spec);
    1144           0 :     return TALER_MHD_reply_with_error (connection,
    1145             :                                        MHD_HTTP_BAD_REQUEST,
    1146             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    1147             :                                        "delivery_location");
    1148             :   }
    1149             : 
    1150             :   /* sanity check result */
    1151             :   {
    1152             :     struct TALER_PrivateContractHashP h_control;
    1153             : 
    1154           0 :     switch (TALER_JSON_contract_hash (order,
    1155             :                                       &h_control))
    1156             :     {
    1157           0 :     case GNUNET_SYSERR:
    1158           0 :       GNUNET_break (0);
    1159           0 :       GNUNET_JSON_parse_free (spec);
    1160           0 :       return TALER_MHD_reply_with_error (
    1161             :         connection,
    1162             :         MHD_HTTP_INTERNAL_SERVER_ERROR,
    1163             :         TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
    1164             :         "could not compute hash of patched order");
    1165           0 :     case GNUNET_NO:
    1166           0 :       GNUNET_JSON_parse_free (spec);
    1167           0 :       GNUNET_break_op (0);
    1168           0 :       return TALER_MHD_reply_with_error (
    1169             :         connection,
    1170             :         MHD_HTTP_BAD_REQUEST,
    1171             :         TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
    1172             :         "order contained unallowed values");
    1173           0 :     case GNUNET_OK:
    1174           0 :       break;
    1175             :     }
    1176           0 :   }
    1177             :   {
    1178             :     MHD_RESULT mres;
    1179             : 
    1180           0 :     mres = execute_order (connection,
    1181             :                           hc,
    1182             :                           h_post_data,
    1183             :                           order,
    1184             :                           claim_token,
    1185             :                           inventory_products_length,
    1186             :                           inventory_products,
    1187             :                           uuids_length,
    1188             :                           uuids);
    1189           0 :     GNUNET_JSON_parse_free (spec);
    1190           0 :     return mres;
    1191             :   }
    1192             : }
    1193             : 
    1194             : 
    1195             : /**
    1196             :  * Process the @a payment_target and add the details of how the
    1197             :  * order could be paid to @a order. On success, continue
    1198             :  * processing with patch_order().
    1199             :  *
    1200             :  * @param connection connection to write the result or error to
    1201             :  * @param hc handler context for the request
    1202             :  * @param h_post_data hash of the client's POST request, for idempotency checks
    1203             :  * @param[in,out] order order to process (can be modified)
    1204             :  * @param claim_token token to use for access control
    1205             :  * @param refund_delay refund delay
    1206             :  * @param payment_target desired wire method, NULL for no preference
    1207             :  * @param inventory_products_length length of the @a inventory_products array
    1208             :  * @param inventory_products array of products to add to @a order from our inventory
    1209             :  * @param uuids_length length of the @a uuids array
    1210             :  * @param uuids array of UUIDs used to reserve products from @a inventory_products
    1211             :  * @return MHD result code
    1212             :  */
    1213             : static MHD_RESULT
    1214           0 : add_payment_details (struct MHD_Connection *connection,
    1215             :                      struct TMH_HandlerContext *hc,
    1216             :                      const struct TALER_MerchantPostDataHashP *h_post_data,
    1217             :                      json_t *order,
    1218             :                      const struct TALER_ClaimTokenP *claim_token,
    1219             :                      struct GNUNET_TIME_Relative refund_delay,
    1220             :                      const char *payment_target,
    1221             :                      unsigned int inventory_products_length,
    1222             :                      const struct InventoryProduct inventory_products[],
    1223             :                      unsigned int uuids_length,
    1224             :                      const struct GNUNET_Uuid uuids[])
    1225             : {
    1226             :   struct TMH_WireMethod *wm;
    1227             : 
    1228           0 :   wm = hc->instance->wm_head;
    1229             :   /* Locate wire method that has a matching payment target */
    1230           0 :   while ( (NULL != wm) &&
    1231           0 :           ( (! wm->active) ||
    1232           0 :             ( (NULL != payment_target) &&
    1233           0 :               (0 != strcasecmp (payment_target,
    1234           0 :                                 wm->wire_method) ) ) ) )
    1235           0 :     wm = wm->next;
    1236           0 :   if (NULL == wm)
    1237             :   {
    1238           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1239             :                 "No wire method available for instance '%s'\n",
    1240             :                 hc->instance->settings.id);
    1241           0 :     return TALER_MHD_reply_with_error (connection,
    1242             :                                        MHD_HTTP_NOT_FOUND,
    1243             :                                        TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_INSTANCE_CONFIGURATION_LACKS_WIRE,
    1244             :                                        payment_target);
    1245             :   }
    1246           0 :   GNUNET_assert (0 ==
    1247             :                  json_object_set_new (order,
    1248             :                                       "h_wire",
    1249             :                                       GNUNET_JSON_from_data_auto (
    1250             :                                         &wm->h_wire)));
    1251           0 :   GNUNET_assert (0 ==
    1252             :                  json_object_set_new (order,
    1253             :                                       "wire_method",
    1254             :                                       json_string (wm->wire_method)));
    1255           0 :   return patch_order (connection,
    1256             :                       hc,
    1257             :                       h_post_data,
    1258             :                       order,
    1259             :                       claim_token,
    1260             :                       refund_delay,
    1261             :                       inventory_products_length,
    1262             :                       inventory_products,
    1263             :                       uuids_length,
    1264             :                       uuids);
    1265             : }
    1266             : 
    1267             : 
    1268             : /**
    1269             :  * Merge the inventory products into @a order, querying the
    1270             :  * database about the details of those products. Upon success,
    1271             :  * continue processing by calling add_payment_details().
    1272             :  *
    1273             :  * @param connection connection to write the result or error to
    1274             :  * @param hc handler context for the request
    1275             :  * @param h_post_data hash of the client's POST request, for idempotency checks
    1276             :  * @param[in,out] order order to process (can be modified)
    1277             :  * @param claim_token token to use for access control
    1278             :  * @param refund_delay time window where it is possible to ask a refund
    1279             :  * @param payment_target RFC8905 payment target type to find a matching merchant account
    1280             :  * @param inventory_products_length length of the @a inventory_products array
    1281             :  * @param inventory_products array of products to add to @a order from our inventory
    1282             :  * @param uuids_length length of the @a uuids array
    1283             :  * @param uuids array of UUIDs used to reserve products from @a inventory_products
    1284             :  * @return MHD result code
    1285             :  */
    1286             : static MHD_RESULT
    1287           0 : merge_inventory (struct MHD_Connection *connection,
    1288             :                  struct TMH_HandlerContext *hc,
    1289             :                  const struct TALER_MerchantPostDataHashP *h_post_data,
    1290             :                  json_t *order,
    1291             :                  const struct TALER_ClaimTokenP *claim_token,
    1292             :                  struct GNUNET_TIME_Relative refund_delay,
    1293             :                  const char *payment_target,
    1294             :                  unsigned int inventory_products_length,
    1295             :                  const struct InventoryProduct inventory_products[],
    1296             :                  unsigned int uuids_length,
    1297             :                  const struct GNUNET_Uuid uuids[])
    1298             : {
    1299             :   /**
    1300             :    * inventory_products => instructions to add products to contract terms
    1301             :    * order.products => contains products that are not from the backend-managed inventory.
    1302             :    */
    1303           0 :   GNUNET_assert (NULL != order);
    1304             :   {
    1305           0 :     json_t *jprod = json_object_get (order,
    1306             :                                      "products");
    1307           0 :     if (NULL == jprod)
    1308             :     {
    1309           0 :       GNUNET_assert (0 ==
    1310             :                      json_object_set_new (order,
    1311             :                                           "products",
    1312             :                                           json_array ()));
    1313             :     }
    1314           0 :     else if (! TMH_products_array_valid (jprod))
    1315             :     {
    1316           0 :       return TALER_MHD_reply_with_error (connection,
    1317             :                                          MHD_HTTP_BAD_REQUEST,
    1318             :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    1319             :                                          "order.products");
    1320             :     }
    1321             :   }
    1322             : 
    1323             :   /* Populate products from inventory product array and database */
    1324             :   {
    1325           0 :     json_t *np = json_array ();
    1326             : 
    1327           0 :     for (unsigned int i = 0; i<inventory_products_length; i++)
    1328             :     {
    1329             :       struct TALER_MERCHANTDB_ProductDetails pd;
    1330             :       enum GNUNET_DB_QueryStatus qs;
    1331             : 
    1332           0 :       qs = TMH_db->lookup_product (TMH_db->cls,
    1333           0 :                                    hc->instance->settings.id,
    1334           0 :                                    inventory_products[i].product_id,
    1335             :                                    &pd);
    1336           0 :       if (qs <= 0)
    1337             :       {
    1338           0 :         enum TALER_ErrorCode ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    1339           0 :         unsigned int http_status = 0;
    1340             : 
    1341           0 :         switch (qs)
    1342             :         {
    1343           0 :         case GNUNET_DB_STATUS_HARD_ERROR:
    1344           0 :           GNUNET_break (0);
    1345           0 :           http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
    1346           0 :           ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
    1347           0 :           break;
    1348           0 :         case GNUNET_DB_STATUS_SOFT_ERROR:
    1349           0 :           GNUNET_break (0);
    1350           0 :           http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
    1351           0 :           ec = TALER_EC_GENERIC_DB_SOFT_FAILURE;
    1352           0 :           break;
    1353           0 :         case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1354           0 :           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1355             :                       "Product %s from order unknown\n",
    1356             :                       inventory_products[i].product_id);
    1357           0 :           http_status = MHD_HTTP_NOT_FOUND;
    1358           0 :           ec = TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN;
    1359           0 :           break;
    1360           0 :         case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1361             :           /* case listed to make compilers happy */
    1362           0 :           GNUNET_assert (0);
    1363             :         }
    1364           0 :         json_decref (np);
    1365           0 :         return TALER_MHD_reply_with_error (connection,
    1366             :                                            http_status,
    1367             :                                            ec,
    1368           0 :                                            inventory_products[i].product_id);
    1369             :       }
    1370             :       {
    1371             :         json_t *p;
    1372             : 
    1373           0 :         p = GNUNET_JSON_PACK (
    1374             :           GNUNET_JSON_pack_string ("description",
    1375             :                                    pd.description),
    1376             :           GNUNET_JSON_pack_object_steal ("description_i18n",
    1377             :                                          pd.description_i18n),
    1378             :           GNUNET_JSON_pack_string ("unit",
    1379             :                                    pd.unit),
    1380             :           TALER_JSON_pack_amount ("price",
    1381             :                                   &pd.price),
    1382             :           GNUNET_JSON_pack_array_steal ("taxes",
    1383             :                                         pd.taxes),
    1384             :           GNUNET_JSON_pack_string ("image",
    1385             :                                    pd.image),
    1386             :           GNUNET_JSON_pack_uint64 ("quantity",
    1387             :                                    inventory_products[i].
    1388             :                                    quantity));
    1389           0 :         GNUNET_assert (NULL != p);
    1390           0 :         GNUNET_assert (0 ==
    1391             :                        json_array_append_new (np,
    1392             :                                               p));
    1393             :       }
    1394           0 :       GNUNET_free (pd.description);
    1395           0 :       GNUNET_free (pd.unit);
    1396           0 :       GNUNET_free (pd.image);
    1397           0 :       json_decref (pd.address);
    1398             :     }
    1399             :     /* merge into existing products list */
    1400             :     {
    1401             :       json_t *xp;
    1402             : 
    1403           0 :       xp = json_object_get (order,
    1404             :                             "products");
    1405           0 :       GNUNET_assert (NULL != xp);
    1406           0 :       json_array_extend (xp, np);
    1407           0 :       json_decref (np);
    1408             :     }
    1409             :   }
    1410           0 :   return add_payment_details (connection,
    1411             :                               hc,
    1412             :                               h_post_data,
    1413             :                               order,
    1414             :                               claim_token,
    1415             :                               refund_delay,
    1416             :                               payment_target,
    1417             :                               inventory_products_length,
    1418             :                               inventory_products,
    1419             :                               uuids_length,
    1420             :                               uuids);
    1421             : }
    1422             : 
    1423             : 
    1424             : /**
    1425             :  * Generate an order.  We add the fields 'exchanges', 'merchant_pub', and
    1426             :  * 'H_wire' to the order gotten from the frontend, as well as possibly other
    1427             :  * fields if the frontend did not provide them. Returns the order_id.
    1428             :  *
    1429             :  * @param rh context of the handler
    1430             :  * @param connection the MHD connection to handle
    1431             :  * @param[in,out] hc context with further information about the request
    1432             :  * @return MHD result code
    1433             :  */
    1434             : MHD_RESULT
    1435           0 : TMH_private_post_orders (const struct TMH_RequestHandler *rh,
    1436             :                          struct MHD_Connection *connection,
    1437             :                          struct TMH_HandlerContext *hc)
    1438             : {
    1439             :   json_t *order;
    1440           0 :   struct GNUNET_TIME_Relative refund_delay = GNUNET_TIME_UNIT_ZERO;
    1441           0 :   const char *payment_target = NULL;
    1442           0 :   json_t *ip = NULL;
    1443           0 :   unsigned int ips_len = 0;
    1444           0 :   struct InventoryProduct *ips = NULL;
    1445           0 :   unsigned int uuids_len = 0;
    1446             :   json_t *uuid;
    1447           0 :   struct GNUNET_Uuid *uuids = NULL;
    1448             :   struct TALER_ClaimTokenP claim_token;
    1449           0 :   bool create_token = true; /* default */
    1450             :   struct GNUNET_JSON_Specification spec[] = {
    1451           0 :     GNUNET_JSON_spec_json ("order",
    1452             :                            &order),
    1453           0 :     GNUNET_JSON_spec_mark_optional (
    1454             :       GNUNET_JSON_spec_relative_time ("refund_delay",
    1455             :                                       &refund_delay),
    1456             :       NULL),
    1457           0 :     GNUNET_JSON_spec_mark_optional (
    1458             :       GNUNET_JSON_spec_string ("payment_target",
    1459             :                                &payment_target),
    1460             :       NULL),
    1461           0 :     GNUNET_JSON_spec_mark_optional (
    1462             :       GNUNET_JSON_spec_json ("inventory_products",
    1463             :                              &ip),
    1464             :       NULL),
    1465           0 :     GNUNET_JSON_spec_mark_optional (
    1466             :       GNUNET_JSON_spec_json ("lock_uuids",
    1467             :                              &uuid),
    1468             :       NULL),
    1469           0 :     GNUNET_JSON_spec_mark_optional (
    1470             :       GNUNET_JSON_spec_bool ("create_token",
    1471             :                              &create_token),
    1472             :       NULL),
    1473           0 :     GNUNET_JSON_spec_end ()
    1474             :   };
    1475             :   enum GNUNET_GenericReturnValue ret;
    1476             :   struct TALER_MerchantPostDataHashP h_post_data;
    1477             : 
    1478             :   (void) rh;
    1479           0 :   ret = TALER_MHD_parse_json_data (connection,
    1480           0 :                                    hc->request_body,
    1481             :                                    spec);
    1482           0 :   if (GNUNET_OK != ret)
    1483             :     return (GNUNET_NO == ret)
    1484             :            ? MHD_YES
    1485           0 :            : MHD_NO;
    1486             : 
    1487           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1488             :               "Refund delay is %s\n",
    1489             :               GNUNET_TIME_relative2s (refund_delay,
    1490             :                                       false));
    1491             : 
    1492           0 :   TMH_db->expire_locks (TMH_db->cls);
    1493           0 :   if (create_token)
    1494             :   {
    1495           0 :     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
    1496             :                                 &claim_token,
    1497             :                                 sizeof (claim_token));
    1498             :   }
    1499             :   else
    1500             :   {
    1501             :     /* we use all-zeros for 'no token' */
    1502           0 :     memset (&claim_token,
    1503             :             0,
    1504             :             sizeof (claim_token));
    1505             :   }
    1506             : 
    1507             :   /* Compute h_post_data (for idempotency check) */
    1508             :   {
    1509             :     char *req_body_enc;
    1510             : 
    1511             :     /* Dump normalized JSON to string. */
    1512           0 :     if (NULL == (req_body_enc = json_dumps (hc->request_body,
    1513             :                                             JSON_ENCODE_ANY
    1514             :                                             | JSON_COMPACT
    1515             :                                             | JSON_SORT_KEYS)))
    1516             :     {
    1517           0 :       GNUNET_break (0);
    1518           0 :       GNUNET_JSON_parse_free (spec);
    1519           0 :       return TALER_MHD_reply_with_error (connection,
    1520             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    1521             :                                          TALER_EC_GENERIC_ALLOCATION_FAILURE,
    1522             :                                          "request body normalization for hashing");
    1523             :     }
    1524           0 :     GNUNET_CRYPTO_hash (req_body_enc,
    1525             :                         strlen (req_body_enc),
    1526             :                         &h_post_data.hash);
    1527           0 :     GNUNET_free (req_body_enc);
    1528             :   }
    1529             : 
    1530             :   /* parse the inventory_products (optionally given) */
    1531           0 :   if (NULL != ip)
    1532             :   {
    1533           0 :     if (! json_is_array (ip))
    1534             :     {
    1535           0 :       GNUNET_JSON_parse_free (spec);
    1536           0 :       return TALER_MHD_reply_with_error (connection,
    1537             :                                          MHD_HTTP_BAD_REQUEST,
    1538             :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    1539             :                                          "inventory_products");
    1540             :     }
    1541           0 :     GNUNET_array_grow (ips,
    1542             :                        ips_len,
    1543             :                        json_array_size (ip));
    1544           0 :     for (unsigned int i = 0; i<ips_len; i++)
    1545             :     {
    1546             :       const char *error_name;
    1547             :       unsigned int error_line;
    1548             :       struct GNUNET_JSON_Specification ispec[] = {
    1549           0 :         GNUNET_JSON_spec_string ("product_id",
    1550           0 :                                  &ips[i].product_id),
    1551           0 :         GNUNET_JSON_spec_uint32 ("quantity",
    1552           0 :                                  &ips[i].quantity),
    1553           0 :         GNUNET_JSON_spec_end ()
    1554             :       };
    1555             : 
    1556           0 :       ret = GNUNET_JSON_parse (json_array_get (ip,
    1557             :                                                i),
    1558             :                                ispec,
    1559             :                                &error_name,
    1560             :                                &error_line);
    1561           0 :       if (GNUNET_OK != ret)
    1562             :       {
    1563           0 :         GNUNET_break_op (0);
    1564           0 :         GNUNET_array_grow (ips,
    1565             :                            ips_len,
    1566             :                            0);
    1567           0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1568             :                     "Product parsing failed at #%u: %s:%u\n",
    1569             :                     i,
    1570             :                     error_name,
    1571             :                     error_line);
    1572           0 :         GNUNET_JSON_parse_free (spec);
    1573           0 :         return TALER_MHD_reply_with_error (connection,
    1574             :                                            MHD_HTTP_BAD_REQUEST,
    1575             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
    1576             :                                            "inventory_products");
    1577             :       }
    1578             :     }
    1579             :   }
    1580             : 
    1581             :   /* parse the lock_uuids (optionally given) */
    1582           0 :   if (NULL != uuid)
    1583             :   {
    1584           0 :     if (! json_is_array (uuid))
    1585             :     {
    1586           0 :       GNUNET_array_grow (ips,
    1587             :                          ips_len,
    1588             :                          0);
    1589           0 :       GNUNET_JSON_parse_free (spec);
    1590           0 :       return TALER_MHD_reply_with_error (connection,
    1591             :                                          MHD_HTTP_BAD_REQUEST,
    1592             :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    1593             :                                          "lock_uuids");
    1594             :     }
    1595           0 :     GNUNET_array_grow (uuids,
    1596             :                        uuids_len,
    1597             :                        json_array_size (uuid));
    1598           0 :     for (unsigned int i = 0; i<uuids_len; i++)
    1599             :     {
    1600           0 :       json_t *ui = json_array_get (uuid,
    1601             :                                    i);
    1602             : 
    1603           0 :       if (! json_is_string (ui))
    1604             :       {
    1605           0 :         GNUNET_break_op (0);
    1606           0 :         GNUNET_array_grow (ips,
    1607             :                            ips_len,
    1608             :                            0);
    1609           0 :         GNUNET_array_grow (uuids,
    1610             :                            uuids_len,
    1611             :                            0);
    1612           0 :         GNUNET_JSON_parse_free (spec);
    1613           0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1614             :                     "UUID parsing failed at #%u\n",
    1615             :                     i);
    1616           0 :         return TALER_MHD_reply_with_error (connection,
    1617             :                                            MHD_HTTP_BAD_REQUEST,
    1618             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
    1619             :                                            "lock_uuids");
    1620             :       }
    1621           0 :       TMH_uuid_from_string (json_string_value (ui),
    1622           0 :                             &uuids[i]);
    1623             :     }
    1624             :   }
    1625             : 
    1626             :   /* Finally, start by completing the order */
    1627             :   {
    1628             :     MHD_RESULT res;
    1629             : 
    1630           0 :     res = merge_inventory (connection,
    1631             :                            hc,
    1632             :                            &h_post_data,
    1633             :                            order,
    1634             :                            &claim_token,
    1635             :                            refund_delay,
    1636             :                            payment_target,
    1637             :                            ips_len,
    1638             :                            ips,
    1639             :                            uuids_len,
    1640             :                            uuids);
    1641           0 :     GNUNET_array_grow (ips,
    1642             :                        ips_len,
    1643             :                        0);
    1644           0 :     GNUNET_array_grow (uuids,
    1645             :                        uuids_len,
    1646             :                        0);
    1647           0 :     GNUNET_JSON_parse_free (spec);
    1648           0 :     return res;
    1649             :   }
    1650             : }
    1651             : 
    1652             : 
    1653             : /* end of taler-merchant-httpd_private-post-orders.c */

Generated by: LCOV version 1.14