commit 7a38e2dcaaabfc0a17ca9362a40958539c629785
parent 5e22160688bc002bc9622b38d2d6d922d1dd8394
Author: Christian Grothoff <grothoff@gnunet.org>
Date: Fri, 16 Jan 2026 09:00:09 +0100
more template creation tabularasa
Diffstat:
9 files changed, 1159 insertions(+), 1366 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_get-templates-ID.c b/src/backend/taler-merchant-httpd_get-templates-ID.c
@@ -105,12 +105,12 @@ inventory_payload_cleanup (struct InventoryPayloadContext *ipc)
* @param categories category IDs
*/
static void
-add_inventory_product (void *cls,
- const char *product_id,
- const struct
- TALER_MERCHANTDB_InventoryProductDetails *pd,
- size_t num_categories,
- const uint64_t *categories)
+add_inventory_product (
+ void *cls,
+ const char *product_id,
+ const struct TALER_MERCHANTDB_InventoryProductDetails *pd,
+ size_t num_categories,
+ const uint64_t *categories)
{
struct InventoryPayloadContext *ipc = cls;
json_t *jcategories;
@@ -261,7 +261,6 @@ handle_get_templates_inventory (
size_t num_category_ids = 0;
json_t *inventory_payload;
json_t *template_contract;
- enum GNUNET_DB_QueryStatus qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
memset (&ipc,
0,
@@ -306,8 +305,6 @@ handle_get_templates_inventory (
if (! ipc.selected_all)
{
- // TODO: maybe, these 2 sections would be better to save to the db, in the post/patch part
- // and not here, yet it is quite lightweight and straightforward so I wouldn't really care...
if (NULL != ipc.selected_products)
{
size_t max_ids;
@@ -323,6 +320,8 @@ handle_get_templates_inventory (
if (json_is_string (entry))
product_ids[num_product_ids++] = json_string_value (entry);
+ else
+ GNUNET_break (0);
}
}
if (NULL != ipc.selected_categories)
@@ -339,54 +338,75 @@ handle_get_templates_inventory (
i);
if (json_is_integer (entry) &&
- (0 <= json_integer_value (entry)))
+ (0 < json_integer_value (entry)))
category_ids[num_category_ids++]
= (uint64_t) json_integer_value (entry);
+ else
+ GNUNET_break (0);
}
}
}
if (ipc.selected_all)
+ {
+ enum GNUNET_DB_QueryStatus qs;
+
qs = TMH_db->lookup_inventory_products (TMH_db->cls,
mi->settings.id,
&add_inventory_product,
&ipc);
+ if (0 > qs)
+ {
+ GNUNET_break (0);
+ inventory_payload_cleanup (&ipc);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "lookup_inventory_products");
+ }
+ }
else if ( (0 < num_product_ids) ||
(0 < num_category_ids) )
{
- qs = TMH_db->lookup_inventory_products_filtered (TMH_db->cls,
- mi->settings.id,
- product_ids,
- num_product_ids,
- category_ids,
- num_category_ids,
- &add_inventory_product,
- &ipc);
+ enum GNUNET_DB_QueryStatus qs;
+ qs = TMH_db->lookup_inventory_products_filtered (
+ TMH_db->cls,
+ mi->settings.id,
+ product_ids,
+ num_product_ids,
+ category_ids,
+ num_category_ids,
+ &add_inventory_product,
+ &ipc);
GNUNET_free (product_ids);
GNUNET_free (category_ids);
- }
- if (0 > qs)
- {
- GNUNET_break (0);
- inventory_payload_cleanup (&ipc);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "lookup_inventory_products");
+ if (0 > qs)
+ {
+ GNUNET_break (0);
+ inventory_payload_cleanup (&ipc);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "lookup_inventory_products_filtered");
+ }
}
ipc.category_payload = json_array ();
GNUNET_assert (NULL != ipc.category_payload);
if (0 < ipc.category_set.len)
{
- qs = TMH_db->lookup_categories_by_ids (TMH_db->cls,
- mi->settings.id,
- ipc.category_set.ids,
- ipc.category_set.len,
- &add_inventory_category,
- &ipc);
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TMH_db->lookup_categories_by_ids (
+ TMH_db->cls,
+ mi->settings.id,
+ ipc.category_set.ids,
+ ipc.category_set.len,
+ &add_inventory_category,
+ &ipc);
if (0 > qs)
{
GNUNET_break (0);
@@ -403,11 +423,12 @@ handle_get_templates_inventory (
GNUNET_assert (NULL != ipc.unit_payload);
if (0 < ipc.unit_set.len)
{
+ enum GNUNET_DB_QueryStatus qs;
+
qs = TMH_db->lookup_custom_units_by_names (
TMH_db->cls,
mi->settings.id,
- (const char *const *) ipc.unit_set.
- units,
+ (const char *const *) ipc.unit_set.units,
ipc.unit_set.len,
&add_inventory_unit,
&ipc);
@@ -436,6 +457,14 @@ handle_get_templates_inventory (
template_contract = json_deep_copy (tp->template_contract);
GNUNET_assert (NULL != template_contract);
+ /* remove internal fields */
+ (void) json_object_del (template_contract,
+ "selected_categories");
+ (void) json_object_del (template_contract,
+ "selected_products");
+ (void) json_object_del (template_contract,
+ "selected_all");
+ /* add inventory data */
GNUNET_assert (0 ==
json_object_set_new (template_contract,
"inventory_payload",
@@ -498,11 +527,10 @@ TMH_get_templates_ID (
ret = handle_get_templates_inventory (connection,
mi,
&tp);
- break;
+ TALER_MERCHANTDB_template_details_free (&tp);
+ return ret;
case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
- /* TODO: PAIVANA add paivana-specific payload elements.
- (Yet, I think, everything will be in the contract json so we can just pack it) */
ret = TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_OK,
@@ -511,17 +539,17 @@ TMH_get_templates_ID (
tp.editable_defaults)),
GNUNET_JSON_pack_object_incref ("template_contract",
tp.template_contract));
- break;
+ TALER_MERCHANTDB_template_details_free (&tp);
+ return ret;
case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
- default:
- GNUNET_break_op (0);
- ret = TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "template_type");
break;
}
+ GNUNET_break_op (0);
+ ret = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "template_type");
TALER_MERCHANTDB_template_details_free (&tp);
return ret;
}
diff --git a/src/backend/taler-merchant-httpd_helper.c b/src/backend/taler-merchant-httpd_helper.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014--2023 Taler Systems SA
+ (C) 2014--2026 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
@@ -374,6 +374,7 @@ TMH_products_array_valid (const json_t *products)
}
json_array_foreach ((json_t *) products, idx, product)
{
+ /* FIXME: use TALER_MERCHANT_parse_product() instead? */
const char *product_id = NULL;
const char *description;
uint64_t quantity = 0;
@@ -523,164 +524,151 @@ TMH_template_type_to_string (enum TALER_MERCHANT_TemplateType template_type)
}
-bool
-TMH_template_contract_valid (const json_t *template_contract)
+static bool
+inventory_template_contract_valid (const json_t *template_contract)
{
- enum TALER_MERCHANT_TemplateType template_type;
- const char *summary;
- const char *currency;
- struct TALER_Amount amount = { .value = 0};
- uint32_t minimum_age = 0;
- struct GNUNET_TIME_Relative pay_duration = { 0 };
- struct GNUNET_JSON_Specification spec[] = {
+ bool selected_all = false;
+ bool choose_one = false;
+ bool request_tip = false;
+ const json_t *selected_categories = NULL;
+ const json_t *selected_products = NULL;
+ struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("summary",
- &summary),
+ GNUNET_JSON_spec_bool ("request_tip",
+ &request_tip),
NULL),
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("currency",
- ¤cy),
+ GNUNET_JSON_spec_bool ("selected_all",
+ &selected_all),
NULL),
GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount_any ("amount",
- &amount),
+ GNUNET_JSON_spec_array_const ("selected_categories",
+ &selected_categories),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const ("selected_products",
+ &selected_products),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool ("choose_one",
+ &choose_one),
NULL),
- GNUNET_JSON_spec_uint32 ("minimum_age",
- &minimum_age),
- GNUNET_JSON_spec_relative_time ("pay_duration",
- &pay_duration),
GNUNET_JSON_spec_end ()
};
const char *ename;
unsigned int eline;
- template_type = TMH_template_type_from_contract (template_contract);
- if (TALER_MERCHANT_TEMPLATE_TYPE_INVALID == template_type)
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (template_contract,
+ ispec,
+ &ename,
+ &eline))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid template_type\n");
+ "Invalid inventory template_contract for field %s\n",
+ ename);
return false;
}
-
- if (TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART == template_type)
+ if (NULL != selected_categories)
{
- bool selected_all = false;
- bool choose_one = false;
- bool request_tip = false;
- const json_t *selected_categories = NULL;
- const json_t *selected_products = NULL;
- struct GNUNET_JSON_Specification ispec[] = {
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("summary",
- &summary),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_bool ("request_tip",
- &request_tip),
- NULL),
- GNUNET_JSON_spec_relative_time ("pay_duration",
- &pay_duration),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_bool ("selected_all",
- &selected_all),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const ("selected_categories",
- &selected_categories),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const ("selected_products",
- &selected_products),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_bool ("choose_one",
- &choose_one),
- NULL),
- GNUNET_JSON_spec_end ()
- };
+ const json_t *entry;
+ size_t idx;
- if (GNUNET_OK !=
- GNUNET_JSON_parse (template_contract,
- ispec,
- &ename,
- &eline))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid inventory template_contract for field %s\n",
- ename);
- return false;
- }
- if (NULL != selected_categories)
+ json_array_foreach ((json_t *) selected_categories, idx, entry)
{
- const json_t *entry;
- size_t idx;
-
- json_array_foreach ((json_t *) selected_categories, idx, entry)
+ if (! json_is_integer (entry))
{
- if (! json_is_integer (entry))
- {
- GNUNET_break_op (0);
- return false;
- }
- if (0 > json_integer_value (entry))
- {
- GNUNET_break_op (0);
- return false;
- }
+ GNUNET_break_op (0);
+ return false;
}
- }
- if (NULL != selected_products)
- {
- const json_t *entry;
- size_t idx;
-
- json_array_foreach ((json_t *) selected_products, idx, entry)
+ if (0 > json_integer_value (entry))
{
- if (! json_is_string (entry))
- {
- GNUNET_break_op (0);
- return false;
- }
+ GNUNET_break_op (0);
+ return false;
}
}
- return true;
}
-
- if (TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA == template_type)
+ if (NULL != selected_products)
{
- const char *paivana_id;
- struct GNUNET_JSON_Specification pspec[] = {
- GNUNET_JSON_spec_string ("paivana_id",
- &paivana_id),
- GNUNET_JSON_spec_end ()
- };
+ const json_t *entry;
+ size_t idx;
- if (GNUNET_OK !=
- GNUNET_JSON_parse (template_contract,
- spec,
- &ename,
- &eline))
+ json_array_foreach ((json_t *) selected_products, idx, entry)
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid paivana template_contract for field %s\n",
- ename);
- return false;
- }
- if (GNUNET_OK !=
- GNUNET_JSON_parse (template_contract,
- pspec,
- &ename,
- &eline))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid paivana template_contract for field %s\n",
- ename);
- return false;
+ if (! json_is_string (entry))
+ {
+ GNUNET_break_op (0);
+ return false;
+ }
}
- (void) paivana_id;
- /* TODO: PAIVANA validate paivana-specific fields beyond presence. */
- return true;
}
+ return true;
+}
+
+
+static bool
+paivana_template_contract_valid (const json_t *template_contract)
+{
+ const char *paivana_id;
+ struct GNUNET_JSON_Specification pspec[] = {
+ GNUNET_JSON_spec_string ("paivana_id",
+ &paivana_id),
+ GNUNET_JSON_spec_end ()
+ };
+ const char *ename;
+ unsigned int eline;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (template_contract,
+ pspec,
+ &ename,
+ &eline))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid paivana template_contract for field %s\n",
+ ename);
+ return false;
+ }
+ (void) paivana_id;
+ /* TODO: PAIVANA validate paivana-specific fields beyond presence. */
+ return true;
+}
+
+
+bool
+TMH_template_contract_valid (const json_t *template_contract)
+{
+ const char *summary;
+ const char *currency;
+ struct TALER_Amount amount = { .value = 0};
+ uint32_t minimum_age = 0;
+ struct GNUNET_TIME_Relative pay_duration = { 0 };
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("summary",
+ &summary),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("currency",
+ ¤cy),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount_any ("amount",
+ &amount),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("minimum_age",
+ &minimum_age),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_relative_time ("pay_duration",
+ &pay_duration),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+ const char *ename;
+ unsigned int eline;
+ enum TALER_MERCHANT_TemplateType template_type;
if (GNUNET_OK !=
GNUNET_JSON_parse (template_contract,
@@ -693,7 +681,22 @@ TMH_template_contract_valid (const json_t *template_contract)
ename);
return false;
}
- return true;
+ template_type = TMH_template_type_from_contract (template_contract);
+ switch (template_type)
+ {
+ case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
+ /* no further fields */
+ return true;
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
+ return inventory_template_contract_valid (template_contract);
+ case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
+ return paivana_template_contract_valid (template_contract);
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
+ break;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid template_type\n");
+ return false;
}
@@ -1016,6 +1019,7 @@ trigger_webhook_cb (void *cls,
if (NULL != body_template)
{
int ret;
+
ret = TALER_TEMPLATING_fill (body_template,
t->root,
&body,
@@ -1052,6 +1056,7 @@ trigger_webhook_cb (void *cls,
&extra,
extra_size);
}
+ /* allocated by mustach, hence use free(), not GNUNET_free() */
free (header);
free (body);
}
diff --git a/src/backend/taler-merchant-httpd_helper.h b/src/backend/taler-merchant-httpd_helper.h
@@ -27,7 +27,7 @@
#include "taler-merchant-httpd.h"
-#include <taler/taler_merchant_util.h>
+#include "taler_merchant_util.h"
/**
* check @a accounts for well-formedness
@@ -55,8 +55,6 @@ TMH_location_object_valid (const json_t *location);
* Check if @a products is an array of valid Product(s) in the sense of
* Taler's API definition.
*
- * FIXME: use TALER_MERCHANT_parse_product() instead?
- *
* @param products array to check
* @return true if @a products is an array and all
* entries are valid Products.
@@ -90,17 +88,6 @@ TMH_validate_unit_price_array (const struct TALER_Amount *prices,
size_t prices_len);
/**
- * Kind of fixed-decimal value the helpers operate on.
- * Primarily distinguishes how special sentinel values (such as "-1"
- * meaning infinity for stock) must be encoded.
- */
-enum TMH_ValueKind
-{
- TMH_VK_QUANTITY, /* -1 is illegal */
- TMH_VK_STOCK /* -1 means "infinity" */
-};
-
-/**
* Determine template type from a template contract.
*
* @param template_contract contract JSON
@@ -199,39 +186,6 @@ TMH_unit_set_add (struct TMH_UnitSet *set,
void
TMH_unit_set_clear (struct TMH_UnitSet *set);
-/**
- * Extract a fixed-decimal number that may be supplied either
- * - as pure integer (e.g. "total_stock"), or
- * - as decimal text (e.g. "unit_total_stock").
- *
- * Rules:
- * - If both forms are missing -> error.
- * - If both are present -> they must match and the decimal must have no fraction.
- * - For kind == TMH_VK_STOCK the integer value -1 represents infinity.
- *
- * @param kind See #TMH_ValueKind
- * @param allow_fractional False: any fractional part is rejected
- * @param int_missing True if client omitted the integer field
- * @param int_raw Raw integer (undefined if @a int_missing is true)
- * @param str_missing True if client omitted the string field
- * @param str_raw Raw UTF-8 string (undefined if @a str_missing is true)
- * @param[out] int_out Canonicalised integer part
- * @param[out] frac_out Canonicalised fractional part
- * @param[out] error_param Set to offending field name on failure
- * @param int_field Integer field name (for error reporting)
- * @param str_field String field name (for error reporting)
- * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
- */
-enum GNUNET_GenericReturnValue
-TMH_process_quantity_inputs (enum TMH_ValueKind kind,
- bool allow_fractional,
- bool int_missing,
- int64_t int_raw,
- bool str_missing,
- const char *str_raw,
- uint64_t *int_out,
- uint32_t *frac_out,
- const char **error_param);
/**
* Lookup the defaults for @a unit within @a mi and fall back to sane
@@ -419,13 +373,13 @@ TMH_make_order_status_url (struct MHD_Connection *con,
* @param hr a `TALER_EXCHANGE_HttpResponse`
*/
#define TMH_pack_exchange_reply(hr) \
- GNUNET_JSON_pack_uint64 ("exchange_code", (hr)->ec), \
- GNUNET_JSON_pack_uint64 ("exchange_http_status", (hr)->http_status), \
- GNUNET_JSON_pack_uint64 ("exchange_ec", (hr)->ec), /* LEGACY */ \
- GNUNET_JSON_pack_uint64 ("exchange_hc", (hr)->http_status), /* LEGACY */ \
- GNUNET_JSON_pack_allow_null ( \
- GNUNET_JSON_pack_object_incref ("exchange_reply", (json_t *) (hr)-> \
- reply))
+ GNUNET_JSON_pack_uint64 ("exchange_code", (hr)->ec), \
+ GNUNET_JSON_pack_uint64 ("exchange_http_status", (hr)->http_status), \
+ GNUNET_JSON_pack_uint64 ("exchange_ec", (hr)->ec), /* LEGACY */ \
+ GNUNET_JSON_pack_uint64 ("exchange_hc", (hr)->http_status), /* LEGACY */ \
+ GNUNET_JSON_pack_allow_null ( \
+ GNUNET_JSON_pack_object_incref ("exchange_reply", (json_t *) (hr)-> \
+ reply))
/**
diff --git a/src/backend/taler-merchant-httpd_post-using-templates.c b/src/backend/taler-merchant-httpd_post-using-templates.c
@@ -30,7 +30,6 @@
#include "taler-merchant-httpd_exchanges.h"
#include <taler/taler_json_lib.h>
-
/**
* Item selected from inventory_selection.
*/
@@ -39,12 +38,12 @@ struct InventoryTemplateItemContext
/**
* Product ID as referenced in inventory.
*/
- char *product_id;
+ const char *product_id;
/**
* Unit quantity string as provided by the client.
*/
- char *unit_quantity;
+ const char *unit_quantity;
/**
* Parsed integer quantity.
@@ -82,30 +81,60 @@ enum UsePhase
* Parse request payload into context fields.
*/
USE_PHASE_PARSE_REQUEST,
+
/**
* Fetch template details from the database.
*/
USE_PHASE_LOOKUP_TEMPLATE,
+
+ /**
+ * Parse template.
+ */
+ USE_PHASE_PARSE_TEMPLATE,
+
/**
- * Load DB-backed details needed for verification.
+ * Load additional details (like products and
+ * categories) needed for verification and
+ * price computation.
*/
USE_PHASE_DB_FETCH,
+
/**
* Validate request and template compatibility.
*/
USE_PHASE_VERIFY,
+
+ /**
+ * Compute price of the order.
+ */
+ USE_PHASE_COMPUTE_PRICE,
+
+ /**
+ * Handle tip.
+ */
+ USE_PHASE_CHECK_TIP,
+
+ /**
+ * Check if client-supplied total amount matches
+ * our calculation (if we did any).
+ */
+ USE_PHASE_CHECK_TOTAL,
+
/**
* Construct the internal order request body.
*/
USE_PHASE_CREATE_ORDER,
+
/**
* Submit the order to the shared order handler.
*/
USE_PHASE_SUBMIT_ORDER,
+
/**
* Finished successfully with MHD_YES.
*/
USE_PHASE_FINISHED_MHD_YES,
+
/**
* Finished with MHD_NO.
*/
@@ -115,6 +144,11 @@ enum UsePhase
struct UseContext
{
/**
+ * Context for our handler.
+ */
+ struct TMH_HandlerContext *hc;
+
+ /**
* Internal handler context we are passing into the
* POST /private/orders handler.
*/
@@ -151,91 +185,100 @@ struct UseContext
const char *fulfillment_message;
/**
- * Parsed fields for each template type.
+ * Amount provided by the client.
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Tip provided by the client.
+ */
+ struct TALER_Amount tip;
+
+ /**
+ * True if @e amount was not provided.
+ */
+ bool no_amount;
+
+ /**
+ * True if @e tip was not provided.
*/
- union
+ bool no_tip;
+
+ /**
+ * Parsed fields for inventory templates.
+ */
+ struct
{
/**
- * Parsed fields for fixed-order templates.
+ * Selected products from inventory_selection.
*/
- struct
- {
- /**
- * Amount provided by the client.
- */
- struct TALER_Amount amount;
- /**
- * Tip provided by the client.
- */
- struct TALER_Amount tip;
- /**
- * True if @e amount was not provided.
- */
- bool no_amount;
- /**
- * True if @e tip was not provided.
- */
- bool no_tip;
- } fixed;
+ struct InventoryTemplateItemContext *items;
/**
- * Parsed fields for inventory templates.
+ * Length of @e items.
*/
- struct
- {
- /**
- * Selected products from inventory_selection.
- */
- struct InventoryTemplateItemContext *items;
-
- /**
- * Length of @e items.
- */
- unsigned int items_len;
-
- /**
- * Optional pre-calculated amount provided by the client.
- */
- struct TALER_Amount amount;
-
- /**
- * Optional tip amount.
- */
- struct TALER_Amount tip;
-
- /**
- * True if @e amount was not provided.
- */
- bool no_amount;
- /**
- * True if @e tip was not provided.
- */
- bool no_tip;
- } inventory;
- } template;
+ unsigned int items_len;
+
+ } inventory;
} parse_request;
/**
- * Information set in the #USE_PHASE_DB_FETCH phase.
+ * Information set in the #USE_PHASE_LOOKUP_TEMPLATE phase.
*/
struct
{
+
/**
* Our template details from the DB.
*/
struct TALER_MERCHANTDB_TemplateDetails etp;
+ } lookup_template;
+
+ /**
+ * Information set in the #USE_PHASE_PARSE_TEMPLATE phase.
+ */
+ struct
+ {
+
+ /**
+ * Summary from the template contract.
+ */
+ const char *tsummary;
+
+ /**
+ * Minimum age required by the template.
+ */
+ uint32_t min_age;
+
+ /**
+ * How long does the customer have to pay for the
+ * order. 0 if not specified (use instance default).
+ */
+ struct GNUNET_TIME_Relative pay_duration;
+
/**
- * True once @e etp was initialized.
+ * Currency from the template contract.
*/
- bool have_etp;
+ const char *tcurrency;
/**
- * Inventory template selections from the contract.
+ * Amount from the template contract.
+ */
+ struct TALER_Amount tamount;
+
+ /**
+ * True if @e tamount was not provided.
+ */
+ bool no_tamount;
+
+ /**
+ * Parsed fields for inventory templates.
*/
struct
{
+
/**
* Selected categories from the template contract.
*/
@@ -250,69 +293,26 @@ struct UseContext
* Whether all products are selectable.
*/
bool selected_all;
- } inventory;
- } db_fetch;
- /**
- * Information set in the #USE_PHASE_VERIFY phase.
- */
- struct
- {
- /**
- * Verified fields for each template type.
- */
- union
- {
/**
- * Verified fields for fixed-order templates.
+ * Template requires exactly one selection.
*/
- struct
- {
- /**
- * Summary from the template contract.
- */
- const char *tsummary;
- /**
- * Currency from the template contract.
- */
- const char *tcurrency;
- /**
- * Amount from the template contract.
- */
- struct TALER_Amount tamount;
- /**
- * True if @e tamount was not provided.
- */
- bool no_tamount;
- /**
- * True if no summary override was provided.
- */
- bool no_summary;
- } fixed;
+ bool choose_one;
/**
- * Verified fields for inventory templates.
+ * Template allows tips.
*/
- struct
- {
- /**
- * Template requires exactly one selection.
- */
- bool choose_one;
- /**
- * Template allows tips.
- */
- bool request_tip;
- /**
- * Summary from the template contract.
- */
- const char *tsummary;
- /**
- * True if no summary override was provided.
- */
- bool no_summary;
- } inventory;
- } template;
+ bool request_tip;
+
+ } inventory;
+
+ } parse_template;
+
+ /**
+ * Information set in the #USE_PHASE_COMPUTE_PRICE phase.
+ */
+ struct
+ {
/**
* Per-currency totals across selected products (without tips).
@@ -323,75 +323,51 @@ struct UseContext
* Length of @e totals.
*/
unsigned int totals_len;
- } verify;
+
+ } compute_price;
+
};
/**
* Clean up inventory items.
*
- * @param items item array to free
* @param items_len length of @a items
+ * @param[in] items item array to free
*/
static void
-cleanup_inventory_items (struct InventoryTemplateItemContext **items,
- unsigned int *items_len)
+cleanup_inventory_items (unsigned int items_len,
+ struct InventoryTemplateItemContext items[static
+ items_len])
{
- if ((NULL == items) || (NULL == *items))
- return;
- for (unsigned int i = 0; i < *items_len; i++)
+ for (unsigned int i = 0; i < items_len; i++)
{
- struct InventoryTemplateItemContext *item = &(*items)[i];
+ struct InventoryTemplateItemContext *item = &items[i];
- GNUNET_free (item->product_id);
- GNUNET_free (item->unit_quantity);
TALER_MERCHANTDB_product_details_free (&item->pd);
GNUNET_free (item->categories);
}
- GNUNET_free (*items);
- *items = NULL;
- if (NULL != items_len)
- *items_len = 0;
-}
-
-
-/**
- * Clean up inventory totals.
- *
- * @param totals totals array to free
- * @param totals_len length of @a totals
- */
-static void
-cleanup_inventory_totals (struct TALER_Amount **totals,
- unsigned int *totals_len)
-{
- if ((NULL == totals) || (NULL == *totals))
- return;
- GNUNET_free (*totals);
- *totals = NULL;
- if (NULL != totals_len)
- *totals_len = 0;
+ GNUNET_free (items);
}
/**
* Clean up a `struct UseContext *`
*
- * @param cls a `struct UseContext *`
+ * @param[in] cls a `struct UseContext *`
*/
static void
cleanup_use_context (void *cls)
{
struct UseContext *uc = cls;
- TALER_MERCHANTDB_template_details_free (&uc->db_fetch.etp);
- if (TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART == uc->template_type)
- {
- cleanup_inventory_items (&uc->parse_request.template.inventory.items,
- &uc->parse_request.template.inventory.items_len);
- cleanup_inventory_totals (&uc->verify.totals,
- &uc->verify.totals_len);
- }
+ TALER_MERCHANTDB_template_details_free (&uc->lookup_template.etp);
+ if (NULL !=
+ uc->parse_request.inventory.items)
+ cleanup_inventory_items (uc->parse_request.inventory.items_len,
+ uc->parse_request.inventory.items);
+ GNUNET_free (uc->compute_price.totals);
+ uc->compute_price.totals_len = 0;
if (NULL != uc->ihc.cc)
uc->ihc.cc (uc->ihc.ctx);
GNUNET_free (uc->ihc.infix);
@@ -426,6 +402,7 @@ static void
use_finalize_parse (struct UseContext *uc,
enum GNUNET_GenericReturnValue res)
{
+ GNUNET_assert (GNUNET_OK != res);
use_finalize (uc,
(GNUNET_NO == res)
? MHD_YES
@@ -437,21 +414,19 @@ use_finalize_parse (struct UseContext *uc,
* Reply with error and finalize the request.
*
* @param[in,out] uc use context
- * @param connection connection to reply on
* @param http_status HTTP status code
* @param ec error code
* @param detail error detail
*/
static void
use_reply_with_error (struct UseContext *uc,
- struct MHD_Connection *connection,
unsigned int http_status,
enum TALER_ErrorCode ec,
const char *detail)
{
MHD_RESULT mret;
- mret = TALER_MHD_reply_with_error (connection,
+ mret = TALER_MHD_reply_with_error (uc->hc->connection,
http_status,
ec,
detail);
@@ -465,42 +440,28 @@ use_reply_with_error (struct UseContext *uc,
/**
* Parse request data for inventory templates.
*
- * @param connection connection to reply on
- * @param hc handler context
- * @param uc use context
+ * @param[in,out] uc use context
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-parse_using_templates_inventory_request (struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc,
- struct UseContext *uc)
+parse_using_templates_inventory_request (
+ struct UseContext *uc)
{
- if (NULL != uc->ihc.request_body)
- return GNUNET_OK;
-
- const json_t *inventory_selection = NULL;
-
+ const json_t *inventory_selection;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("summary",
- &uc->parse_request.summary),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount_any ("amount",
- &uc->parse_request.template.inventory.amount),
- &uc->parse_request.template.inventory.no_amount),
- GNUNET_JSON_spec_mark_optional (
TALER_JSON_spec_amount_any ("tip",
- &uc->parse_request.template.inventory.tip),
- &uc->parse_request.template.inventory.no_tip),
+ &uc->parse_request.tip),
+ &uc->parse_request.no_tip),
GNUNET_JSON_spec_array_const ("inventory_selection",
&inventory_selection),
GNUNET_JSON_spec_end ()
};
enum GNUNET_GenericReturnValue res;
- res = TALER_MHD_parse_json_data (connection,
- hc->request_body,
+ GNUNET_assert (NULL == uc->ihc.request_body);
+ res = TALER_MHD_parse_json_data (uc->hc->connection,
+ uc->hc->request_body,
spec);
if (GNUNET_OK != res)
{
@@ -510,55 +471,49 @@ parse_using_templates_inventory_request (struct MHD_Connection *connection,
return GNUNET_SYSERR;
}
- if ( (! uc->parse_request.template.inventory.no_amount) ||
- (! uc->parse_request.template.inventory.no_tip) )
+ if ( (! uc->parse_request.no_amount) ||
+ (! uc->parse_request.no_tip) )
{
if (! TMH_test_exchange_configured_for_currency (
- (! uc->parse_request.template.inventory.no_amount)
- ? uc->parse_request.template.inventory.amount.currency
- : uc->parse_request.template.inventory.tip.currency))
+ (! uc->parse_request.no_amount)
+ ? uc->parse_request.amount.currency
+ : uc->parse_request.tip.currency))
{
GNUNET_break_op (0);
- cleanup_inventory_items (&uc->parse_request.template.inventory.items,
- &uc->parse_request.template.inventory.items_len);
use_reply_with_error (uc,
- connection,
MHD_HTTP_CONFLICT,
TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
- "Currency for amount is not supported by backend");
+ "Currency is not supported by backend");
return GNUNET_SYSERR;
}
}
- if (! uc->parse_request.template.inventory.no_tip)
+ if (! uc->parse_request.no_tip)
{
- if (! uc->parse_request.template.inventory.no_amount &&
+ if (! uc->parse_request.no_amount &&
(GNUNET_YES !=
TALER_amount_cmp_currency (
- &uc->parse_request.template.inventory.amount,
- &uc->parse_request.template.inventory.tip)))
+ &uc->parse_request.amount,
+ &uc->parse_request.tip)))
{
GNUNET_break_op (0);
- cleanup_inventory_items (&uc->parse_request.template.inventory.items,
- &uc->parse_request.template.inventory.items_len);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
- "Mismatch of currencies between the amount and tip");
+ use_reply_with_error (
+ uc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_CURRENCY_MISMATCH,
+ "Mismatch of currencies between the total amount and tip");
return GNUNET_SYSERR;
}
}
for (size_t i = 0; i < json_array_size (inventory_selection); i++)
{
- const char *product_id;
- const char *quantity;
+ struct InventoryTemplateItemContext item = { 0 };
struct GNUNET_JSON_Specification ispec[] = {
GNUNET_JSON_spec_string ("product_id",
- &product_id),
+ &item.product_id),
GNUNET_JSON_spec_string ("quantity",
- &quantity),
+ &item.unit_quantity),
GNUNET_JSON_spec_end ()
};
const char *err_name;
@@ -572,364 +527,353 @@ parse_using_templates_inventory_request (struct MHD_Connection *connection,
if (GNUNET_OK != res)
{
GNUNET_break_op (0);
- cleanup_inventory_items (&uc->parse_request.template.inventory.items,
- &uc->parse_request.template.inventory.items_len);
use_reply_with_error (uc,
- connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"inventory_selection");
return GNUNET_SYSERR;
}
- {
- struct InventoryTemplateItemContext item;
-
- memset (&item,
- 0,
- sizeof (item));
- item.product_id = GNUNET_strdup (product_id);
- item.unit_quantity = GNUNET_strdup (quantity);
- GNUNET_array_append (uc->parse_request.template.inventory.items,
- uc->parse_request.template.inventory.items_len,
- item);
- }
+ GNUNET_array_append (uc->parse_request.inventory.items,
+ uc->parse_request.inventory.items_len,
+ item);
}
- if (0 == uc->parse_request.template.inventory.items_len)
+#if FIXME_PERMIT_ONLY_TIPPING
+ if (0 == uc->parse_request.inventory.items_len)
{
GNUNET_break_op (0);
- cleanup_inventory_items (&uc->parse_request.template.inventory.items,
- &uc->parse_request.template.inventory.items_len);
use_reply_with_error (uc,
- connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MISSING,
"inventory_selection");
return GNUNET_SYSERR;
}
+#endif
return GNUNET_OK;
}
/**
- * Parse request data for fixed-order templates.
+ * Parse request data for paivana templates.
*
- * @param connection connection to reply on
- * @param hc handler context
- * @param uc use context
+ * @param[in,out] uc use context
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-parse_using_templates_fixed_request (struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc,
- struct UseContext *uc)
+parse_using_templates_paivana_request (
+ struct UseContext *uc)
+{
+ /* FIXME: not implemented */
+ return GNUNET_OK;
+}
+
+
+/**
+ * Main function for the #USE_PHASE_PARSE_REQUEST.
+ *
+ * @param[in,out] uc context to update
+ */
+static void
+handle_phase_parse_request (
+ struct UseContext *uc)
{
+ const char *template_type = NULL;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("template_type",
+ &template_type),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("summary",
&uc->parse_request.summary),
NULL),
GNUNET_JSON_spec_mark_optional (
TALER_JSON_spec_amount_any ("amount",
- &uc->parse_request.template.fixed.amount),
- &uc->parse_request.template.fixed.no_amount),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount_any ("tip",
- &uc->parse_request.template.fixed.tip),
- &uc->parse_request.template.fixed.no_tip),
+ &uc->parse_request.amount),
+ &uc->parse_request.no_amount),
GNUNET_JSON_spec_end ()
};
enum GNUNET_GenericReturnValue res;
- res = TALER_MHD_parse_json_data (connection,
- hc->request_body,
+ res = TALER_MHD_parse_json_data (uc->hc->connection,
+ uc->hc->request_body,
spec);
if (GNUNET_OK != res)
{
GNUNET_break_op (0);
use_finalize_parse (uc,
res);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Parse request data for paivana templates.
- *
- * @param connection connection to reply on
- * @param hc handler context
- * @param uc use context
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-parse_using_templates_paivana_request (struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc,
- struct UseContext *uc)
-{
- /* TODO: PAIVANA include paivana_id in instantiation flow. */
- return parse_using_templates_fixed_request (connection,
- hc,
- uc);
-}
-
-
-static void
-handle_phase_parse_request (struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc,
- struct UseContext *uc)
-{
- if (TALER_MERCHANT_TEMPLATE_TYPE_INVALID == uc->template_type)
- {
- if (NULL == hc->request_body || ! json_is_object (hc->request_body))
- {
- GNUNET_break_op (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "request");
- return;
- }
- uc->phase = USE_PHASE_LOOKUP_TEMPLATE;
return;
}
- enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
-
- // FIXME: share more logic between these, just skip (or run) individual
- // phases depending on the template type!
+ if (NULL == template_type)
+ template_type = "fixed-order";
+ uc->template_type
+ = TALER_MERCHANT_template_type_from_string (
+ template_type);
switch (uc->template_type)
{
case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
- res = parse_using_templates_fixed_request (connection,
- hc,
- uc);
- break;
+ /* nothig left to do */
+ uc->phase++;
+ return;
case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
- /* TODO: PAIVANA handle paivana-specific instantiation. */
- res = parse_using_templates_paivana_request (connection,
- hc,
- uc);
- break;
+ res = parse_using_templates_paivana_request (uc);
+ if (GNUNET_OK == res)
+ uc->phase++;
+ return;
case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
- res = parse_using_templates_inventory_request (connection,
- hc,
- uc);
- break;
+ res = parse_using_templates_inventory_request (uc);
+ if (GNUNET_OK == res)
+ uc->phase++;
+ return;
case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
- GNUNET_break (0);
- use_reply_with_error (
- uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "template_contract has unexpected type");
break;
}
- if (GNUNET_OK == res)
- uc->phase = USE_PHASE_DB_FETCH;
+ GNUNET_break (0);
+ use_reply_with_error (
+ uc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "template_type");
}
/* ***************** USE_PHASE_LOOKUP_TEMPLATE **************** */
+/**
+ * Main function for the #USE_PHASE_LOOKUP_TEMPLATE.
+ *
+ * @param[in,out] uc context to update
+ */
static void
-handle_phase_lookup_template (struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc,
- struct UseContext *uc)
+handle_phase_lookup_template (
+ struct UseContext *uc)
{
- struct TMH_MerchantInstance *mi = hc->instance;
- const char *template_id = hc->infix;
-
- if (! uc->db_fetch.have_etp)
+ struct TMH_MerchantInstance *mi = uc->hc->instance;
+ const char *template_id = uc->hc->infix;
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TMH_db->lookup_template (TMH_db->cls,
+ mi->settings.id,
+ template_id,
+ &uc->lookup_template.etp);
+ switch (qs)
{
- enum GNUNET_DB_QueryStatus qs;
-
- // FIXME: include template type in request, fully parse
- // request before even going into the DB.
- qs = TMH_db->lookup_template (TMH_db->cls,
- mi->settings.id,
- template_id,
- &uc->db_fetch.etp);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- /* Clean up and fail hard */
- GNUNET_break (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- NULL);
- break;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- /* this should be impossible (single select) */
- GNUNET_break (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- NULL);
- break;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- /* template not found! */
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN,
- template_id);
- break;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- /* all good */
- uc->db_fetch.have_etp = true;
- break;
- }
- if (! uc->db_fetch.have_etp)
- return;
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ /* Clean up and fail hard */
+ GNUNET_break (0);
+ use_reply_with_error (uc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "lookup_template");
+ return;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ /* this should be impossible (single select) */
+ GNUNET_break (0);
+ use_reply_with_error (uc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "lookup_template");
+ return;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* template not found! */
+ use_reply_with_error (uc,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN,
+ template_id);
+ return;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ /* all good */
+ break;
}
- uc->template_type =
- TMH_template_type_from_contract (uc->db_fetch.etp.template_contract);
- if (TALER_MERCHANT_TEMPLATE_TYPE_INVALID == uc->template_type)
+ if (uc->template_type !=
+ TMH_template_type_from_contract (
+ uc->lookup_template.etp.template_contract))
{
- GNUNET_break (0);
+ GNUNET_break_op (0);
use_reply_with_error (
uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "template_contract has unexpected type");
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_POST_USING_TEMPLATES_WRONG_TYPE,
+ "template_contract has different type");
return;
}
- uc->phase = USE_PHASE_PARSE_REQUEST;
+ uc->phase++;
}
-/* ***************** USE_PHASE_DB_FETCH **************** */
+/* ***************** USE_PHASE_PARSE_TEMPLATE **************** */
+
/**
- * Fetch DB data for inventory templates.
+ * Parse template.
*
- * @param connection connection to reply on
- * @param hc handler context
- * @param uc use context
- * @return #GNUNET_OK on success
+ * @param[in,out] uc use context
*/
-static enum GNUNET_GenericReturnValue
-db_fetch_using_templates_inventory (struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc,
- struct UseContext *uc)
-{
- struct TMH_MerchantInstance *mi = hc->instance;
-
- for (unsigned int i = 0; i < uc->parse_request.template.inventory.items_len;
- i++)
- {
- struct InventoryTemplateItemContext *item =
- &uc->parse_request.template.inventory.items[i];
- struct TALER_MERCHANTDB_ProductDetails pd;
- enum GNUNET_DB_QueryStatus qs;
- size_t num_categories = 0;
- uint64_t *categories = NULL;
-
- qs = TMH_db->lookup_product (TMH_db->cls,
- mi->settings.id,
- item->product_id,
- &pd,
- &num_categories,
- &categories);
- if (qs <= 0)
- {
- enum TALER_ErrorCode ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
- unsigned int http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
-
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- GNUNET_break (0);
- ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
- break;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- GNUNET_break (0);
- ec = TALER_EC_GENERIC_DB_SOFT_FAILURE;
- break;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- http_status = MHD_HTTP_NOT_FOUND;
- ec = TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN;
- break;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- GNUNET_assert (0);
- }
- use_reply_with_error (uc,
- connection,
- http_status,
- ec,
- item->product_id);
- return GNUNET_SYSERR;
- }
-
- item->pd = pd;
- item->categories = categories;
- item->num_categories = num_categories;
- }
- return GNUNET_OK;
-}
-
-
static void
-handle_phase_db_fetch (struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc,
- struct UseContext *uc)
+handle_phase_parse_template (struct UseContext *uc)
{
- enum GNUNET_GenericReturnValue res = GNUNET_OK;
-
- switch (uc->template_type)
- {
- case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
+ struct GNUNET_JSON_Specification tcspec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("summary",
+ &uc->parse_template.tsummary),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("currency",
+ &uc->parse_template.tcurrency),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount_any ("amount",
+ &uc->parse_template.tamount),
+ &uc->parse_template.no_tamount),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("minimum_age",
+ &uc->parse_template.min_age),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_relative_time ("pay_duration",
+ &uc->parse_template.pay_duration),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const (
+ "selected_categories",
+ &uc->parse_template.inventory.selected_categories),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const (
+ "selected_products",
+ &uc->parse_template.inventory.selected_products),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool ("choose_one",
+ &uc->parse_template.inventory.choose_one),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool ("request_tip",
+ &uc->parse_template.inventory.request_tip),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool ("selected_all",
+ &uc->parse_template.inventory.selected_all),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+ const char *err_name;
+ unsigned int err_line;
+ enum GNUNET_GenericReturnValue res;
+
+ res = GNUNET_JSON_parse (uc->lookup_template.etp.template_contract,
+ tcspec,
+ &err_name,
+ &err_line);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ use_reply_with_error (uc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ err_name);
+ return;
+ }
+ uc->phase++;
+}
+
+
+/* ***************** USE_PHASE_DB_FETCH **************** */
+
+/**
+ * Fetch DB data for inventory templates.
+ *
+ * @param[in,out] uc use context
+ */
+static void
+handle_phase_db_fetch (struct UseContext *uc)
+{
+ struct TMH_MerchantInstance *mi = uc->hc->instance;
+
+ switch (uc->template_type)
+ {
+ case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
+ uc->phase++;
+ return;
case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
- break;
+ uc->phase++;
+ return;
case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
- res = db_fetch_using_templates_inventory (connection,
- hc,
- uc);
break;
case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
- GNUNET_break (0);
- use_reply_with_error (
- uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "template_contract has unexpected type");
- break;
+ GNUNET_assert (0);
}
- if (GNUNET_OK == res)
- uc->phase = USE_PHASE_VERIFY;
+
+ for (unsigned int i = 0;
+ i < uc->parse_request.inventory.items_len;
+ i++)
+ {
+ struct InventoryTemplateItemContext *item =
+ &uc->parse_request.inventory.items[i];
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TMH_db->lookup_product (TMH_db->cls,
+ mi->settings.id,
+ item->product_id,
+ &item->pd,
+ &item->num_categories,
+ &item->categories);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ use_reply_with_error (uc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "lookup_product");
+ return;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ use_reply_with_error (uc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "lookup_product");
+ return;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ use_reply_with_error (uc,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN,
+ item->product_id);
+ return;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
+ }
+ }
+ uc->phase++;
}
-/* Helpers for USE_PHASE_VERIFY. */
+/* *************** Helpers for USE_PHASE_VERIFY ***************** */
/**
- * Check if a product ID appears in the selected_products list.
+ * Check if the given product ID appears in the array of allowed_products.
*
- * @param selected_products JSON array of selected product IDs, may be NULL
+ * @param allowed_products JSON array of product IDs allowed by the template, may be NULL
* @param product_id product ID to check
- * @return true if the product ID is selected
+ * @return true if the product ID is in the list
*/
static bool
-product_id_selected (const json_t *selected_products,
- const char *product_id)
+product_id_allowed (const json_t *allowed_products,
+ const char *product_id)
{
const json_t *entry;
size_t idx;
- if (NULL == selected_products)
+ if (NULL == allowed_products)
return false;
- json_array_foreach ((json_t *) selected_products, idx, entry)
+ json_array_foreach ((json_t *) allowed_products, idx, entry)
{
if (! json_is_string (entry))
+ {
+ GNUNET_break (0);
continue;
+ }
if (0 == strcmp (json_string_value (entry),
product_id))
return true;
@@ -941,29 +885,35 @@ product_id_selected (const json_t *selected_products,
/**
* Check if any product category is in the selected_categories list.
*
- * @param selected_categories JSON array of selected category IDs, may be NULL
+ * @param allowed_categories JSON array of categories allowed by the template, may be NULL
* @param num_categories length of @a categories
- * @param categories list of category IDs for the product
- * @return true if any category matches
+ * @param categories list of categories of the selected product
+ * @return true if any category of the product is in the list of allowed categories matches
*/
static bool
-categories_selected (const json_t *selected_categories,
- size_t num_categories,
- const uint64_t *categories)
+category_allowed (const json_t *allowed_categories,
+ size_t num_categories,
+ const uint64_t categories[num_categories])
{
const json_t *entry;
size_t idx;
- if (NULL == selected_categories)
+ if (NULL == allowed_categories)
return false;
- json_array_foreach ((json_t *) selected_categories, idx, entry)
+ json_array_foreach ((json_t *) allowed_categories, idx, entry)
{
uint64_t selected_id;
if (! json_is_integer (entry))
+ {
+ GNUNET_break (0);
continue;
+ }
if (0 > json_integer_value (entry))
+ {
+ GNUNET_break (0);
continue;
+ }
selected_id = (uint64_t) json_integer_value (entry);
for (size_t i = 0; i < num_categories; i++)
{
@@ -976,713 +926,595 @@ categories_selected (const json_t *selected_categories,
/**
- * Compute the line total for a product based on quantity.
+ * Verify request data for inventory templates.
+ * Checks that the selected products are allowed
+ * for this template.
*
- * @param unit_price price per unit
- * @param quantity integer quantity
- * @param quantity_frac fractional quantity (0..TALER_MERCHANT_UNIT_FRAC_BASE-1)
- * @param[out] line_total resulting line total
+ * @param[in,out] uc use context
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-compute_line_total (const struct TALER_Amount *unit_price,
- uint64_t quantity,
- uint32_t quantity_frac,
- struct TALER_Amount *line_total)
+verify_using_templates_inventory (struct UseContext *uc)
{
- struct TALER_Amount tmp;
-
- if (GNUNET_OK !=
- TALER_amount_set_zero (unit_price->currency,
- line_total))
+ if (uc->parse_template.inventory.choose_one &&
+ (1 != uc->parse_request.inventory.items_len))
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_line_total: failed to init total for currency %s\n",
- unit_price->currency);
+ GNUNET_break_op (0);
+ use_reply_with_error (uc,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "inventory_selection");
return GNUNET_SYSERR;
}
- if (0 != quantity)
- {
- if (0 >
- TALER_amount_multiply (line_total,
- unit_price,
- (uint32_t) quantity))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_line_total: multiply failed for quantity %lu"
- " in %s\n",
- quantity,
- unit_price->currency);
- return GNUNET_SYSERR;
- }
- }
- if (0 != quantity_frac)
+ if (uc->parse_template.inventory.selected_all)
+ return GNUNET_OK;
+ for (unsigned int i = 0;
+ i < uc->parse_request.inventory.items_len;
+ i++)
{
- if (0 >
- TALER_amount_multiply (&tmp,
- unit_price,
- quantity_frac))
+ struct InventoryTemplateItemContext *item =
+ &uc->parse_request.inventory.items[i];
+ const char *eparam = NULL;
+
+ if (GNUNET_OK !=
+ TALER_MERCHANT_vk_process_quantity_inputs (
+ TALER_MERCHANT_VK_QUANTITY,
+ item->pd.allow_fractional_quantity,
+ true,
+ 0,
+ false,
+ item->unit_quantity,
+ &item->quantity_value,
+ &item->quantity_frac,
+ &eparam))
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_line_total: frac multiply failed for frac %u in %s\n",
- quantity_frac,
- unit_price->currency);
+ GNUNET_break_op (0);
+ use_reply_with_error (uc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ eparam);
return GNUNET_SYSERR;
}
- TALER_amount_divide (&tmp,
- &tmp,
- TALER_MERCHANT_UNIT_FRAC_BASE);
- if (0 >
- TALER_amount_add (line_total,
- line_total,
- &tmp))
+
+ if (0 == item->pd.price_array_length)
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_line_total: frac add failed in %s\n",
- unit_price->currency);
+ GNUNET_break (0);
+ use_reply_with_error (uc,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "price_array");
return GNUNET_SYSERR;
}
}
+
+ for (unsigned int i = 0;
+ i < uc->parse_request.inventory.items_len;
+ i++)
+ {
+ struct InventoryTemplateItemContext *item =
+ &uc->parse_request.inventory.items[i];
+
+ if (product_id_allowed (uc->parse_template.inventory.selected_products,
+ item->product_id))
+ continue;
+ if (category_allowed (
+ uc->parse_template.inventory.selected_categories,
+ item->num_categories,
+ item->categories))
+ continue;
+ GNUNET_break_op (0);
+ use_reply_with_error (
+ uc,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED, /* FIXME: bad EC! */
+ item->product_id);
+ return GNUNET_SYSERR;
+ }
return GNUNET_OK;
}
/**
- * Compute totals for all currencies shared across selected products.
+ * Verify request data for fixed-order templates.
+ * As here we cannot compute the total amount, either
+ * the template or the client request must provide it.
*
* @param[in,out] uc use context
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-compute_totals_per_currency (struct UseContext *uc)
+verify_using_templates_fixed (
+ struct UseContext *uc)
{
- struct TALER_Amount line_total;
- struct InventoryTemplateItemContext *items =
- uc->parse_request.template.inventory.items;
- unsigned int items_len = uc->parse_request.template.inventory.items_len;
-
- if (0 == items_len)
+ if ( (! uc->parse_request.no_amount) &&
+ (! uc->parse_template.no_tamount) )
{
- GNUNET_break (0);
+ GNUNET_break_op (0);
+ use_reply_with_error (uc,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
+ NULL);
return GNUNET_SYSERR;
}
-
- for (size_t i = 0; i < items[0].pd.price_array_length; i++)
- {
- const char *currency = items[0].pd.price_array[i].currency;
-
- if (! TMH_test_exchange_configured_for_currency (currency))
- continue;
-
- GNUNET_array_append (uc->verify.totals,
- uc->verify.totals_len,
- (struct TALER_Amount) { 0 });
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (
- currency,
- &uc->verify.totals[uc->verify.totals_len - 1]));
- }
-
- if (0 == uc->verify.totals_len)
- return GNUNET_OK;
-
- /* Loop through items, ensure each currency exists and sum totals. */
- for (unsigned int i = 0; i < items_len; i++)
+ if (uc->parse_request.no_amount &&
+ uc->parse_template.no_tamount)
{
- const struct InventoryTemplateItemContext *item = &items[i];
- for (unsigned int c = 0; c < uc->verify.totals_len;)
- {
- const struct TALER_Amount *unit_price = NULL;
-
- for (size_t j = 0; j < item->pd.price_array_length; j++)
- {
- if (0 == strcmp (item->pd.price_array[j].currency,
- uc->verify.totals[c].currency))
- {
- unit_price = &item->pd.price_array[j];
- break;
- }
- }
- if (NULL == unit_price)
- {
- /* Just drop the currency, that we can't find in one of the items*/
- uc->verify.totals[c] = uc->verify.totals[uc->verify.totals_len - 1];
- uc->verify.totals_len--;
- continue;
- }
- if (GNUNET_OK !=
- compute_line_total (unit_price,
- item->quantity_value,
- item->quantity_frac,
- &line_total))
- return GNUNET_SYSERR;
- if (0 >
- TALER_amount_add (&uc->verify.totals[c],
- &uc->verify.totals[c],
- &line_total))
- return GNUNET_SYSERR;
- c++;
- }
+ GNUNET_break_op (0);
+ use_reply_with_error (uc,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_AMOUNT,
+ NULL);
+ return GNUNET_SYSERR;
}
-
- return (0 == uc->verify.totals_len)
- ? GNUNET_SYSERR // FIXME: at least some logging would be nice...
- : GNUNET_OK;
+ return GNUNET_OK;
}
/**
- * Compute total for a specific currency.
+ * Verify request data for paivana templates.
*
- * @param[in] items inventory items
- * @param[in] items_len length of @a items
- * @param[in] currency currency to total
- * @param[out] total computed total
+ * @param[in,out] uc use context
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-compute_inventory_total (const struct InventoryTemplateItemContext *items,
- unsigned int items_len,
- const char *currency,
- struct TALER_Amount *total)
+verify_using_templates_paivana (
+ struct UseContext *uc)
{
- struct TALER_Amount line_total;
+ /* TODO: PAIVANA include paivana-specific verification. */
+ return verify_using_templates_fixed (uc);
+}
+
+
+/**
+ * Verify that the client request is structurally acceptable for the specified
+ * template. Does NOT check the total amount being reasonable.
+ *
+ * @param[in,out] uc use context
+ */
+static void
+handle_phase_verify (
+ struct UseContext *uc)
+{
+ enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
- if (NULL == currency)
+ if ( (NULL != uc->parse_request.summary) &&
+ (NULL != uc->parse_template.tsummary) )
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_inventory_total: currency is NULL\n");
- return GNUNET_SYSERR;
+ GNUNET_break_op (0);
+ use_reply_with_error (uc,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_POST_USING_TEMPLATES_SUMMARY_CONFLICT_TEMPLATES_CONTRACT_SUBJECT,
+ NULL);
+ return;
}
- if (GNUNET_OK !=
- TALER_amount_set_zero (currency,
- total))
+ if ( (NULL == uc->parse_request.summary) &&
+ (NULL == uc->parse_template.tsummary) )
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_inventory_total: failed to init total for %s\n",
- currency);
- return GNUNET_SYSERR;
+ GNUNET_break_op (0);
+ use_reply_with_error (uc,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_SUMMARY,
+ NULL);
+ return;
}
- for (unsigned int i = 0; i < items_len; i++)
+ if ( (! uc->parse_request.no_amount) &&
+ (NULL != uc->parse_template.tcurrency) &&
+ (0 != strcasecmp (uc->parse_template.tcurrency,
+ uc->parse_request.amount.currency)) )
{
- const struct InventoryTemplateItemContext *item = &items[i];
- const struct TALER_Amount *unit_price = NULL;
-
- for (size_t j = 0; j < item->pd.price_array_length; j++)
- {
- if (0 == strcmp (item->pd.price_array[j].currency,
- currency))
- {
- unit_price = &item->pd.price_array[j];
- break;
- }
- }
- if (NULL == unit_price)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_inventory_total: missing price in %s for %s\n",
- currency,
- item->product_id);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- compute_line_total (unit_price,
- item->quantity_value,
- item->quantity_frac,
- &line_total))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_inventory_total: line total failed for %s in %s\n",
- item->product_id,
- currency);
- return GNUNET_SYSERR;
- }
- if (0 >
- TALER_amount_add (total,
- total,
- &line_total))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "compute_inventory_total: add failed for %s in %s\n",
- item->product_id,
- currency);
- return GNUNET_SYSERR;
- }
+ GNUNET_break_op (0);
+ use_reply_with_error (uc,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
+ uc->parse_template.tcurrency);
+ return;
}
-
- return GNUNET_OK;
+ switch (uc->template_type)
+ {
+ case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
+ res = verify_using_templates_fixed (uc);
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
+ /* TODO: PAIVANA handle paivana-specific instantiation. */
+ res = verify_using_templates_paivana (uc);
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
+ res = verify_using_templates_inventory (uc);
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
+ GNUNET_assert (0);
+ }
+ if (GNUNET_OK == res)
+ uc->phase++;
}
-/* ***************** USE_PHASE_VERIFY **************** */
+/* ***************** USE_PHASE_COMPUTE_PRICE **************** */
+
/**
- * Verify request data for inventory templates.
+ * Compute the line total for a product based on quantity.
*
- * @param connection connection to reply on
- * @param uc use context
+ * @param unit_price price per unit
+ * @param quantity integer quantity
+ * @param quantity_frac fractional quantity (0..TALER_MERCHANT_UNIT_FRAC_BASE-1)
+ * @param[out] line_total resulting line total
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-verify_using_templates_inventory (struct MHD_Connection *connection,
- struct UseContext *uc)
+compute_line_total (const struct TALER_Amount *unit_price,
+ uint64_t quantity,
+ uint32_t quantity_frac,
+ struct TALER_Amount *line_total)
{
- struct GNUNET_JSON_Specification tcspec[] = {
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("summary",
- &uc->verify.template.inventory.tsummary),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const ("selected_categories",
- &uc->db_fetch.inventory.selected_categories),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const ("selected_products",
- &uc->db_fetch.inventory.selected_products),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_bool ("choose_one",
- &uc->verify.template.inventory.choose_one),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_bool ("request_tip",
- &uc->verify.template.inventory.request_tip),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_bool ("selected_all",
- &uc->db_fetch.inventory.selected_all),
- NULL),
- GNUNET_JSON_spec_end ()
- };
- const char *err_name;
- unsigned int err_line;
- enum GNUNET_GenericReturnValue res;
+ struct TALER_Amount tmp;
- res = GNUNET_JSON_parse (uc->db_fetch.etp.template_contract,
- tcspec,
- &err_name,
- &err_line);
- if (GNUNET_OK != res)
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (unit_price->currency,
+ line_total));
+ if ( (0 != quantity) &&
+ (0 >
+ TALER_amount_multiply (line_total,
+ unit_price,
+ (uint32_t) quantity)) )
{
GNUNET_break (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- err_name);
return GNUNET_SYSERR;
}
-
- if ( (NULL != uc->parse_request.summary) &&
- (NULL != uc->verify.template.inventory.tsummary) )
+ if (0 == quantity_frac)
+ return GNUNET_OK;
+ if (0 >
+ TALER_amount_multiply (&tmp,
+ unit_price,
+ quantity_frac))
{
- GNUNET_break_op (0);
- use_reply_with_error (
- uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_POST_USING_TEMPLATES_SUMMARY_CONFLICT_TEMPLATES_CONTRACT_SUBJECT,
- NULL);
+ GNUNET_break (0);
return GNUNET_SYSERR;
}
- if ( (NULL == uc->parse_request.summary) &&
- (NULL == uc->verify.template.inventory.tsummary) )
+ TALER_amount_divide (&tmp,
+ &tmp,
+ TALER_MERCHANT_UNIT_FRAC_BASE);
+ if (0 >
+ TALER_amount_add (line_total,
+ line_total,
+ &tmp))
{
- GNUNET_break_op (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_SUMMARY,
- NULL);
+ GNUNET_break (0);
return GNUNET_SYSERR;
}
- uc->verify.template.inventory.no_summary = (NULL ==
- uc->parse_request.summary);
+ return GNUNET_OK;
+}
- if (! uc->parse_request.template.inventory.no_tip &&
- ! uc->verify.template.inventory.request_tip)
+
+/**
+ * Find the price of the given @a item in the specified
+ * @a currency.
+ *
+ * @param currency currency to search price in
+ * @param item item to check prices of
+ * @return NULL if a suitable price was not found
+ */
+static const struct TALER_Amount *
+find_item_price_in_currency (
+ const char *currency,
+ const struct InventoryTemplateItemContext *item)
+{
+ for (size_t j = 0; j < item->pd.price_array_length; j++)
{
- GNUNET_break_op (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "tip");
- return GNUNET_SYSERR;
+ if (0 == strcasecmp (item->pd.price_array[j].currency,
+ currency))
+ return &item->pd.price_array[j];
}
+ return NULL;
+}
+
- if (uc->verify.template.inventory.choose_one &&
- (1 != uc->parse_request.template.inventory.items_len))
+/**
+ * Compute totals for all currencies shared across selected products.
+ *
+ * @param[in,out] uc use context
+ * @return #GNUNET_OK on success (including no price due to no items)
+ * #GNUNET_NO if we could not find a price in any accepted currency
+ * for all selected products
+ * #GNUNET_SYSERR on arithmetic issues (internal error)
+ */
+static enum GNUNET_GenericReturnValue
+compute_totals_per_currency (struct UseContext *uc)
+{
+ const struct InventoryTemplateItemContext *items
+ = uc->parse_request.inventory.items;
+ unsigned int items_len = uc->parse_request.inventory.items_len;
+
+ if (0 == items_len)
+ return GNUNET_OK;
+ for (size_t i = 0; i < items[0].pd.price_array_length; i++)
{
- GNUNET_break_op (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "inventory_selection");
- return GNUNET_SYSERR;
- }
+ const struct TALER_Amount *price
+ = &items[0].pd.price_array[i];
+ struct TALER_Amount zero;
- for (unsigned int i = 0; i < uc->parse_request.template.inventory.items_len;
- i++)
+ if (! TMH_test_exchange_configured_for_currency (price->currency))
+ continue;
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (price->currency,
+ &zero));
+ GNUNET_array_append (uc->compute_price.totals,
+ uc->compute_price.totals_len,
+ zero);
+ }
+ if (0 == uc->compute_price.totals_len)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "No currency supported by our configuration in which we have prices for first selected product!\n");
+ return GNUNET_NO;
+ }
+ /* Loop through items, ensure each currency exists and sum totals. */
+ for (unsigned int i = 0; i < items_len; i++)
{
- struct InventoryTemplateItemContext *item =
- &uc->parse_request.template.inventory.items[i];
- const char *eparam = NULL;
+ const struct InventoryTemplateItemContext *item = &items[i];
+ unsigned int c = 0;
- if (! uc->db_fetch.inventory.selected_all)
+ while (c < uc->compute_price.totals_len)
{
- bool allowed = false;
+ struct TALER_Amount *total = &uc->compute_price.totals[c];
+ const struct TALER_Amount *unit_price;
+ struct TALER_Amount line_total;
- if (product_id_selected (uc->db_fetch.inventory.selected_products,
- item->product_id))
- allowed = true;
- else if (categories_selected (uc->db_fetch.inventory.selected_categories,
- item->num_categories,
- item->categories))
- allowed = true;
-
- if (! allowed)
+ unit_price = find_item_price_in_currency (total->currency,
+ item);
+ if (NULL == unit_price)
+ {
+ /* Drop the currency: we have no price in one of
+ the selected products */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Product `%s' has no price in %s: dropping currency\n",
+ item->product_id,
+ total->currency);
+ *total = uc->compute_price.totals[--uc->compute_price.totals_len];
+ continue;
+ }
+ if (GNUNET_OK !=
+ compute_line_total (unit_price,
+ item->quantity_value,
+ item->quantity_frac,
+ &line_total))
{
- use_reply_with_error (
- uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "inventory_selection(selected product is not available for this template)");
+ GNUNET_break (0);
return GNUNET_SYSERR;
}
- }
-
- if (GNUNET_OK !=
- TALER_MERCHANT_vk_process_quantity_inputs (
- TMH_VK_QUANTITY,
- item->pd.allow_fractional_quantity,
- true,
- 0,
- false,
- item->unit_quantity,
- &item->quantity_value,
- &item->quantity_frac,
- &eparam))
- {
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- eparam);
- return GNUNET_SYSERR;
- }
-
- if (0 == item->pd.price_array_length)
- {
- GNUNET_break (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "price_array");
- return GNUNET_SYSERR;
+ if (0 >
+ TALER_amount_add (total,
+ total,
+ &line_total))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ c++;
}
}
-
- if (uc->parse_request.template.inventory.no_amount &&
- uc->parse_request.template.inventory.no_tip)
+ if (0 == uc->compute_price.totals_len)
{
- if (GNUNET_OK !=
- compute_totals_per_currency (uc))
- {
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "calculation of currency totals failed");
- return GNUNET_SYSERR;
- }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "No currency available in which we have prices for all selected products!\n");
+ GNUNET_free (uc->compute_price.totals);
}
+ return (0 == uc->compute_price.totals_len)
+ ? GNUNET_NO
+ : GNUNET_OK;
+}
- if (! uc->parse_request.template.inventory.no_amount ||
- ! uc->parse_request.template.inventory.no_tip)
+
+/**
+ * Compute total for only the given @a currency.
+ *
+ * @param items_len length of @a items
+ * @param items inventory items
+ * @param currency currency to total
+ * @param[out] total computed total
+ * @return #GNUNET_OK on success
+ * #GNUNET_NO if we could not find a price in any accepted currency
+ * for all selected products
+ * #GNUNET_SYSERR on arithmetic issues (internal error)
+ */
+static enum GNUNET_GenericReturnValue
+compute_inventory_total (unsigned int items_len,
+ const struct InventoryTemplateItemContext *items,
+ const char *currency,
+ struct TALER_Amount *total)
+{
+ GNUNET_assert (NULL != currency);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (currency,
+ total));
+ for (unsigned int i = 0; i < items_len; i++)
{
- const char *primary_currency =
- uc->parse_request.template.inventory.no_amount
- ? uc->parse_request.template.inventory.tip.currency
- : uc->parse_request.template.inventory.amount.currency;
-
- GNUNET_array_append (uc->verify.totals,
- uc->verify.totals_len,
- (struct TALER_Amount) { 0 });
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (primary_currency,
- &uc->verify.totals[0]));
- if (GNUNET_OK !=
- compute_inventory_total (uc->parse_request.template.inventory.items,
- uc->parse_request.template.inventory.items_len,
- primary_currency,
- &uc->verify.totals[0]))
+ const struct InventoryTemplateItemContext *item = &items[i];
+ const struct TALER_Amount *unit_price;
+ struct TALER_Amount line_total;
+
+ unit_price = find_item_price_in_currency (currency,
+ item);
+ if (NULL == unit_price)
{
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "total amount calculation failed");
- return GNUNET_SYSERR;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "compute_inventory_total: no price in %s for product `%s'\n",
+ currency,
+ item->product_id);
+ return GNUNET_NO;
}
- }
-
- if (! uc->parse_request.template.inventory.no_tip)
- {
- if (GNUNET_YES !=
- TALER_amount_cmp_currency (&uc->parse_request.template.inventory.tip,
- &uc->verify.totals[0]))
+ if (GNUNET_OK !=
+ compute_line_total (unit_price,
+ item->quantity_value,
+ item->quantity_frac,
+ &line_total))
{
- /* YOU NEVER SUPPOSED TO REACH IT */
- GNUNET_break_op (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
- uc->parse_request.template.inventory.tip.currency);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "compute_inventory_total: line total failed for %s in %s\n",
+ item->product_id,
+ currency);
return GNUNET_SYSERR;
}
if (0 >
- TALER_amount_add (&uc->verify.totals[0],
- &uc->verify.totals[0],
- &uc->parse_request.template.inventory.tip))
+ TALER_amount_add (total,
+ total,
+ &line_total))
{
GNUNET_break (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
- "tip");
return GNUNET_SYSERR;
}
}
-
- if (! uc->parse_request.template.inventory.no_amount &&
- (0 != TALER_amount_cmp (&uc->parse_request.template.inventory.amount,
- &uc->verify.totals[0])))
- {
- GNUNET_break_op (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
- NULL);
- return GNUNET_SYSERR;
- }
return GNUNET_OK;
}
/**
- * Verify request data for fixed-order templates.
+ * Compute total price.
*
- * @param connection connection to reply on
- * @param uc use context
- * @return #GNUNET_OK on success
+ * @param[in,out] uc use context
*/
-static enum GNUNET_GenericReturnValue
-verify_using_templates_fixed (struct MHD_Connection *connection,
- struct UseContext *uc)
+static void
+handle_phase_compute_price (struct UseContext *uc)
{
- uint32_t min_age;
- struct GNUNET_TIME_Relative pay_duration;
- struct GNUNET_JSON_Specification tspec[] = {
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("summary",
- &uc->verify.template.fixed.tsummary),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("currency",
- &uc->verify.template.fixed.tcurrency),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount_any ("amount",
- &uc->verify.template.fixed.tamount),
- &uc->verify.template.fixed.no_tamount),
- GNUNET_JSON_spec_uint32 ("minimum_age",
- &min_age),
- GNUNET_JSON_spec_relative_time ("pay_duration",
- &pay_duration),
- GNUNET_JSON_spec_end ()
- };
- enum GNUNET_GenericReturnValue res;
- const char *err_name;
- unsigned int err_line;
+ const char *primary_currency;
+ enum GNUNET_GenericReturnValue ret;
- /* USELESS STUFF FROM AI
- memset (&uc->verify.template.fixed,
- 0,
- sizeof (uc->verify.template.fixed));
- */
-
- res = GNUNET_JSON_parse (uc->db_fetch.etp.template_contract,
- tspec,
- &err_name,
- &err_line);
- if (GNUNET_OK != res)
+ switch (uc->template_type)
{
- GNUNET_break (0);
- json_dumpf (uc->db_fetch.etp.template_contract,
- stderr,
- JSON_INDENT (2));
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- err_name);
- return GNUNET_SYSERR;
+ case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
+ uc->phase++;
+ return;
+ case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
+ uc->phase++;
+ return;
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
+ break;
+ case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
+ GNUNET_assert (0);
}
-
- if ( (! uc->parse_request.template.fixed.no_amount) &&
- (! uc->verify.template.fixed.no_tamount) )
+ primary_currency = uc->parse_template.tcurrency;
+ if (! uc->parse_request.no_amount)
+ primary_currency = uc->parse_request.amount.currency;
+ if (! uc->parse_request.no_tip)
+ primary_currency = uc->parse_request.tip.currency;
+ if (NULL == primary_currency)
{
- GNUNET_break_op (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
- NULL);
- return GNUNET_SYSERR;
+ ret = compute_totals_per_currency (uc);
}
-
- if ( (! uc->parse_request.template.fixed.no_amount) &&
- (NULL != uc->verify.template.fixed.tcurrency) &&
- (0 != strcmp (uc->verify.template.fixed.tcurrency,
- uc->parse_request.template.fixed.amount.currency)) )
+ else
+ {
+ uc->compute_price.totals
+ = GNUNET_new (struct TALER_Amount);
+ uc->compute_price.totals_len
+ = 1;
+ ret = compute_inventory_total (uc->parse_request.inventory.items_len,
+ uc->parse_request.inventory.items,
+ primary_currency,
+ uc->compute_price.totals);
+ }
+ if (GNUNET_SYSERR == ret)
{
- GNUNET_break_op (0);
use_reply_with_error (uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
- uc->verify.template.fixed.tcurrency);
- return GNUNET_SYSERR;
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, // FIXME: bad EC, use arithmetic error
+ "calculation of currency totals failed");
+ return;
}
-
- if (uc->parse_request.template.fixed.no_amount &&
- uc->verify.template.fixed.no_tamount)
+ if (GNUNET_NO == ret)
{
- GNUNET_break_op (0);
use_reply_with_error (uc,
- connection,
MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_AMOUNT,
- NULL);
- return GNUNET_SYSERR;
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, // FIXME: bad EC!
+ "failed to find prices in any acceptable currency for all selected products");
+ return;
}
- if (! uc->parse_request.template.fixed.no_tip)
+ uc->phase++;
+}
+
+
+/* ***************** USE_PHASE_CHECK_TIP **************** */
+
+
+/**
+ * Check that tip specified is reasonable and add to total.
+ *
+ * @param[in,out] uc use context
+ */
+static void
+handle_phase_check_tip (struct UseContext *uc)
+{
+ struct TALER_Amount *total_amount;
+
+ if (uc->parse_request.no_tip)
{
- const struct TALER_Amount *total_amount;
-
- total_amount = uc->parse_request.template.fixed.no_amount
- ? &uc->verify.template.fixed.tamount
- : &uc->parse_request.template.fixed.amount;
- if (GNUNET_YES !=
- TALER_amount_cmp_currency (&uc->parse_request.template.fixed.tip,
- total_amount))
- {
- GNUNET_break_op (0);
- use_reply_with_error (uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
- uc->parse_request.template.fixed.tip.currency);
- return GNUNET_SYSERR;
- }
+ uc->phase++;
+ return;
}
-
- if ( (NULL != uc->parse_request.summary) &&
- (NULL != uc->verify.template.fixed.tsummary) )
+ // FIXME: not quite right if we do not have compute_price total!
+ total_amount = &uc->compute_price.totals[0];
+ if (GNUNET_YES !=
+ TALER_amount_cmp_currency (&uc->parse_request.tip,
+ total_amount))
{
GNUNET_break_op (0);
use_reply_with_error (uc,
- connection,
MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_POST_USING_TEMPLATES_SUMMARY_CONFLICT_TEMPLATES_CONTRACT_SUBJECT,
- NULL);
- return GNUNET_SYSERR;
+ TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH,
+ uc->parse_request.tip.currency);
+ return;
}
-
- if ( (NULL == uc->parse_request.summary) &&
- (NULL == uc->verify.template.fixed.tsummary) )
+ if (0 >
+ TALER_amount_add (total_amount,
+ total_amount,
+ &uc->parse_request.tip))
{
- GNUNET_break_op (0);
+ GNUNET_break (0);
use_reply_with_error (uc,
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_SUMMARY,
- NULL);
- return GNUNET_SYSERR;
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
+ "tip");
+ return;
}
- uc->verify.template.fixed.no_summary = (NULL == uc->parse_request.summary);
-
- return GNUNET_OK;
+ uc->phase++;
}
+/* ***************** USE_PHASE_CHECK_TOTAL **************** */
+
/**
- * Verify request data for paivana templates.
+ * Check that if the client specified a total,
+ * it matches our own calculation.
*
- * @param connection connection to reply on
- * @param uc use context
- * @return #GNUNET_OK on success
+ * @param[in,out] uc use context
*/
-static enum GNUNET_GenericReturnValue
-verify_using_templates_paivana (struct MHD_Connection *connection,
- struct UseContext *uc)
-{
- /* TODO: PAIVANA include paivana-specific verification. */
- return verify_using_templates_fixed (connection,
- uc);
-}
-
-
static void
-handle_phase_verify (struct MHD_Connection *connection,
- struct UseContext *uc)
+handle_phase_check_total (struct UseContext *uc)
{
- enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
-
- switch (uc->template_type)
+ // FIXME: not quite right if we do not have compute_price total!
+ if (! uc->parse_request.no_amount &&
+ (0 != TALER_amount_cmp (&uc->parse_request.amount,
+ &uc->compute_price.totals[0])))
{
- case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
- res = verify_using_templates_fixed (connection,
- uc);
- break;
- case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
- /* TODO: PAIVANA handle paivana-specific instantiation. */
- res = verify_using_templates_paivana (connection,
- uc);
- break;
- case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
- res = verify_using_templates_inventory (connection,
- uc);
- break;
- case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
- GNUNET_break (0);
- use_reply_with_error (
- uc,
- connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "template_contract has unexpected type");
- break;
+ GNUNET_break_op (0);
+ use_reply_with_error (uc,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT,
+ NULL);
+ return;
}
- if (GNUNET_OK == res)
- uc->phase = USE_PHASE_CREATE_ORDER;
+ uc->phase++;
}
/* ***************** USE_PHASE_CREATE_ORDER **************** */
+// FIXME: review entirely from here...
+
+
/**
* Create order request for inventory templates.
*
- * @param connection connection to reply on
* @param uc use context
* @return #GNUNET_OK on success
*/
@@ -1691,15 +1523,16 @@ create_using_templates_inventory (struct UseContext *uc)
{
json_t *inventory_products;
json_t *tip_products = NULL;
- const bool multi_choices = uc->parse_request.template.inventory.no_amount &&
- uc->parse_request.template.inventory.no_tip;
+ const bool multi_choices = uc->parse_request.no_amount &&
+ uc->parse_request.no_tip;
if (NULL != uc->ihc.request_body)
return GNUNET_OK;
- if (! uc->parse_request.template.inventory.no_tip)
+ if (! uc->parse_request.no_tip)
{
tip_products = json_array ();
+ GNUNET_assert (NULL != tip_products);
GNUNET_assert (0 ==
json_array_append_new (
tip_products,
@@ -1710,15 +1543,15 @@ create_using_templates_inventory (struct UseContext *uc)
1),
TALER_JSON_pack_amount (
"price",
- &uc->parse_request.template.inventory.tip))));
+ &uc->parse_request.tip))));
}
inventory_products = json_array ();
GNUNET_assert (NULL != inventory_products);
- for (size_t i = 0; i < uc->parse_request.template.inventory.items_len; i++)
+ for (size_t i = 0; i < uc->parse_request.inventory.items_len; i++)
{
const struct InventoryTemplateItemContext *item =
- &uc->parse_request.template.inventory.items[i];
+ &uc->parse_request.inventory.items[i];
GNUNET_assert (0 ==
json_array_append_new (
@@ -1743,25 +1576,27 @@ create_using_templates_inventory (struct UseContext *uc)
choices,
GNUNET_JSON_PACK (
TALER_JSON_pack_amount ("amount",
- &uc->verify.totals[0]))));
+ &uc->compute_price.totals[0])))
+ );
}
else
{
- for (size_t i = 0; i < uc->verify.totals_len; i++)
+ for (size_t i = 0; i < uc->compute_price.totals_len; i++)
{
GNUNET_assert (0 ==
json_array_append_new (
choices,
GNUNET_JSON_PACK (
TALER_JSON_pack_amount ("amount",
- &uc->verify.totals[i]))));
+ &uc->compute_price.totals[i])
+ )));
}
}
body = GNUNET_JSON_PACK (
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("otp_id",
- uc->db_fetch.etp.otp_id)),
+ uc->lookup_template.etp.otp_id)),
GNUNET_JSON_pack_array_steal ("inventory_products",
inventory_products),
GNUNET_JSON_pack_object_steal (
@@ -1772,8 +1607,8 @@ create_using_templates_inventory (struct UseContext *uc)
GNUNET_JSON_pack_array_steal ("choices",
choices),
GNUNET_JSON_pack_string ("summary",
- uc->verify.template.inventory.no_summary
- ? uc->verify.template.inventory.tsummary
+ NULL == uc->parse_request.summary
+ ? uc->parse_template.tsummary
: uc->parse_request.summary),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("fulfillment_url",
@@ -1787,10 +1622,6 @@ create_using_templates_inventory (struct UseContext *uc)
GNUNET_assert (NULL != body);
uc->ihc.request_body = body;
}
- cleanup_inventory_items (&uc->parse_request.template.inventory.items,
- &uc->parse_request.template.inventory.items_len);
- cleanup_inventory_totals (&uc->verify.totals,
- &uc->verify.totals_len);
return GNUNET_OK;
}
@@ -1809,7 +1640,7 @@ create_using_templates_fixed (struct UseContext *uc)
if (NULL != uc->ihc.request_body)
return GNUNET_OK;
- if (! uc->parse_request.template.fixed.no_tip)
+ if (! uc->parse_request.no_tip)
{
tip_products = json_array ();
GNUNET_assert (NULL != tip_products);
@@ -1823,7 +1654,7 @@ create_using_templates_fixed (struct UseContext *uc)
1),
TALER_JSON_pack_amount (
"price",
- &uc->parse_request.template.fixed.tip))));
+ &uc->parse_request.tip))));
}
{
json_t *body;
@@ -1831,19 +1662,19 @@ create_using_templates_fixed (struct UseContext *uc)
body = GNUNET_JSON_PACK (
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("otp_id",
- uc->db_fetch.etp.otp_id)),
+ uc->lookup_template.etp.otp_id)),
GNUNET_JSON_pack_object_steal (
"order",
GNUNET_JSON_PACK (
TALER_JSON_pack_amount (
"amount",
- uc->parse_request.template.fixed.no_amount
- ? &uc->verify.template.fixed.tamount
- : &uc->parse_request.template.fixed.amount),
+ uc->parse_request.no_amount
+ ? &uc->parse_template.tamount
+ : &uc->parse_request.amount),
GNUNET_JSON_pack_string (
"summary",
- uc->verify.template.fixed.no_summary
- ? uc->verify.template.fixed.tsummary
+ NULL == uc->parse_request.summary
+ ? uc->parse_template.tsummary
: uc->parse_request.summary),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("fulfillment_url",
@@ -1876,8 +1707,7 @@ create_using_templates_paivana (struct UseContext *uc)
static void
-handle_phase_create_order (struct MHD_Connection *connection,
- struct UseContext *uc)
+handle_phase_create_order (struct UseContext *uc)
{
enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
@@ -1897,14 +1727,13 @@ handle_phase_create_order (struct MHD_Connection *connection,
GNUNET_break (0);
use_reply_with_error (
uc,
- connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
"template_contract has unexpected type");
break;
}
if (GNUNET_OK == res)
- uc->phase = USE_PHASE_SUBMIT_ORDER;
+ uc->phase++;
}
@@ -1920,6 +1749,7 @@ TMH_post_using_templates_ID (const struct TMH_RequestHandler *rh,
if (NULL == uc)
{
uc = GNUNET_new (struct UseContext);
+ uc->hc = hc;
hc->ctx = uc;
hc->cc = &cleanup_use_context;
uc->ihc.instance = hc->instance;
@@ -1932,27 +1762,31 @@ TMH_post_using_templates_ID (const struct TMH_RequestHandler *rh,
switch (uc->phase)
{
case USE_PHASE_PARSE_REQUEST:
- handle_phase_parse_request (connection,
- hc,
- uc);
+ handle_phase_parse_request (uc);
break;
case USE_PHASE_LOOKUP_TEMPLATE:
- handle_phase_lookup_template (connection,
- hc,
- uc);
+ handle_phase_lookup_template (uc);
+ break;
+ case USE_PHASE_PARSE_TEMPLATE:
+ handle_phase_parse_template (uc);
break;
case USE_PHASE_DB_FETCH:
- handle_phase_db_fetch (connection,
- hc,
- uc);
+ handle_phase_db_fetch (uc);
break;
case USE_PHASE_VERIFY:
- handle_phase_verify (connection,
- uc);
+ handle_phase_verify (uc);
+ break;
+ case USE_PHASE_COMPUTE_PRICE:
+ handle_phase_compute_price (uc);
+ break;
+ case USE_PHASE_CHECK_TIP:
+ handle_phase_check_tip (uc);
+ break;
+ case USE_PHASE_CHECK_TOTAL:
+ handle_phase_check_total (uc);
break;
case USE_PHASE_CREATE_ORDER:
- handle_phase_create_order (connection,
- uc);
+ handle_phase_create_order (uc);
break;
case USE_PHASE_SUBMIT_ORDER:
return TMH_private_post_orders (
diff --git a/src/backend/taler-merchant-httpd_private-patch-templates-ID.c b/src/backend/taler-merchant-httpd_private-patch-templates-ID.c
@@ -87,30 +87,6 @@ determine_cause (struct MHD_Connection *connection,
/**
- * Validate template contract based on its type.
- *
- * @param template_contract JSON template contract
- * @return true if valid
- */
-static bool
-validate_template_contract_by_type (const json_t *template_contract)
-{
- /* For some special different post/patch checks
- otherwise change/check the TMH_template */
- switch (TMH_template_type_from_contract (template_contract))
- {
- case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
- case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
- case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
- return TMH_template_contract_valid (template_contract);
- case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
- break;
- }
- return false;
-}
-
-
-/**
* PATCH configuration of an existing instance, given its configuration.
*
* @param rh context of the handler
@@ -157,7 +133,7 @@ TMH_private_patch_templates_ID (const struct TMH_RequestHandler *rh,
: MHD_NO;
}
- if (! validate_template_contract_by_type (tp.template_contract))
+ if (! TMH_template_contract_valid (tp.template_contract))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
diff --git a/src/backend/taler-merchant-httpd_private-post-templates.c b/src/backend/taler-merchant-httpd_private-post-templates.c
@@ -58,30 +58,6 @@ templates_equal (const struct TALER_MERCHANTDB_TemplateDetails *t1,
}
-/**
- * Validate template contract based on its type.
- *
- * @param template_contract JSON template contract
- * @return true if valid
- */
-static bool
-validate_template_contract_by_type (const json_t *template_contract)
-{
- /* For some special different post/patch checks
- otherwise change/check the TMH_template */
- switch (TMH_template_type_from_contract (template_contract))
- {
- case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
- case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
- case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
- return TMH_template_contract_valid (template_contract);
- case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
- break;
- }
- return false;
-}
-
-
MHD_RESULT
TMH_private_post_templates (const struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
@@ -125,7 +101,7 @@ TMH_private_post_templates (const struct TMH_RequestHandler *rh,
: MHD_NO;
}
}
- if (! validate_template_contract_by_type (tp.template_contract))
+ if (! TMH_template_contract_valid (tp.template_contract))
{
GNUNET_break_op (0);
json_dumpf (tp.template_contract,
diff --git a/src/lib/merchant_api_post_templates.c b/src/lib/merchant_api_post_templates.c
@@ -32,7 +32,7 @@
#include "merchant_api_common.h"
#include <taler/taler_json_lib.h>
#include <taler/taler_curl_lib.h>
-#include <taler/taler_merchant_util.h>
+#include "taler_merchant_util.h"
/**
diff --git a/src/lib/merchant_api_post_using_templates.c b/src/lib/merchant_api_post_using_templates.c
@@ -174,6 +174,8 @@ TALER_MERCHANT_using_templates_post (
json_t *req_obj;
req_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("template_type",
+ "fixed-order"),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("summary",
summary)),
diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c
@@ -49,7 +49,7 @@
* commands should NOT wait for this timeout!
*/
#define POLL_ORDER_TIMEOUT \
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
/**
* The 'poll-orders-conclude-1x' and other 'conclude'
@@ -57,7 +57,7 @@
* here we use a short value!
*/
#define POLL_ORDER_SHORT_TIMEOUT \
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2)
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2)
/**
* Configuration file we use. One (big) configuration is used
@@ -288,8 +288,8 @@ cmd_exec_wirewatch (const char *label)
* @param label label to use for the command.
*/
#define CMD_EXEC_AGGREGATOR(label) \
- TALER_TESTING_cmd_exec_aggregator (label "-aggregator", config_file), \
- TALER_TESTING_cmd_exec_transfer (label "-transfer", config_file)
+ TALER_TESTING_cmd_exec_aggregator (label "-aggregator", config_file), \
+ TALER_TESTING_cmd_exec_transfer (label "-transfer", config_file)
/**
@@ -1996,6 +1996,8 @@ run (void *cls,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("amount",
"EUR:1"),
+ GNUNET_JSON_pack_string ("template_type",
+ "inventory-cart"),
GNUNET_JSON_pack_array_steal (
"inventory_selection",
make_inventory_selection ("inv-product-1",
@@ -2030,6 +2032,8 @@ run (void *cls,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("amount",
"EUR:3"),
+ GNUNET_JSON_pack_string ("template_type",
+ "inventory-cart"),
GNUNET_JSON_pack_array_steal (
"inventory_selection",
make_inventory_selection ("inv-product-1",
@@ -2046,6 +2050,8 @@ run (void *cls,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("amount",
"EUR:1"),
+ GNUNET_JSON_pack_string ("template_type",
+ "inventory-cart"),
GNUNET_JSON_pack_array_steal (
"inventory_selection",
json_array ())),
@@ -2059,6 +2065,8 @@ run (void *cls,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("amount",
"EUR:3"),
+ GNUNET_JSON_pack_string ("template_type",
+ "inventory-cart"),
GNUNET_JSON_pack_array_steal (
"inventory_selection",
make_inventory_selection ("inv-product-1",
@@ -2077,6 +2085,8 @@ run (void *cls,
"EUR:1"),
GNUNET_JSON_pack_string ("tip",
"EUR:1"),
+ GNUNET_JSON_pack_string ("template_type",
+ "inventory-cart"),
GNUNET_JSON_pack_array_steal (
"inventory_selection",
make_inventory_selection ("inv-product-1",
@@ -2093,6 +2103,8 @@ run (void *cls,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("amount",
"EUR:1"),
+ GNUNET_JSON_pack_string ("template_type",
+ "inventory-cart"),
GNUNET_JSON_pack_array_steal (
"inventory_selection",
make_inventory_selection ("inv-product-404",
@@ -2127,6 +2139,8 @@ run (void *cls,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("amount",
"EUR:2"),
+ GNUNET_JSON_pack_string ("template_type",
+ "inventory-cart"),
GNUNET_JSON_pack_array_steal (
"inventory_selection",
make_inventory_selection ("inv-product-2",
@@ -2143,6 +2157,8 @@ run (void *cls,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("amount",
"EUR:4"),
+ GNUNET_JSON_pack_string ("template_type",
+ "inventory-cart"),
GNUNET_JSON_pack_array_steal (
"inventory_selection",
make_inventory_selection ("inv-product-1",
@@ -2260,6 +2276,8 @@ run (void *cls,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("amount",
"EUR:3.2"),
+ GNUNET_JSON_pack_string ("template_type",
+ "inventory-cart"),
GNUNET_JSON_pack_array_steal (
"inventory_selection",
make_inventory_selection ("inv-unit-product-1",