aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Blättler <blatc2@bfh.ch>2024-03-18 20:58:28 +0100
committerChristian Blättler <blatc2@bfh.ch>2024-03-18 20:58:28 +0100
commitf4585ef6c7eb890bf3ec345f032e223505a4e2d3 (patch)
tree6bf639ce36b1dab44e3600acaa7799c50362ccde
parent52708604b7cc3bebfb90f4c2800a76fda323d574 (diff)
serialize v1 contract ready for db
-rw-r--r--src/backend/taler-merchant-httpd_private-post-orders.c1157
1 files changed, 694 insertions, 463 deletions
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c
index d8120e91..cf32f886 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders.c
+++ b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -22,6 +22,7 @@
* @brief the POST /orders handler
* @author Christian Grothoff
* @author Marcello Stanisci
+ * @author Christian Blättler
*/
#include "platform.h"
#include <gnunet/gnunet_common.h>
@@ -32,11 +33,13 @@
#include <taler/taler_error_codes.h>
#include <taler/taler_signatures.h>
#include <taler/taler_json_lib.h>
+#include "taler-merchant-httpd.h"
#include "taler-merchant-httpd_private-post-orders.h"
#include "taler-merchant-httpd_exchanges.h"
#include "taler-merchant-httpd_contract.h"
#include "taler-merchant-httpd_helper.h"
#include "taler-merchant-httpd_private-get-orders.h"
+#include "taler_merchantdb_plugin.h"
/**
@@ -238,6 +241,38 @@ struct OrderContext
const char *order_id;
/**
+ * Summary of the contract.
+ */
+ const char *summary;
+
+ /**
+ * Internationalized summary.
+ */
+ json_t *summary_i18n;
+
+ /**
+ * URL that will show that the contract was successful
+ * after it has been paid for.
+ */
+ const char *fulfillment_url;
+
+ /**
+ * Message shown to the customer after paying for the contract.
+ * Either fulfillment_url or fulfillment_message must be specified.
+ */
+ const char *fulfillment_message;
+
+ /**
+ * Map from IETF BCP 47 language tags to localized fulfillment messages.
+ */
+ json_t *fulfillment_message_i18n;
+
+ /**
+ * Array of products that are part of the purchase.
+ */
+ const json_t *products;
+
+ /**
* URL where the same contract could be ordered again (if available).
*/
const char *public_reorder_url;
@@ -248,6 +283,11 @@ struct OrderContext
const json_t *choices;
/**
+ * Maps authority labels to token details. Is null for v0 contracts.
+ */
+ const json_t *token_types;
+
+ /**
* Merchant base URL.
*/
char *merchant_base_url;
@@ -289,26 +329,9 @@ struct OrderContext
struct TALER_Amount brutto;
/**
- * Array of limits per currency. Is null for v0 contracts.
- * See @e max_fees for v0 contracts.
- */
- // const json_t *limits;
-
- /**
- * Maximum fee as given by the client request. Only used in v0 contracts.
- * See @e limits for v1 contracts.
+ * Maximum fee as given by the client request.
*/
- // struct TALER_Amount max_fee;
-
- /**
- * Array of fee limits and wire account details by currency.
- */
- struct TALER_MerchantContractLimits *limits;
-
- /**
- * Length of the @e limits array;
- */
- unsigned int limits_len;
+ struct TALER_Amount max_fee;
/**
* Specifies for how long the wallet should try to get an
@@ -330,6 +353,22 @@ struct OrderContext
} parse_order;
/**
+ * Information set in the ORDER_PHASE_PARSE_TOKEN_TYPES phase.
+ */
+ struct
+ {
+ /**
+ * Array of token types referenced in the contract.
+ */
+ struct TALER_MerchantContractTokenAuthority *authorities;
+
+ /**
+ * Length of the @e authorities array.
+ */
+ unsigned int authorities_len;
+ } parse_token_types;
+
+ /**
* Information set in the ORDER_PHASE_PARSE_CHOICES phase.
*/
struct
@@ -408,15 +447,9 @@ struct OrderContext
struct
{
/**
- * Array of fee limits and wire account details by currency.
- */
- struct TALER_MerchantContractLimits *limits;
-
- /**
- * Length of the @e limits array;
+ * Maximum fee
*/
- unsigned int limits_len;
-
+ struct TALER_Amount max_fee;
} set_max_fee;
/**
@@ -470,6 +503,7 @@ struct OrderContext
{
ORDER_PHASE_PARSE_REQUEST,
ORDER_PHASE_PARSE_ORDER,
+ ORDER_PHASE_PARSE_TOKEN_TYPES,
ORDER_PHASE_PARSE_CHOICES,
ORDER_PHASE_MERGE_INVENTORY,
ORDER_PHASE_ADD_PAYMENT_DETAILS,
@@ -608,24 +642,15 @@ clean_order (void *cls)
json_decref (oc->set_exchanges.exchanges);
oc->set_exchanges.exchanges = NULL;
}
- /* TODO: Clean choices array */
+ // TODO: Check if this is even correct
for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
{
- if (NULL != oc->parse_choices.choices[i].fulfillment_message_i18n)
- {
- json_decref (oc->parse_choices.choices[i].fulfillment_message_i18n);
- oc->parse_choices.choices[i].fulfillment_message_i18n = NULL;
- }
- if (NULL != oc->parse_choices.choices[i].summary_i18n)
- {
- json_decref (oc->parse_choices.choices[i].summary_i18n);
- oc->parse_choices.choices[i].summary_i18n = NULL;
- }
- if (NULL != oc->parse_order.delivery_location)
- {
- json_decref (oc->parse_order.delivery_location);
- oc->parse_order.delivery_location = NULL;
- }
+ GNUNET_array_grow (oc->parse_choices.choices[i].inputs,
+ oc->parse_choices.choices[i].inputs_len,
+ 0);
+ GNUNET_array_grow (oc->parse_choices.choices[i].outputs,
+ oc->parse_choices.choices[i].outputs_len,
+ 0);
}
if (NULL != oc->merge_inventory.products)
{
@@ -1189,9 +1214,6 @@ keys_cb (
struct OrderContext *oc = rx->oc;
const struct TALER_MERCHANTDB_InstanceSettings *settings =
&oc->hc->instance->settings;
- struct TALER_MerchantContractLimits *parsed_limits = oc->parse_order.limits;
-
- GNUNET_assert (1 == oc->parse_order.limits_len && NULL != parsed_limits);
rx->fo = NULL;
GNUNET_CONTAINER_DLL_remove (oc->set_exchanges.pending_reload_head,
@@ -1210,7 +1232,7 @@ keys_cb (
rx->url);
if ( (settings->use_stefan) &&
(GNUNET_OK !=
- TALER_amount_is_valid (&parsed_limits->max_fee)) )
+ TALER_amount_is_valid (&oc->parse_order.max_fee)) )
update_stefan (oc,
keys);
get_acceptable (oc,
@@ -1258,14 +1280,21 @@ get_exchange_keys (void *cls,
rx);
}
+/**
+ * Serialize order into @a oc->serialize_order.contract,
+ * ready to be stored in the database. Upon success, continue
+ * processing with check_contract().
+ *
+ * @param[in,out] oc order context
+ */
static void
-serialize_order_v0 (struct OrderContext *oc)
+serialize_order (struct OrderContext *oc)
{
const struct TALER_MERCHANTDB_InstanceSettings *settings =
&oc->hc->instance->settings;
- struct TALER_MerchantContractChoice *choice = oc->parse_choices.choices;
- struct TALER_MerchantContractLimits *limits = oc->set_max_fee.limits;
json_t *merchant = NULL;
+ json_t *token_types = NULL;
+ json_t *choices = NULL;
{
merchant = GNUNET_JSON_PACK (
@@ -1313,196 +1342,115 @@ serialize_order_v0 (struct OrderContext *oc)
}
}
- // TODO: How to properly handle these cases / errors?
- GNUNET_assert (1 == oc->parse_choices.choices_len && NULL != choice);
- GNUNET_assert (1 == oc->set_max_fee.limits_len && NULL != limits);
-
- oc->serialize_order.contract = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("summary",
- choice->summary),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("summary_i18n",
- choice->summary_i18n)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("public_reorder_url",
- oc->parse_order.public_reorder_url)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("fulfillment_message",
- choice->fulfillment_message)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("fulfillment_message_i18n",
- choice->fulfillment_message_i18n)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("fulfillment_url",
- choice->fulfillment_url)),
- GNUNET_JSON_pack_array_incref ("products",
- oc->merge_inventory.products),
- GNUNET_JSON_pack_data_auto ("h_wire",
- &oc->add_payment_details.wm->h_wire),
- GNUNET_JSON_pack_string ("wire_method",
- oc->add_payment_details.wm->wire_method),
- GNUNET_JSON_pack_string ("order_id",
- oc->parse_order.order_id),
- GNUNET_JSON_pack_timestamp ("timestamp",
- oc->parse_order.timestamp),
- GNUNET_JSON_pack_timestamp ("pay_deadline",
- oc->parse_order.pay_deadline),
- GNUNET_JSON_pack_timestamp ("wire_transfer_deadline",
- oc->parse_order.wire_deadline),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_timestamp ("delivery_date",
- oc->parse_order.delivery_date)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("delivery_location",
- oc->parse_order.delivery_location)),
- GNUNET_JSON_pack_string ("merchant_base_url",
- oc->parse_order.merchant_base_url),
- GNUNET_JSON_pack_object_incref ("merchant",
- merchant),
- GNUNET_JSON_pack_data_auto ("merchant_pub",
- &oc->hc->instance->merchant_pub),
- GNUNET_JSON_pack_array_incref ("exchanges",
- oc->set_exchanges.exchanges),
- TALER_JSON_pack_amount ("max_fee",
- &limits->max_fee),
- TALER_JSON_pack_amount ("amount",
- &oc->parse_order.brutto),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("extra",
- (json_t *) oc->parse_order.extra))
- );
-
- /* Pack does not work here, because it doesn't set zero-values for timestamps */
- GNUNET_assert (0 ==
- json_object_set_new (oc->serialize_order.contract,
- "refund_deadline",
- GNUNET_JSON_from_timestamp (
- oc->parse_order.refund_deadline)));
- GNUNET_log (
- GNUNET_ERROR_TYPE_INFO,
- "Refund deadline for contact is %llu\n",
- (unsigned long long) oc->parse_order.refund_deadline.abs_time.abs_value_us);
- GNUNET_log (
- GNUNET_ERROR_TYPE_INFO,
- "Wallet timestamp for contact is %llu\n",
- (unsigned long long) oc->parse_order.timestamp.abs_time.abs_value_us);
-
- /* Pack does not work here, because it sets zero-values for relative times */
- /* auto_refund should only be set if it is not 0 */
- if (! GNUNET_TIME_relative_is_zero (oc->parse_order.auto_refund))
{
- GNUNET_assert (0 ==
- json_object_set_new (oc->serialize_order.contract,
- "auto_refund",
- GNUNET_JSON_from_time_rel (
- oc->parse_order.auto_refund)));
+ for (unsigned int i = 0; i<oc->parse_token_types.authorities_len; i++)
+ {
+ struct TALER_MerchantContractTokenAuthority *authority = &oc->parse_token_types.authorities[i];
+
+ // TODO: What information about a token family do we want to store in the db?
+ json_t *jauthority = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("summary",
+ authority->summary),
+ GNUNET_JSON_pack_data_auto ("h_pub",
+ &authority->key.public_key.pub_key_hash),
+ GNUNET_JSON_pack_timestamp ("token_expiration",
+ authority->token_expiration)
+ );
+
+ json_object_set_new (token_types, authority->label, jauthority);
+ }
}
-}
-
-static void
-serialize_order_v1 (struct OrderContext *oc)
-{
- const struct TALER_MERCHANTDB_InstanceSettings *settings =
- &oc->hc->instance->settings;
- json_t *merchant = NULL;
- json_t *limits = json_object ();
- json_t *choices = json_array ();
{
- merchant = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("name",
- settings->name),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("website",
- settings->website)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("email",
- settings->email)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("logo",
- settings->logo)));
- GNUNET_assert (NULL != merchant);
+ for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
{
- json_t *loca;
+ struct TALER_MerchantContractChoice *choice = &oc->parse_choices.choices[i];
- /* Handle merchant address */
- loca = settings->address;
- if (NULL != loca)
+ json_t *inputs = NULL;
+ json_t *outputs = NULL;
+
+ for (unsigned int j = 0; j<choice->inputs_len; j++)
{
- loca = json_deep_copy (loca);
- GNUNET_assert (NULL != loca);
- GNUNET_assert (0 ==
- json_object_set_new (merchant,
- "address",
- loca));
+ struct TALER_MerchantContractInput *input = &choice->inputs[j];
+
+ json_t *jinput = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_int64 ("type",
+ input->type)
+ );
+
+ if (TALER_MCIT_TOKEN == input->type)
+ {
+ GNUNET_assert(0 ==
+ json_object_set_new(jinput,
+ "number",
+ json_integer (
+ input->details.token.number)));
+ GNUNET_assert(0 ==
+ json_object_set_new(jinput,
+ "token_authority_label",
+ json_string (
+ input->details.token.token_authority_label)));
+ }
+
+ json_array_append_new (inputs, jinput);
}
- }
- {
- json_t *juri;
- /* Handle merchant jurisdiction */
- juri = settings->jurisdiction;
- if (NULL != juri)
+ for (unsigned int j = 0; j<choice->outputs_len; j++)
{
- juri = json_deep_copy (juri);
- GNUNET_assert (NULL != juri);
- GNUNET_assert (0 ==
- json_object_set_new (merchant,
- "jurisdiction",
- juri));
+ struct TALER_MerchantContractOutput *output = &choice->outputs[j];
+
+ json_t *joutput = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_int64 ("type",
+ output->type)
+ );
+
+ if (TALER_MCOT_TOKEN == output->type)
+ {
+ GNUNET_assert(0 ==
+ json_object_set_new(joutput,
+ "number",
+ json_integer (
+ output->details.token.number)));
+
+ GNUNET_assert(0 ==
+ json_object_set_new(joutput,
+ "token_authority_label",
+ json_string (
+ output->details.token.token_authority_label)));
+ }
+
+ json_array_append_new (outputs, joutput);
}
- }
- }
- for (unsigned int i = 0; i<oc->set_max_fee.limits_len; i++)
- {
- struct TALER_MerchantContractLimits l = oc->set_max_fee.limits[i];
- GNUNET_assert (0 ==
- json_object_set_new(
- limits,
- l.currency,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_data_auto ("h_wire",
- &l.h_wire),
- GNUNET_JSON_pack_string ("wire_method",
- l.wire_method),
- TALER_JSON_pack_amount("max_fee",
- &l.max_fee))));
- }
+ json_t *jchoice = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_incref ("inputs",
+ inputs),
+ GNUNET_JSON_pack_array_incref ("outputs",
+ outputs)
+ );
- for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
- {
- struct TALER_MerchantContractChoice choice = oc->parse_choices.choices[i];
- GNUNET_assert (0 ==
- json_array_append_new(
- choices,
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("summary",
- choice.summary),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("summary_i18n",
- choice.summary_i18n)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("fulfillment_message",
- choice.fulfillment_message)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("fulfillment_message_i18n",
- choice.fulfillment_message_i18n)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("fulfillment_url",
- choice.fulfillment_url)))));
+ json_array_append_new (choices, jchoice);
+ }
}
oc->serialize_order.contract = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("summary",
+ oc->parse_order.summary),
GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("choices",
- choices)),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("limits",
- limits)),
+ GNUNET_JSON_pack_object_incref ("summary_i18n",
+ oc->parse_order.summary_i18n)),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("public_reorder_url",
oc->parse_order.public_reorder_url)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("fulfillment_message",
+ oc->parse_order.fulfillment_message)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("fulfillment_message_i18n",
+ oc->parse_order.fulfillment_message_i18n)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("fulfillment_url",
+ oc->parse_order.fulfillment_url)),
GNUNET_JSON_pack_array_incref ("products",
oc->merge_inventory.products),
GNUNET_JSON_pack_data_auto ("h_wire",
@@ -1531,9 +1479,19 @@ serialize_order_v1 (struct OrderContext *oc)
&oc->hc->instance->merchant_pub),
GNUNET_JSON_pack_array_incref ("exchanges",
oc->set_exchanges.exchanges),
+ TALER_JSON_pack_amount ("max_fee",
+ &oc->parse_order.max_fee),
TALER_JSON_pack_amount ("amount",
&oc->parse_order.brutto),
GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref("choices",
+ choices)
+ ),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref("token_types",
+ token_types)
+ ),
+ GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_object_incref ("extra",
(json_t *) oc->parse_order.extra))
);
@@ -1544,6 +1502,7 @@ serialize_order_v1 (struct OrderContext *oc)
"refund_deadline",
GNUNET_JSON_from_timestamp (
oc->parse_order.refund_deadline)));
+
GNUNET_log (
GNUNET_ERROR_TYPE_INFO,
"Refund deadline for contact is %llu\n",
@@ -1563,26 +1522,6 @@ serialize_order_v1 (struct OrderContext *oc)
GNUNET_JSON_from_time_rel (
oc->parse_order.auto_refund)));
}
-}
-
-/**
- * Serialize order into @a oc->serialize_order.contract,
- * ready to be stored in the database. Upon success, continue
- * processing with check_contract().
- *
- * @param[in,out] oc order context
- */
-static void
-serialize_order (struct OrderContext *oc)
-{
- if (TALER_MCV_V0 == oc->parse_order.version)
- {
- serialize_order_v0 (oc);
- }
- else
- {
- serialize_order_v1 (oc);
- }
oc->phase++;
}
@@ -1600,33 +1539,28 @@ set_max_fee (struct OrderContext *oc)
const struct TALER_MERCHANTDB_InstanceSettings *settings =
&oc->hc->instance->settings;
- GNUNET_array_grow(oc->set_max_fee.limits,
- oc->set_max_fee.limits_len,
- oc->parse_order.limits_len);
-
- for (unsigned int i = 0; i<oc->parse_order.limits_len; i++)
+ if (GNUNET_OK !=
+ TALER_amount_is_valid (&oc->parse_order.max_fee))
{
- if (GNUNET_OK !=
- TALER_amount_is_valid (&oc->parse_order.limits[i].max_fee))
- {
- struct TALER_Amount stefan;
+ struct TALER_Amount stefan;
- if ( (settings->use_stefan) &&
- (GNUNET_OK ==
- TALER_amount_is_valid (&oc->set_exchanges.max_stefan_fee)) )
- stefan = oc->set_exchanges.max_stefan_fee;
- else
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (oc->parse_order.brutto.currency,
- &stefan));
- oc->set_max_fee.limits[i].max_fee = stefan;
- }
+ if ( (settings->use_stefan) &&
+ (GNUNET_OK ==
+ TALER_amount_is_valid (&oc->set_exchanges.max_stefan_fee)) )
+ stefan = oc->set_exchanges.max_stefan_fee;
+ else
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (oc->parse_order.brutto.currency,
+ &stefan));
+ oc->set_max_fee.max_fee = stefan;
+ }
+ else
+ {
+ oc->set_max_fee.max_fee = oc->parse_order.max_fee;
}
-
oc->phase++;
}
-
/**
* Set list of acceptable exchanges in @a oc. Upon success, continue
* processing with set_max_fee().
@@ -1693,28 +1627,22 @@ set_exchanges (struct OrderContext *oc)
/**
* Parse the order field of the request. Upon success, continue
- * processing with parse_choices().
+ * processing with parse_token_types().
*
* @param[in,out] oc order context
*/
static void
parse_order (struct OrderContext *oc)
{
- // struct TALER_MerchantContractLimits *limits;
-
const struct TALER_MERCHANTDB_InstanceSettings *settings =
&oc->hc->instance->settings;
const char *merchant_base_url = NULL;
const char *version = NULL;
const json_t *jmerchant = NULL;
- const json_t *jlimits;
- struct TALER_Amount max_fee;
- // limits = GNUNET_new (struct TALER_MerchantContractLimits);
/* auto_refund only needs to be type-checked,
* mostly because in GNUnet relative times can't
* be negative. */
bool no_fee;
- bool no_limits;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("version",
@@ -1722,11 +1650,33 @@ parse_order (struct OrderContext *oc)
NULL),
TALER_JSON_spec_amount_any ("amount",
&oc->parse_order.brutto),
+ GNUNET_JSON_spec_string ("summary",
+ &oc->parse_order.summary),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const ("products",
+ &oc->parse_order.products),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_json ("summary_i18n",
+ &oc->parse_order.summary_i18n),
+ NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("order_id",
&oc->parse_order.order_id),
NULL),
GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("fulfillment_message",
+ &oc->parse_order.fulfillment_message),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_json ("fulfillment_message_i18n",
+ &oc->parse_order.fulfillment_message_i18n),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("fulfillment_url",
+ &oc->parse_order.fulfillment_url),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("public_reorder_url",
&oc->parse_order.public_reorder_url),
NULL),
@@ -1743,6 +1693,10 @@ parse_order (struct OrderContext *oc)
&jmerchant),
NULL),
GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_object_const ("token_types",
+ &oc->parse_order.token_types),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_timestamp ("timestamp",
&oc->parse_order.timestamp),
NULL),
@@ -1760,13 +1714,9 @@ parse_order (struct OrderContext *oc)
NULL),
GNUNET_JSON_spec_mark_optional (
TALER_JSON_spec_amount_any ("max_fee",
- &max_fee),
+ &oc->parse_order.max_fee),
&no_fee),
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const ("limits",
- &jlimits),
- &no_limits),
- GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("delivery_location",
&oc->parse_order.delivery_location),
NULL),
@@ -1802,82 +1752,31 @@ parse_order (struct OrderContext *oc)
{
oc->parse_order.version = TALER_MCV_V0;
- if ( (! no_fee) &&
- (GNUNET_OK !=
- TALER_amount_cmp_currency (&oc->parse_order.brutto,
- &max_fee)) )
+ if (NULL != oc->parse_order.choices)
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
reply_with_error (oc,
MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_CURRENCY_MISMATCH,
- "different currencies used for 'max_fee' and 'amount' currency");
+ TALER_EC_GENERIC_UNEXPECTED_REQUEST_ERROR,
+ "choices array must be null for v0 contracts");
return;
}
- /**
- * v0 only supports one currency. For the sake of simplicity,
- * we still use the limits array with just one item.
- */
- GNUNET_array_grow (oc->parse_order.limits,
- oc->parse_order.limits_len,
- 1);
-
- oc->parse_order.limits->max_fee = max_fee;
-
- /* Copy currency of max_fee into limits label */
- GNUNET_snprintf (oc->parse_order.limits->currency,
- sizeof (oc->parse_order.limits->currency),
- "%s",
- oc->parse_order.limits->max_fee.currency);
+ if (NULL != oc->parse_order.token_types)
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_UNEXPECTED_REQUEST_ERROR,
+ "token_types object must be null for v0 contracts");
+ return;
+ }
}
else if (0 != strcmp("v1", version))
{
oc->parse_order.version = TALER_MCV_V1;
-
- GNUNET_array_grow (oc->parse_order.limits,
- oc->parse_order.limits_len,
- json_array_size (jlimits));
-
- for (unsigned int i = 0; i<oc->parse_order.limits_len; i++)
- {
- struct TALER_MerchantContractLimits *limits = &oc->parse_order.limits[i];
- const char *error_name;
- unsigned int error_line;
- struct GNUNET_JSON_Specification ispec[] = {
- TALER_JSON_spec_amount_any ("max_fee",
- &limits->max_fee),
- // TODO: Add the rest of the fields
- GNUNET_JSON_spec_end ()
- };
-
- ret = GNUNET_JSON_parse (json_array_get (jlimits,
- i),
- ispec,
- &error_name,
- &error_line);
- if (GNUNET_OK != ret)
- {
- GNUNET_break_op (0);
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Limits parsing failed at #%u: %s:%u\n",
- i,
- error_name,
- error_line);
- reply_with_error (oc,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "order.limits");
- return;
- }
-
- /* Copy currency of max_fee into limits label */
- GNUNET_snprintf (limits->currency,
- sizeof (limits->currency),
- "%s",
- limits->max_fee.currency);
- }
}
else
{
@@ -1910,6 +1809,19 @@ parse_order (struct OrderContext *oc)
"no trusted exchange for this currency");
return;
}
+ if ( (! no_fee) &&
+ (GNUNET_OK !=
+ TALER_amount_cmp_currency (&oc->parse_order.brutto,
+ &oc->parse_order.max_fee)) )
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_CURRENCY_MISMATCH,
+ "different currencies used for 'max_fee' and 'amount' currency");
+ return;
+ }
/* Add order_id if it doesn't exist. */
if (NULL == oc->parse_order.order_id)
@@ -1957,6 +1869,46 @@ parse_order (struct OrderContext *oc)
GNUNET_assert (NULL != oc->parse_order.order_id);
}
+ /* Patch fulfillment URL with order_id (implements #6467). */
+ if (NULL != oc->parse_order.fulfillment_url)
+ {
+ const char *pos;
+
+ pos = strstr (oc->parse_order.fulfillment_url,
+ "${ORDER_ID}");
+ if (NULL != pos)
+ {
+ /* replace ${ORDER_ID} with the real order_id */
+ char *nurl;
+
+ /* We only allow one placeholder */
+ if (strstr (pos + strlen ("${ORDER_ID}"),
+ "${ORDER_ID}"))
+ {
+ GNUNET_break_op (0);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "fulfillment_url");
+ return;
+ }
+
+ GNUNET_asprintf (&nurl,
+ "%.*s%s%s",
+ /* first output URL until ${ORDER_ID} */
+ (int) (pos - oc->parse_order.fulfillment_url),
+ oc->parse_order.fulfillment_url,
+ /* replace ${ORDER_ID} with the right order_id */
+ oc->parse_order.order_id,
+ /* append rest of original URL */
+ pos + strlen ("${ORDER_ID}"));
+
+ oc->parse_order.fulfillment_url = GNUNET_strdup (nurl);
+
+ GNUNET_free (nurl);
+ }
+ }
+
/* Check soundness of refund deadline, and that a timestamp
* is actually present. */
{
@@ -2099,6 +2051,18 @@ parse_order (struct OrderContext *oc)
oc->parse_order.merchant_base_url = url;
}
+ if ( (NULL != oc->parse_order.products) &&
+ (! TMH_products_array_valid (oc->parse_order.products)) )
+ {
+ GNUNET_break_op (0);
+ reply_with_error (
+ oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "order.products");
+ return;
+ }
+
/* Merchant information must not already be present */
if (NULL != jmerchant)
{
@@ -2126,165 +2090,433 @@ parse_order (struct OrderContext *oc)
}
/**
- * Parse a json contract choice.
- *
- * @param[out] choice resulting contract choice
- * @param root json object
- * @param order_id order_id to be replaced in fulfillment URL
- * @return #GNUNET_OK if choice could be parsed.
+ * Parse the token_types map of the request. Upon success, continue
+ * processing with parse_choices().
*/
-static enum GNUNET_GenericReturnValue
-parse_choice (
- struct TALER_MerchantContractChoice *choice,
- json_t *root,
- const char *order_id)
+static void
+parse_token_types (struct OrderContext *oc)
{
- const char *error_name;
- unsigned int error_line;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("summary",
- &choice->summary),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const ("products",
- &choice->products),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_json ("summary_i18n",
- &choice->summary_i18n),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("fulfillment_message",
- &choice->fulfillment_message),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_json ("fulfillment_message_i18n",
- &choice->fulfillment_message_i18n),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_string ("fulfillment_url",
- &choice->fulfillment_url),
- NULL),
- };
-
- enum GNUNET_GenericReturnValue ret;
- ret = GNUNET_JSON_parse (root,
- spec,
- &error_name,
- &error_line);
- if (GNUNET_OK != ret)
+ if (NULL != oc->parse_order.token_types)
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Choice parsing failed: %s:%u\n",
- error_name,
- error_line);
- return ret;
- }
+ const char *key;
+ json_t *value;
- /* Patch fulfillment URL with order_id (implements #6467). */
- if (NULL != choice->fulfillment_url)
- {
- const char *pos;
+ GNUNET_array_grow (oc->parse_token_types.authorities,
+ oc->parse_token_types.authorities_len,
+ json_object_size(oc->parse_order.token_types));
- pos = strstr (choice->fulfillment_url,
- "${ORDER_ID}");
- if (NULL != pos)
+ json_object_foreach ((json_t*) oc->parse_order.token_types,
+ key,
+ value)
{
- /* replace ${ORDER_ID} with the real order_id */
- char *nurl;
+ struct TALER_MerchantContractTokenAuthority authority;
+ const struct json_t *pub_key;
- /* We only allow one placeholder */
- if (strstr (pos + strlen ("${ORDER_ID}"),
- "${ORDER_ID}"))
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_object_const ("key", &pub_key),
+ GNUNET_JSON_spec_bool ("critical", &authority.critical),
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ NULL,
+ NULL))
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Patching fulfillment URL failed\n");
- return GNUNET_SYSERR;
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "token_types");
+ return;
}
- GNUNET_asprintf (&nurl,
- "%.*s%s%s",
- /* first output URL until ${ORDER_ID} */
- (int) (pos - choice->fulfillment_url),
- choice->fulfillment_url,
- /* replace ${ORDER_ID} with the right order_id */
- order_id,
- /* append rest of original URL */
- pos + strlen ("${ORDER_ID}"));
+ const char *cipher = NULL;
- choice->fulfillment_url = GNUNET_strdup (nurl);
+ struct GNUNET_JSON_Specification kspec[] = {
+ GNUNET_JSON_spec_string ("cipher", &cipher),
+ GNUNET_JSON_spec_rsa_public_key("public_key", &authority.key.public_key.details.rsa_public_key),
+ };
- GNUNET_free (nurl);
- }
- }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (pub_key,
+ kspec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (kspec);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "token_types.key");
+ return;
+ }
- if ( (NULL != choice->products) &&
- (! TMH_products_array_valid (choice->products)) )
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid choice.products array\n");
- return GNUNET_SYSERR;
+ if (0 == strcmp("rsa", cipher))
+ {
+ authority.key.public_key.cipher = GNUNET_CRYPTO_BSA_RSA;
+ }
+ else
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (kspec);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "token_types.key.cipher");
+ return;
+ }
+
+ GNUNET_CRYPTO_rsa_public_key_hash (
+ authority.key.public_key.details.rsa_public_key,
+ &authority.key.public_key.pub_key_hash);
+
+ struct TALER_MERCHANTDB_TokenFamilyKeyDetails key_details;
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TMH_db->lookup_token_family_key (TMH_db->cls,
+ oc->hc->instance->settings.id,
+ &authority.key.public_key.pub_key_hash,
+ &key_details);
+
+ if (qs <= 0)
+ {
+ enum TALER_ErrorCode ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ unsigned int http_status = 0;
+
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
+ break;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ ec = TALER_EC_GENERIC_DB_SOFT_FAILURE;
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Token family public key unknown\n");
+ http_status = MHD_HTTP_NOT_FOUND;
+ // TODO: Add error code
+ ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ /* case listed to make compilers happy */
+ GNUNET_assert (0);
+ }
+ json_decref (oc->merge_inventory.products);
+ reply_with_error (oc,
+ http_status,
+ ec,
+ "token family public key unknown");
+ return;
+ }
+
+ strcpy((char *) authority.label, key);
+ strcpy((char *) authority.summary, key_details.token_family.description);
+ // TODO: Copy summary_i18n
+
+ switch (key_details.token_family.kind) {
+ case TALER_MERCHANTDB_TFK_Subscription:
+ authority.kind = TALER_MCTK_SUBSCRIPTION;
+ break;
+ case TALER_MERCHANTDB_TFK_Discount:
+ authority.kind = TALER_MCTK_DISCOUNT;
+ break;
+ }
+
+ GNUNET_array_append (oc->parse_token_types.authorities,
+ oc->parse_token_types.authorities_len,
+ authority);
+ }
}
- return GNUNET_OK;
+ oc->phase++;
}
/**
- * Check contract version and call parse_choices_v0 or parse_choices_v1
- * respectively. Upon success, continue processing with merge_inventory().
+ * Parse contract choices. Upon success, continue
+ * processing with merge_inventory().
*
* @param[in,out] oc order context
*/
static void
parse_choices (struct OrderContext *oc)
{
- enum GNUNET_GenericReturnValue ret;
-
- if (TALER_MCV_V0 == oc->parse_order.version)
+ if (NULL != oc->parse_order.choices)
{
- /**
- * v0 contract is basically a v1 contract with only one choice
- * and no inputs or outputs.
- */
- GNUNET_array_grow (oc->parse_choices.choices,
- oc->parse_choices.choices_len,
- 1);
-
- ret = parse_choice(oc->parse_choices.choices,
- oc->parse_request.order,
- oc->parse_order.order_id);
-
- if (GNUNET_OK != ret)
- {
- GNUNET_break_op (0);
- reply_with_error (oc,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "order");
- return;
- }
- }
- else if (TALER_MCV_V1 == oc->parse_order.version)
- {
- GNUNET_assert (NULL != oc->parse_order.choices);
-
GNUNET_array_grow (oc->parse_choices.choices,
oc->parse_choices.choices_len,
json_array_size (oc->parse_order.choices));
for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
{
- ret = parse_choice(&oc->parse_choices.choices[i],
- json_array_get (oc->parse_order.choices, i),
- oc->parse_order.order_id);
+ const char *error_name;
+ unsigned int error_line;
+ const json_t *jinputs = NULL;
+ const json_t *joutputs = NULL;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("inputs",
+ &jinputs),
+ GNUNET_JSON_spec_array_const ("outputs",
+ &joutputs),
+ };
+ enum GNUNET_GenericReturnValue ret;
+ ret = GNUNET_JSON_parse (json_array_get (oc->parse_order.choices, i),
+ spec,
+ &error_name,
+ &error_line);
if (GNUNET_OK != ret)
{
+ GNUNET_JSON_parse_free (spec);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Choice parsing failed: %s:%u\n",
+ error_name,
+ error_line);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "choice json parse failed");
+ return;
+ }
+
+ if (! json_is_array (jinputs) ||
+ ! json_is_array (joutputs))
+ {
GNUNET_break_op (0);
reply_with_error (oc,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "choices");
+ "choice");
return;
}
+
+ {
+ const json_t *jinput;
+ size_t idx;
+ json_array_foreach ((json_t *) jinputs, idx, jinput)
+ {
+ struct TALER_MerchantContractInput input;
+ const char *kind = NULL;
+ bool no_number;
+
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("kind",
+ &kind),
+ GNUNET_JSON_spec_string ("authority_label",
+ &input.details.token.token_authority_label),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("number",
+ &input.details.token.number),
+ &no_number),
+ };
+
+ const char *ename;
+ unsigned int eline;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (jinput,
+ spec,
+ &ename,
+ &eline))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid input #%u for field %s\n",
+ (unsigned int) idx,
+ ename);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "input json parse failed");
+ return;
+ }
+
+ /**
+ * Parse kind of input. For now, only 'token' is supported.
+ */
+ if (0 == strcmp("token", kind))
+ {
+ input.type = TALER_MCIT_TOKEN;
+ }
+ else
+ {
+ GNUNET_break_op (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid input #%u for field kind\n",
+ (unsigned int) idx);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "invalid kind field in input");
+ return;
+ }
+
+ /**
+ * Set default number to 1 if not present.
+ */
+ if (no_number)
+ {
+ input.details.token.number = 1;
+ }
+ else if (1 > input.details.token.number)
+ {
+ GNUNET_break_op (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid input #%u for field number\n",
+ (unsigned int) idx);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "invalid number field in input");
+ return;
+ }
+
+ bool found = false;
+
+ for (unsigned int i = 0; i<oc->parse_token_types.authorities_len; i++)
+ {
+ if (0 == strcmp (oc->parse_token_types.authorities[i].label,
+ input.details.token.token_authority_label))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (! found)
+ {
+ GNUNET_break_op (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid input #%u: authority_label not found in token_types\n",
+ (unsigned int) idx);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "invalid authority_label field in input");
+ return;
+ }
+
+ GNUNET_array_append (oc->parse_choices.choices[i].inputs,
+ oc->parse_choices.choices[i].inputs_len,
+ input);
+ }
+ }
+
+ {
+ const json_t *joutput;
+ size_t idx;
+ json_array_foreach ((json_t *) joutputs, idx, joutput)
+ {
+ struct TALER_MerchantContractOutput output;
+ const char *kind = NULL;
+ bool no_number;
+
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("kind",
+ &kind),
+ GNUNET_JSON_spec_string ("authority_label",
+ &output.details.token.token_authority_label),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("number",
+ &output.details.token.number),
+ &no_number),
+ };
+
+ const char *ename;
+ unsigned int eline;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (joutput,
+ spec,
+ &ename,
+ &eline))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid output #%u for field %s\n",
+ (unsigned int) idx,
+ ename);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "output json parse failed");
+ return;
+ }
+
+ /**
+ * Parse kind of output. For now, only 'token' is supported.
+ */
+ if (0 == strcmp("token", kind))
+ {
+ output.type = TALER_MCOT_TOKEN;
+ }
+ else
+ {
+ GNUNET_break_op (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid output #%u for field kind\n",
+ (unsigned int) idx);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "invalid kind field in output");
+ return;
+ }
+
+ /**
+ * Set default number to 1 if not present.
+ */
+ if (no_number)
+ {
+ output.details.token.number = 1;
+ }
+ else if (1 > output.details.token.number)
+ {
+ GNUNET_break_op (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid output #%u for field number\n",
+ (unsigned int) idx);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "invalid number field in output");
+ return;
+ }
+
+ bool found = false;
+
+ for (unsigned int i = 0; i<oc->parse_token_types.authorities_len; i++)
+ {
+ if (0 == strcmp (oc->parse_token_types.authorities[i].label,
+ output.details.token.token_authority_label))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (! found)
+ {
+ GNUNET_break_op (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid output #%u: authority_label not found in token_types\n",
+ (unsigned int) idx);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "invalid authority_label field in output");
+ return;
+ }
+
+ GNUNET_array_append (oc->parse_choices.choices[i].outputs,
+ oc->parse_choices.choices[i].outputs_len,
+ output);
+ }
+ }
}
}
@@ -2337,17 +2569,13 @@ add_payment_details (struct OrderContext *oc)
static void
merge_inventory (struct OrderContext *oc)
{
- struct TALER_MerchantContractChoice *choice = oc->parse_choices.choices;
-
- GNUNET_assert (1 == oc->parse_choices.choices_len && NULL != choice);
-
/**
* parse_request.inventory_products => instructions to add products to contract terms
* parse_order.products => contains products that are not from the backend-managed inventory.
*/
- if (NULL != choice->products)
+ if (NULL != oc->parse_order.products)
oc->merge_inventory.products
- = json_deep_copy (choice->products);
+ = json_deep_copy (oc->parse_order.products);
else
oc->merge_inventory.products
= json_array ();
@@ -2676,6 +2904,9 @@ TMH_private_post_orders (
case ORDER_PHASE_PARSE_ORDER:
parse_order (oc);
break;
+ case ORDER_PHASE_PARSE_TOKEN_TYPES:
+ parse_token_types (oc);
+ break;
case ORDER_PHASE_PARSE_CHOICES:
parse_choices (oc);
break;