gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant] 15/31: serialize v1 contract ready for db


From: gnunet
Subject: [taler-merchant] 15/31: serialize v1 contract ready for db
Date: Thu, 18 Apr 2024 08:39:08 +0200

This is an automated email from the git hooks/post-receive script.

christian-blaettler pushed a commit to branch master
in repository merchant.

commit f4585ef6c7eb890bf3ec345f032e223505a4e2d3
Author: Christian Blättler <blatc2@bfh.ch>
AuthorDate: Mon Mar 18 20:58:28 2024 +0100

    serialize v1 contract ready for db
---
 .../taler-merchant-httpd_private-post-orders.c     | 1157 ++++++++++++--------
 1 file 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"
 
 
 /**
@@ -237,6 +240,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).
      */
@@ -247,6 +282,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.
      */
@@ -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
@@ -329,6 +352,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.
    */
@@ -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,8 +1479,18 @@ 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,10 +1650,32 @@ 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),
@@ -1742,6 +1692,10 @@ parse_order (struct OrderContext *oc)
       GNUNET_JSON_spec_object_const ("merchant",
                                      &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),
@@ -1760,12 +1714,8 @@ 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),
@@ -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;

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]