[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-merchant] branch master updated: -fix order creation logic for co
From: |
gnunet |
Subject: |
[taler-merchant] branch master updated: -fix order creation logic for contract v1 |
Date: |
Tue, 24 Dec 2024 14:12:07 +0100 |
This is an automated email from the git hooks/post-receive script.
grothoff pushed a commit to branch master
in repository merchant.
The following commit(s) were added to refs/heads/master by this push:
new 801f1890 -fix order creation logic for contract v1
801f1890 is described below
commit 801f18909fc696d37a160551233f0749db6f7d68
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Tue Dec 24 14:12:00 2024 +0100
-fix order creation logic for contract v1
---
src/backend/taler-merchant-httpd_contract.h | 98 +-
.../taler-merchant-httpd_private-post-orders.c | 1478 +++++++++++++-------
src/include/taler_merchant_testing_lib.h | 4 +-
src/testing/test_merchant_api.c | 8 +-
src/testing/test_merchant_order_creation.sh | 4 +-
src/testing/testing_api_cmd_post_orders.c | 167 +--
6 files changed, 1119 insertions(+), 640 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_contract.h
b/src/backend/taler-merchant-httpd_contract.h
index d937c119..182e7732 100644
--- a/src/backend/taler-merchant-httpd_contract.h
+++ b/src/backend/taler-merchant-httpd_contract.h
@@ -220,6 +220,9 @@ struct TALER_MerchantContractOutput
*/
unsigned int count;
+ // FIXME: add support for clients picking a validity
+ // period in the future for output tokens!
+
} token;
} details;
@@ -380,10 +383,9 @@ struct TALER_MerchantContractTokenFamily
} details;
};
+
/**
- * Struct to hold contract terms in v0 and v1 format. v0 contracts are modelled
- * as a v1 contract with a single choice and no inputs and outputs. Use the
- * version field to explicitly differentiate between v0 and v1 contracts.
+ * Struct to hold contract terms.
*/
struct TALER_MerchantContract
{
@@ -436,15 +438,8 @@ struct TALER_MerchantContract
* Jurisdiction of the business
*/
json_t *jurisdiction;
- } merchant;
- /**
- * Price to be paid for the transaction. Could be 0. The price is in addition
- * to other instruments, such as rations and tokens.
- * The exchange will subtract deposit fees from that amount
- * before transferring it to the merchant.
- */
- struct TALER_Amount brutto;
+ } merchant;
/**
* Summary of the contract.
@@ -531,44 +526,81 @@ struct TALER_MerchantContract
enum TALER_MerchantContractVersion version;
/**
- * Array of possible specific contracts the wallet/customer may choose
- * from by selecting the respective index when signing the deposit
- * confirmation.
+ * Details depending on the @e version.
*/
- struct TALER_MerchantContractChoice *choices;
+ union
+ {
- /**
- * Length of the @e choices array.
- */
- unsigned int choices_len;
+ /**
+ * Details for v0 contracts.
+ */
+ struct
+ {
- /**
- * Array of token authorities.
- */
- struct TALER_MerchantContractTokenFamily *token_authorities;
+ /**
+ * Price to be paid for the transaction. Could be 0. The price is in
addition
+ * to other instruments, such as rations and tokens.
+ * The exchange will subtract deposit fees from that amount
+ * before transferring it to the merchant.
+ */
+ struct TALER_Amount brutto;
- /**
- * Length of the @e token_authorities array.
- */
- unsigned int token_authorities_len;
+ /**
+ * Maximum fee as given by the client request.
+ */
+ struct TALER_Amount max_fee;
- /**
- * Maximum fee as given by the client request.
- */
- struct TALER_Amount max_fee;
+ } v0;
+
+ /**
+ * Details for v1 contracts.
+ */
+ struct
+ {
+
+ /**
+ * Array of possible specific contracts the wallet/customer may choose
+ * from by selecting the respective index when signing the deposit
+ * confirmation.
+ */
+ struct TALER_MerchantContractChoice *choices;
+
+ /**
+ * Length of the @e choices array.
+ */
+ unsigned int choices_len;
+
+ /**
+ * Array of token authorities.
+ */
+ struct TALER_MerchantContractTokenFamily *token_authorities;
+
+ /**
+ * Length of the @e token_authorities array.
+ */
+ unsigned int token_authorities_len;
+
+ } v1;
+
+ } details;
+
+ // FIXME: Add exchanges array?
- // TODO: Add exchanges array
};
+
enum TALER_MerchantContractInputType
TMH_contract_input_type_from_string (const char *str);
+
enum TALER_MerchantContractOutputType
TMH_contract_output_type_from_string (const char *str);
+
const char *
TMH_string_from_contract_input_type (enum TALER_MerchantContractInputType t);
+
const char *
TMH_string_from_contract_output_type (enum TALER_MerchantContractOutputType t);
@@ -590,12 +622,14 @@ TMH_serialize_contract (const struct
TALER_MerchantContract *contract,
json_t *exchanges,
json_t **out);
+
enum GNUNET_GenericReturnValue
TMH_serialize_contract_v0 (const struct TALER_MerchantContract *contract,
const struct TMH_MerchantInstance *instance,
json_t *exchanges,
json_t **out);
+
enum GNUNET_GenericReturnValue
TMH_serialize_contract_v1 (const struct TALER_MerchantContract *contract,
const struct TMH_MerchantInstance *instance,
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c
b/src/backend/taler-merchant-httpd_private-post-orders.c
index 836efb27..1cb11a15 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders.c
+++ b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -247,10 +247,6 @@ struct OrderContext
*/
struct
{
- /**
- * Version of the contract terms.
- */
- enum TALER_MerchantContractVersion version;
/**
* Our order ID.
@@ -294,11 +290,6 @@ struct OrderContext
*/
const char *public_reorder_url;
- /**
- * Array of contract choices. Is null for v0 contracts.
- */
- const json_t *choices;
-
/**
* Merchant base URL.
*/
@@ -334,17 +325,6 @@ struct OrderContext
*/
const json_t *delivery_location;
- /**
- * Gross amount value of the contract. Used to
- * compute @e max_stefan_fee.
- */
- struct TALER_Amount brutto;
-
- /**
- * Maximum fee as given by the client request.
- */
- struct TALER_Amount max_fee;
-
/**
* Specifies for how long the wallet should try to get an
* automatic refund for the purchase.
@@ -367,6 +347,45 @@ struct OrderContext
*/
uint32_t minimum_age;
+ /**
+ * Version of the contract terms.
+ */
+ enum TALER_MerchantContractVersion version;
+
+ /**
+ * Details present depending on @e version.
+ */
+ union
+ {
+ /**
+ * Details only present for v0.
+ */
+ struct
+ {
+ /**
+ * Gross amount value of the contract. Used to
+ * compute @e max_stefan_fee.
+ */
+ struct TALER_Amount brutto;
+
+ /**
+ * Maximum fee as given by the client request.
+ */
+ struct TALER_Amount max_fee;
+ } v0;
+
+ /**
+ * Details only present for v1.
+ */
+ struct
+ {
+ /**
+ * Array of contract choices. Is null for v0 contracts.
+ */
+ const json_t *choices;
+ } v1;
+ } details;
+
} parse_order;
/**
@@ -445,18 +464,16 @@ struct OrderContext
bool exchange_good;
/**
- * Maximum fee for @e order based on STEFAN curves.
- * Used to set @e max_fee if not provided as part of
- * @e order.
+ * Array of maximum amounts that could be paid over all
+ * available exchanges. Used to determine if this
+ * order creation requests exceeds legal limits.
*/
- struct TALER_Amount max_stefan_fee;
+ struct TALER_Amount *total_exchange_limits;
/**
- * Maximum amount that could be paid over all
- * available exchanges. Used to determine if this
- * order creation requests exceeds legal limits.
+ * Length of the @e total_exchange_limits array.
*/
- struct TALER_Amount total_exchange_limit;
+ unsigned int num_total_exchange_limits;
/**
* How long do we wait at most until giving up on getting keys?
@@ -468,6 +485,43 @@ struct OrderContext
*/
struct GNUNET_SCHEDULER_Task *wakeup_task;
+ /**
+ * Details depending on the contract version.
+ */
+ union
+ {
+
+ /**
+ * Details for contract v0.
+ */
+ struct
+ {
+ /**
+ * Maximum fee for @e order based on STEFAN curves.
+ * Used to set @e max_fee if not provided as part of
+ * @e order.
+ */
+ struct TALER_Amount max_stefan_fee;
+
+ } v0;
+
+ /**
+ * Details for contract v1.
+ */
+ struct
+ {
+ /**
+ * Maximum fee for @e order based on STEFAN curves by
+ * contract choice.
+ * Used to set @e max_fee if not provided as part of
+ * @e order.
+ */
+ struct TALER_Amount *max_stefan_fees;
+
+ } v1;
+
+ } details;
+
} set_exchanges;
/**
@@ -475,10 +529,37 @@ struct OrderContext
*/
struct
{
+
/**
- * Maximum fee
+ * Details depending on the contract version.
*/
- struct TALER_Amount max_fee;
+ union
+ {
+
+ /**
+ * Details for contract v0.
+ */
+ struct
+ {
+ /**
+ * Maximum fee
+ */
+ struct TALER_Amount max_fee;
+ } v0;
+
+ /**
+ * Details for contract v1.
+ */
+ struct
+ {
+ /**
+ * Maximum fees by contract choice.
+ */
+ struct TALER_Amount *max_fees;
+
+ } v1;
+
+ } details;
} set_max_fee;
/**
@@ -693,6 +774,16 @@ clean_order (void *cls)
json_decref (oc->set_exchanges.exchanges);
oc->set_exchanges.exchanges = NULL;
}
+ GNUNET_free (oc->set_exchanges.total_exchange_limits);
+ switch (oc->parse_order.version)
+ {
+ case TALER_MCV_V0:
+ break;
+ case TALER_MCV_V1:
+ GNUNET_free (oc->set_max_fee.details.v1.max_fees);
+ GNUNET_free (oc->set_exchanges.details.v1.max_stefan_fees);
+ break;
+ }
if (NULL != oc->merge_inventory.products)
{
json_decref (oc->merge_inventory.products);
@@ -1790,86 +1881,38 @@ add_output_token_family (struct OrderContext *oc,
/**
- * Serialize order into @a oc->serialize_order.contract,
- * ready to be stored in the database. Upon success, continue
- * processing with check_contract().
+ * Build JSON array that represents all of the token families
+ * in the contract.
*
- * @param[in,out] oc order context
+ * @param[in] v1-style order
+ * @return JSON array with token families for the contract
*/
-static void
-serialize_order (struct OrderContext *oc)
+static json_t *
+output_token_families (struct OrderContext *oc)
{
- const struct TALER_MERCHANTDB_InstanceSettings *settings =
- &oc->hc->instance->settings;
- json_t *merchant;
json_t *token_families = 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);
- {
- json_t *loca;
-
- /* Handle merchant address */
- loca = settings->address;
- if (NULL != loca)
- {
- loca = json_deep_copy (loca);
- GNUNET_assert (NULL != loca);
- GNUNET_assert (0 ==
- json_object_set_new (merchant,
- "address",
- loca));
- }
- }
- {
- json_t *juri;
-
- /* Handle merchant jurisdiction */
- juri = settings->jurisdiction;
- if (NULL != juri)
- {
- juri = json_deep_copy (juri);
- GNUNET_assert (NULL != juri);
- GNUNET_assert (0 ==
- json_object_set_new (merchant,
- "jurisdiction",
- juri));
- }
- }
+ GNUNET_assert (NULL != token_families);
for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++)
{
- json_t *keys = json_array ();
- struct TALER_MerchantContractTokenFamily *family
+ const struct TALER_MerchantContractTokenFamily *family
= &oc->parse_choices.token_families[i];
+ json_t *keys;
json_t *jfamily;
+ keys = json_array ();
+ GNUNET_assert (NULL != keys);
for (unsigned int j = 0; j<family->keys_len; j++)
{
- struct TALER_MerchantContractTokenFamilyKey key = family->keys[j];
-
+ const struct TALER_MerchantContractTokenFamilyKey *key
+ = &family->keys[j];
json_t *jkey = GNUNET_JSON_PACK (
- /* TODO: Remove h_pub. */
- GNUNET_JSON_pack_data_auto ("h_pub",
- &key.pub.public_key->pub_key_hash),
TALER_JSON_pack_token_pub ("public_key",
- &key.pub),
+ &key->pub),
GNUNET_JSON_pack_timestamp ("valid_after",
- key.valid_after),
+ key->valid_after),
GNUNET_JSON_pack_timestamp ("valid_before",
- key.valid_before)
+ key->valid_before)
);
GNUNET_assert (0 ==
@@ -1877,7 +1920,7 @@ serialize_order (struct OrderContext *oc)
jkey));
}
- /* TODO: Add 'details' field. */
+ /* FIXME: Add 'details' field. */
jfamily = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("name",
family->name),
@@ -1892,11 +1935,28 @@ serialize_order (struct OrderContext *oc)
family->critical)
);
- GNUNET_assert (0 == json_object_set_new (token_families,
- family->slug,
- jfamily));
+ GNUNET_assert (0 ==
+ json_object_set_new (token_families,
+ family->slug,
+ jfamily));
}
+ return token_families;
+}
+
+
+/**
+ * Build JSON array that represents all of the contract choices
+ * in the contract.
+ *
+ * @param[in] v1-style order
+ * @return JSON array with token families for the contract
+ */
+static json_t *
+output_contract_choices (struct OrderContext *oc)
+{
+ json_t *choices = json_array ();
+ GNUNET_assert (NULL != choices);
for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
{
const struct TALER_MerchantContractChoice *choice
@@ -1912,15 +1972,15 @@ serialize_order (struct OrderContext *oc)
= &choice->inputs[j];
json_t *jinput;
- /* For now, only tokens are supported */
+ /* For now, only tokens are supported for inputs */
GNUNET_assert (TALER_MCIT_TOKEN == input->type);
jinput = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("kind",
- TMH_string_from_contract_input_type (input->
- type)),
+ TMH_string_from_contract_input_type (
+ input->type)),
GNUNET_JSON_pack_string ("token_family_slug",
input->details.token.token_family_slug),
- GNUNET_JSON_pack_int64 ("number",
+ GNUNET_JSON_pack_int64 ("count",
input->details.token.count)
);
@@ -1935,19 +1995,47 @@ serialize_order (struct OrderContext *oc)
json_t *joutput;
/* For now, only tokens are supported */
- GNUNET_assert (TALER_MCOT_TOKEN == output->type);
-
- joutput = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("kind",
- TMH_string_from_contract_output_type (output->
- type)),
- GNUNET_JSON_pack_string ("token_family_slug",
- output->details.token.token_family_slug),
- GNUNET_JSON_pack_int64 ("number",
- output->details.token.count),
- GNUNET_JSON_pack_int64 ("key_index",
- output->details.token.key_index)
- );
+ switch (output->type)
+ {
+ case TALER_MCOT_INVALID:
+ /* How did we get here? */
+ GNUNET_assert (0);
+ /* mostly to make compiler happy... */
+ finalize_order (oc,
+ MHD_NO);
+ json_decref (choices);
+ return NULL;
+ case TALER_MCOT_TOKEN:
+ joutput = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("kind",
+ TMH_string_from_contract_output_type (
+ output->type)),
+ GNUNET_JSON_pack_string ("token_family_slug",
+ output->details.token.token_family_slug),
+ GNUNET_JSON_pack_int64 ("count",
+ output->details.token.count),
+ GNUNET_JSON_pack_int64 ("key_index",
+ output->details.token.key_index)
+ );
+ break;
+ case TALER_MCOT_COIN:
+ /* Not implemented, how did we get here? */
+ GNUNET_break (0);
+ reply_with_error (oc,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "currency conversion not supported");
+ json_decref (choices);
+ return NULL;
+ case TALER_MCOT_TAX_RECEIPT:
+ // FIXME: generate JSON for DONAU here instead of killing
+ // the connection!
+ GNUNET_break (0);
+ finalize_order (oc,
+ MHD_NO);
+ json_decref (choices);
+ return NULL;
+ }
GNUNET_assert (0 ==
json_array_append_new (outputs,
@@ -1957,6 +2045,10 @@ serialize_order (struct OrderContext *oc)
{
json_t *jchoice
= GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("amount",
+ &choice->amount),
+ TALER_JSON_pack_amount ("max_fee",
+ &oc->set_max_fee.details.v1.max_fees[i]),
GNUNET_JSON_pack_array_incref ("inputs",
inputs),
GNUNET_JSON_pack_array_incref ("outputs",
@@ -1967,6 +2059,67 @@ serialize_order (struct OrderContext *oc)
json_array_append_new (choices,
jchoice));
}
+ } /* for all choices */
+ return choices;
+}
+
+
+/**
+ * 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)
+{
+ const struct TALER_MERCHANTDB_InstanceSettings *settings =
+ &oc->hc->instance->settings;
+ json_t *merchant;
+
+ 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);
+ {
+ json_t *loca;
+
+ /* Handle merchant address */
+ loca = settings->address;
+ if (NULL != loca)
+ {
+ loca = json_deep_copy (loca);
+ GNUNET_assert (NULL != loca);
+ GNUNET_assert (0 ==
+ json_object_set_new (merchant,
+ "address",
+ loca));
+ }
+ }
+ {
+ json_t *juri;
+
+ /* Handle merchant jurisdiction */
+ juri = settings->jurisdiction;
+ if (NULL != juri)
+ {
+ juri = json_deep_copy (juri);
+ GNUNET_assert (NULL != juri);
+ GNUNET_assert (0 ==
+ json_object_set_new (merchant,
+ "jurisdiction",
+ juri));
+ }
}
oc->serialize_order.contract = GNUNET_JSON_PACK (
@@ -1975,8 +2128,9 @@ serialize_order (struct OrderContext *oc)
GNUNET_JSON_pack_string ("summary",
oc->parse_order.summary),
GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("summary_i18n",
- (json_t *)
oc->parse_order.summary_i18n)),
+ GNUNET_JSON_pack_object_incref (
+ "summary_i18n",
+ (json_t *) oc->parse_order.summary_i18n)),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("public_reorder_url",
oc->parse_order.public_reorder_url)),
@@ -1984,10 +2138,9 @@ serialize_order (struct OrderContext *oc)
GNUNET_JSON_pack_string ("fulfillment_message",
oc->parse_order.fulfillment_message)),
GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("fulfillment_message_i18n",
- (json_t *) oc->parse_order.
- fulfillment_message_i18n))
- ,
+ GNUNET_JSON_pack_object_incref (
+ "fulfillment_message_i18n",
+ (json_t *) oc->parse_order.fulfillment_message_i18n)),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_string ("fulfillment_url",
oc->parse_order.fulfillment_url)),
@@ -2012,9 +2165,9 @@ serialize_order (struct OrderContext *oc)
GNUNET_JSON_pack_timestamp ("delivery_date",
oc->parse_order.delivery_date)),
GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_incref ("delivery_location",
- (json_t *) oc->parse_order.
- delivery_location)),
+ GNUNET_JSON_pack_object_incref (
+ "delivery_location",
+ (json_t *) oc->parse_order.delivery_location)),
GNUNET_JSON_pack_string ("merchant_base_url",
oc->parse_order.merchant_base_url),
GNUNET_JSON_pack_object_steal ("merchant",
@@ -2023,43 +2176,61 @@ serialize_order (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->set_max_fee.max_fee),
- TALER_JSON_pack_amount ("amount",
- &oc->parse_order.brutto),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_array_steal ("choices",
- choices)
- ),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_object_steal ("token_families",
- token_families)
- ),
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_object_incref ("extra",
(json_t *) oc->parse_order.extra))
);
+ {
+ json_t *xtra;
+
+ switch (oc->parse_order.version)
+ {
+ case TALER_MCV_V0:
+ xtra = GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("max_fee",
+ &oc->set_max_fee.details.v0.max_fee),
+ TALER_JSON_pack_amount ("amount",
+ &oc->parse_order.details.v0.brutto));
+ break;
+ case TALER_MCV_V1:
+ {
+ json_t *token_families = output_token_families (oc);
+ json_t *choices = output_contract_choices (oc);
+
+ if ( (NULL == token_families) ||
+ (NULL == choices) )
+ {
+ GNUNET_break (0);
+ return;
+ }
+ xtra = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("choices",
+ choices),
+ GNUNET_JSON_pack_object_steal ("token_families",
+ token_families));
+ break;
+ }
+ default:
+ GNUNET_assert (0);
+ }
+ GNUNET_assert (0 ==
+ json_object_update (oc->serialize_order.contract,
+ xtra));
+ json_decref (xtra);
+ }
+
+
/* 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))
{
+ /* Pack does not work here, because it sets zero-values for relative times
*/
GNUNET_assert (0 ==
json_object_set_new (oc->serialize_order.contract,
"auto_refund",
@@ -2072,36 +2243,78 @@ serialize_order (struct OrderContext *oc)
/**
- * Set max_fee in @a oc based on STEFAN value if
- * not yet present. Upon success, continue
- * processing with serialize_order().
+ * Set @a max_fee in @a oc based on @a max_stefan_fee value if not overridden
+ * by @a client_fee. If neither is set, set the fee to zero using currency
+ * from @a brutto.
*
* @param[in,out] oc order context
+ * @param brutto brutto amount to compute fee for
+ * @param client_fee client-given fee override (or invalid)
+ * @param max_stefan_fee maximum STEFAN fee of any exchange
+ * @param max_fee set to the maximum stefan fee
*/
static void
-set_max_fee (struct OrderContext *oc)
+compute_fee (struct OrderContext *oc,
+ const struct TALER_Amount *brutto,
+ const struct TALER_Amount *client_fee,
+ const struct TALER_Amount *max_stefan_fee,
+ struct TALER_Amount *max_fee)
{
- const struct TALER_MERCHANTDB_InstanceSettings *settings =
- &oc->hc->instance->settings;
+ const struct TALER_MERCHANTDB_InstanceSettings *settings
+ = &oc->hc->instance->settings;
- if (GNUNET_OK !=
- TALER_amount_is_valid (&oc->parse_order.max_fee))
+ if (GNUNET_OK ==
+ TALER_amount_is_valid (client_fee))
{
- 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.max_fee = stefan;
+ *max_fee = *client_fee;
+ return;
}
- else
+ if ( (settings->use_stefan) &&
+ (GNUNET_OK ==
+ TALER_amount_is_valid (max_stefan_fee)) )
+ {
+ *max_fee = *max_stefan_fee;
+ return;
+ }
+ GNUNET_assert (
+ GNUNET_OK ==
+ TALER_amount_set_zero (brutto->currency,
+ max_fee));
+}
+
+
+/**
+ * Initialize "set_max_fee" in @a oc based on STEFAN value or client
+ * preference. Upon success, continue processing in next phase.
+ *
+ * @param[in,out] oc order context
+ */
+static void
+set_max_fee (struct OrderContext *oc)
+{
+ switch (oc->parse_order.version)
{
- oc->set_max_fee.max_fee = oc->parse_order.max_fee;
+ case TALER_MCV_V0:
+ compute_fee (oc,
+ &oc->parse_order.details.v0.brutto,
+ &oc->parse_order.details.v0.max_fee,
+ &oc->set_exchanges.details.v0.max_stefan_fee,
+ &oc->set_max_fee.details.v0.max_fee);
+ break;
+ case TALER_MCV_V1:
+ oc->set_max_fee.details.v1.max_fees
+ = GNUNET_new_array (oc->parse_choices.choices_len,
+ struct TALER_Amount);
+ for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
+ compute_fee (oc,
+ &oc->parse_choices.choices[i].amount,
+ &oc->parse_choices.choices[i].max_fee,
+ &oc->set_exchanges.details.v1.max_stefan_fees[i],
+ &oc->set_max_fee.details.v1.max_fees[i]);
+ break;
+ default:
+ GNUNET_break (0);
+ break;
}
oc->phase++;
}
@@ -2130,51 +2343,89 @@ resume_with_keys (struct OrderContext *oc)
/**
- * Update MAX STEFAN fees based on @a keys.
+ * Given a @a brutto amount for exchange with @a keys, set the
+ * @a stefan_fee. Note that @a stefan_fee is updated to the maximum
+ * of the input and the computed fee.
*
- * @param[in,out] oc order context to update
- * @param keys keys to derive STEFAN fees from
+ * @param[in,out] oc order context
+ * @param brutto some brutto amount the client is to pay
+ * @param[in,out] stefan_fee set to STEFAN fee to be paid by the merchant
*/
static void
-update_stefan (struct OrderContext *oc,
- const struct TALER_EXCHANGE_Keys *keys)
+compute_stefan_fee (const struct TALER_EXCHANGE_Keys *keys,
+ const struct TALER_Amount *brutto,
+ struct TALER_Amount *stefan_fee)
{
struct TALER_Amount net;
if (GNUNET_SYSERR !=
TALER_EXCHANGE_keys_stefan_b2n (keys,
- &oc->parse_order.brutto,
+ brutto,
&net))
{
struct TALER_Amount fee;
TALER_EXCHANGE_keys_stefan_round (keys,
&net);
- if (-1 == TALER_amount_cmp (&oc->parse_order.brutto,
+ if (-1 == TALER_amount_cmp (brutto,
&net))
{
/* brutto < netto! */
/* => after rounding, there is no real difference */
- net = oc->parse_order.brutto;
+ net = *brutto;
}
GNUNET_assert (0 <=
TALER_amount_subtract (&fee,
- &oc->parse_order.brutto,
+ brutto,
&net));
if ( (GNUNET_OK !=
- TALER_amount_is_valid (&oc->set_exchanges.max_stefan_fee)) ||
- (-1 == TALER_amount_cmp (&oc->set_exchanges.max_stefan_fee,
+ TALER_amount_is_valid (stefan_fee)) ||
+ (-1 == TALER_amount_cmp (stefan_fee,
&fee)) )
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Updated STEFAN-based fee to %s\n",
TALER_amount2s (&fee));
- oc->set_exchanges.max_stefan_fee = fee;
+ *stefan_fee = fee;
}
}
}
+/**
+ * Update MAX STEFAN fees based on @a keys.
+ *
+ * @param[in,out] oc order context to update
+ * @param keys keys to derive STEFAN fees from
+ */
+static void
+update_stefan (struct OrderContext *oc,
+ const struct TALER_EXCHANGE_Keys *keys)
+{
+ switch (oc->parse_order.version)
+ {
+ case TALER_MCV_V0:
+ compute_stefan_fee (keys,
+ &oc->parse_order.details.v0.brutto,
+ &oc->set_exchanges.details.v0.max_stefan_fee);
+ break;
+ case TALER_MCV_V1:
+ oc->set_exchanges.details.v1.max_stefan_fees
+ = GNUNET_new_array (oc->parse_choices.choices_len,
+ struct TALER_Amount);
+ for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
+ if (0 == strcasecmp (keys->currency,
+ oc->parse_choices.choices[i].amount.currency))
+ compute_stefan_fee (keys,
+ &oc->parse_choices.choices[i].amount,
+ &oc->set_exchanges.details.v1.max_stefan_fees[i]);
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+}
+
+
/**
* Compute the set of exchanges that would be acceptable
* for this order.
@@ -2182,11 +2433,13 @@ update_stefan (struct OrderContext *oc,
* @param cls our `struct OrderContext`
* @param url base URL of an exchange (not used)
* @param exchange internal handle for the exchange
+ * @param max_needed maximum amount needed in this currency
*/
static void
get_acceptable (void *cls,
const char *url,
- const struct TMH_Exchange *exchange)
+ const struct TMH_Exchange *exchange,
+ const struct TALER_Amount *max_needed)
{
struct OrderContext *oc = cls;
unsigned int priority = 42; /* make compiler happy */
@@ -2194,7 +2447,7 @@ get_acceptable (void *cls,
enum GNUNET_GenericReturnValue res;
struct TALER_Amount max_amount;
- max_amount = oc->parse_order.brutto;
+ max_amount = *max_needed;
res = TMH_exchange_check_debit (
oc->hc->instance->settings.id,
exchange,
@@ -2205,8 +2458,7 @@ get_acceptable (void *cls,
url,
res,
TALER_amount2s (&max_amount));
- if ( (! TALER_amount_is_zero (&max_amount)) &&
- (TALER_amount_is_zero (&max_amount)) )
+ if (TALER_amount_is_zero (&max_amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Exchange %s deposit limit is zero, skipping it\n",
@@ -2236,20 +2488,47 @@ get_acceptable (void *cls,
"Exchange %s deposit limit is %s, adding it!\n",
url,
TALER_amount2s (&max_amount));
- GNUNET_assert (0 <=
- TALER_amount_add (
- &oc->set_exchanges.total_exchange_limit,
- &oc->set_exchanges.total_exchange_limit,
- &max_amount));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_min (&oc->set_exchanges.total_exchange_limit,
- &oc->set_exchanges.total_exchange_limit,
- &oc->parse_order.brutto));
- j_exchange = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("url",
- url),
- GNUNET_JSON_pack_uint64 ("priority",
- priority),
+ {
+ bool found = false;
+
+ for (unsigned int i = 0; i<oc->set_exchanges.num_total_exchange_limits;
i++)
+ {
+ struct TALER_Amount *limit
+ = &oc->set_exchanges.total_exchange_limits[i];
+
+ if (GNUNET_OK ==
+ TALER_amount_cmp_currency (limit,
+ &max_amount))
+ {
+ GNUNET_assert (0 <=
+ TALER_amount_add (limit,
+ limit,
+ &max_amount));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_min (limit,
+ limit,
+ max_needed));
+ found = true;
+ }
+ }
+ if (! found)
+ {
+ struct TALER_Amount limit;
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_min (&limit,
+ &max_amount,
+ max_needed));
+ GNUNET_array_append (oc->set_exchanges.total_exchange_limits,
+ oc->set_exchanges.num_total_exchange_limits,
+ limit);
+ }
+ }
+ j_exchange = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("url",
+ url),
+ GNUNET_JSON_pack_uint64 ("priority",
+ priority),
TALER_JSON_pack_amount ("max_contribution",
&max_amount),
GNUNET_JSON_pack_data_auto ("master_pub",
@@ -2292,17 +2571,59 @@ keys_cb (
}
else
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Got response for %skeys\n",
- rx->url);
- if ( (settings->use_stefan) &&
- (GNUNET_OK !=
- TALER_amount_is_valid (&oc->parse_order.max_fee)) )
- update_stefan (oc,
- keys);
- get_acceptable (oc,
- rx->url,
- exchange);
+ bool currency_ok = false;
+ struct TALER_Amount max_needed;
+
+ switch (oc->parse_order.version)
+ {
+ case TALER_MCV_V0:
+ if (0 == strcasecmp (keys->currency,
+ oc->parse_order.details.v0.brutto.currency))
+ {
+ max_needed = oc->parse_order.details.v0.brutto;
+ currency_ok = true;
+ }
+ break;
+ case TALER_MCV_V1:
+ for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
+ {
+ const struct TALER_Amount *amount
+ = &oc->parse_choices.choices[i].amount;
+
+ if (0 == strcasecmp (keys->currency,
+ amount->currency))
+ {
+ if (currency_ok)
+ {
+ TALER_amount_max (&max_needed,
+ &max_needed,
+ amount);
+ }
+ else
+ {
+ max_needed = *amount;
+ currency_ok = true;
+ }
+ }
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ if ( (currency_ok) &&
+ (! TALER_amount_is_zero (&max_needed)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Got response for %skeys\n",
+ rx->url);
+ if (settings->use_stefan)
+ update_stefan (oc,
+ keys);
+ get_acceptable (oc,
+ rx->url,
+ exchange,
+ &max_needed);
+ }
}
GNUNET_free (rx->url);
GNUNET_free (rx);
@@ -2409,6 +2730,40 @@ wakeup_timeout (void *cls)
}
+/**
+ * Check that the @a brutto amount is at or below the exchange
+ * limits we have for the respective currency.
+ *
+ * @param oc order context to check
+ * @param brutto amount to check
+ * @param true if the amount is OK, false if it is too high
+ */
+static bool
+check_exchange_limits (const struct OrderContext *oc,
+ struct TALER_Amount *brutto)
+{
+ for (unsigned int i = 0; i<oc->set_exchanges.num_total_exchange_limits; i++)
+ {
+ const struct TALER_Amount *total_exchange_limit
+ = &oc->set_exchanges.total_exchange_limits[i];
+
+ if (GNUNET_OK !=
+ TALER_amount_cmp_currency (brutto,
+ total_exchange_limit))
+ continue;
+ if (1 !=
+ TALER_amount_cmp (brutto,
+ total_exchange_limit))
+ return true;
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Cannot create order: %s is above the sum of hard limits from
supported exchanges\n",
+ TALER_amount2s (brutto));
+ return false;
+}
+
+
/**
* Set list of acceptable exchanges in @a oc. Upon success, continue
* processing with set_max_fee().
@@ -2419,12 +2774,32 @@ wakeup_timeout (void *cls)
static bool
set_exchanges (struct OrderContext *oc)
{
+ bool need_exchange;
+
if (NULL != oc->set_exchanges.wakeup_task)
{
GNUNET_SCHEDULER_cancel (oc->set_exchanges.wakeup_task);
oc->set_exchanges.wakeup_task = NULL;
}
- if (TALER_amount_is_zero (&oc->parse_order.brutto))
+ switch (oc->parse_order.version)
+ {
+ case TALER_MCV_V0:
+ need_exchange = ! TALER_amount_is_zero (
+ &oc->parse_order.details.v0.brutto);
+ break;
+ case TALER_MCV_V1:
+ need_exchange = false;
+ for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
+ if (! TALER_amount_is_zero (&oc->parse_choices.choices[i].amount))
+ {
+ need_exchange = true;
+ break;
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ if (! need_exchange)
{
/* Total amount is zero, so we don't actually need exchanges! */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -2435,21 +2810,13 @@ set_exchanges (struct OrderContext *oc)
return false;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Order total is %s, trying to find exchanges\n",
- TALER_amount2s (&oc->parse_order.brutto));
- /* Note: re-building 'oc->set_exchanges.exchanges' every time here might be a
- tad expensive; could likely consider caching the result if it starts to
- matter. */
+ "Trying to find exchanges\n");
if (NULL == oc->set_exchanges.exchanges)
{
oc->set_exchanges.keys_timeout
= GNUNET_TIME_relative_to_absolute (MAX_KEYS_WAIT);
oc->set_exchanges.exchanges = json_array ();
GNUNET_assert (NULL != oc->set_exchanges.exchanges);
- GNUNET_assert (
- GNUNET_OK ==
- TALER_amount_set_zero (oc->parse_order.brutto.currency,
- &oc->set_exchanges.total_exchange_limit));
TMH_exchange_get_trusted (&get_exchange_keys,
oc);
}
@@ -2505,32 +2872,52 @@ set_exchanges (struct OrderContext *oc)
oc->add_payment_details.wm->wire_method);
return false;
}
- if (1 ==
- TALER_amount_cmp (&oc->parse_order.brutto,
- &oc->set_exchanges.total_exchange_limit))
+
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Cannot create order: %s is the sum of hard limits from
supported exchanges\n",
- TALER_amount2s (&oc->set_exchanges.total_exchange_limit));
- notify_kyc_required (oc);
- reply_with_error (
- oc,
- MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
- TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_AMOUNT_EXCEEDS_LEGAL_LIMITS,
- TALER_amount2s (&oc->set_exchanges.total_exchange_limit));
- return false;
+ bool ok;
+ struct TALER_Amount ea;
+
+ switch (oc->parse_order.version)
+ {
+ case TALER_MCV_V0:
+ ea = oc->parse_order.details.v0.brutto;
+ ok = check_exchange_limits (oc,
+ &ea);
+ break;
+ case TALER_MCV_V1:
+ ok = true;
+ for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
+ {
+ ea = oc->parse_choices.choices[i].amount;
+ if (! check_exchange_limits (oc,
+ &ea))
+ {
+ ok = false;
+ break;
+ }
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+
+ if (! ok)
+ {
+ notify_kyc_required (oc);
+ reply_with_error (
+ oc,
+ MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
+ TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_AMOUNT_EXCEEDS_LEGAL_LIMITS,
+ TALER_amount2s (&ea));
+ return false;
+ }
}
+
if (! oc->set_exchanges.exchange_good)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Creating order, but possibly without usable trusted
exchanges\n");
}
- else
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Can create order: %s is the sum of hard limits from supported
exchanges\n",
- TALER_amount2s (&oc->set_exchanges.total_exchange_limit));
- }
oc->phase++;
return false;
}
@@ -2550,18 +2937,12 @@ parse_order (struct OrderContext *oc)
const char *merchant_base_url = NULL;
uint64_t version = 0;
const json_t *jmerchant = NULL;
- /* auto_refund only needs to be type-checked,
- * mostly because in GNUnet relative times can't
- * be negative. */
- bool no_fee;
- const char *oid;
+ const char *order_id;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_uint64 ("version",
&version),
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 (
@@ -2574,7 +2955,7 @@ parse_order (struct OrderContext *oc)
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("order_id",
- &oid),
+ &order_id),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("fulfillment_message",
@@ -2592,14 +2973,11 @@ parse_order (struct OrderContext *oc)
GNUNET_JSON_spec_string ("public_reorder_url",
&oc->parse_order.public_reorder_url),
NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_array_const ("choices",
- &oc->parse_order.choices),
- NULL),
GNUNET_JSON_spec_mark_optional (
TALER_JSON_spec_web_url ("merchant_base_url",
&merchant_base_url),
NULL),
+ /* For sanity check, this field must NOT be present */
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_object_const ("merchant",
&jmerchant),
@@ -2620,10 +2998,6 @@ parse_order (struct OrderContext *oc)
GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
&oc->parse_order.wire_deadline),
NULL),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount_any ("max_fee",
- &oc->parse_order.max_fee),
- &no_fee),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_object_const ("delivery_location",
&oc->parse_order.delivery_location),
@@ -2660,36 +3034,100 @@ parse_order (struct OrderContext *oc)
ret);
return;
}
- if (0 == version)
+ switch (version)
{
- oc->parse_order.version = TALER_MCV_V0;
- if (NULL != oc->parse_order.choices)
+ case 0:
{
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- reply_with_error (oc,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_UNEXPECTED_REQUEST_ERROR,
- "choices array must be null for v0 contracts");
- return;
+ bool no_fee;
+ const json_t *choices = NULL;
+ struct GNUNET_JSON_Specification specv0[] = {
+ TALER_JSON_spec_amount_any (
+ "amount",
+ &oc->parse_order.details.v0.brutto),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount_any (
+ "max_fee",
+ &oc->parse_order.details.v0.max_fee),
+ &no_fee),
+ /* for sanity check, must be *absent*! */
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const ("choices",
+ &choices),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ ret = TALER_MHD_parse_json_data (oc->connection,
+ oc->parse_request.order,
+ specv0);
+ if (GNUNET_OK != ret)
+ {
+ GNUNET_break_op (0);
+ finalize_order2 (oc,
+ ret);
+ return;
+ }
+ if ( (! no_fee) &&
+ (GNUNET_OK !=
+ TALER_amount_cmp_currency (&oc->parse_order.details.v0.brutto,
+ &oc->parse_order.details.v0.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;
+ }
+ if (! TMH_test_exchange_configured_for_currency (
+ oc->parse_order.details.v0.brutto.currency))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ // FIXME: use CONFLICT and a different EC!
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_CURRENCY_MISMATCH,
+ "no trusted exchange for this currency");
+ return;
+ }
+ if (NULL != choices)
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_UNEXPECTED_REQUEST_ERROR,
+ "choices array must be null for v0 contracts");
+ return;
+ }
+ oc->parse_order.version = TALER_MCV_V0;
+ break;
}
- }
- else if (1 == version)
- {
- oc->parse_order.version = TALER_MCV_V1;
- if (NULL == oc->parse_order.choices)
+ case 1:
{
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- reply_with_error (oc,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "order.choices is required in v1 contracts");
- return;
+ struct GNUNET_JSON_Specification specv1[] = {
+ GNUNET_JSON_spec_array_const (
+ "choices",
+ &oc->parse_order.details.v1.choices),
+ GNUNET_JSON_spec_end ()
+ };
+
+ ret = TALER_MHD_parse_json_data (oc->connection,
+ oc->parse_request.order,
+ specv1);
+ if (GNUNET_OK != ret)
+ {
+ GNUNET_break_op (0);
+ finalize_order2 (oc,
+ ret);
+ return;
+ }
+ oc->parse_order.version = TALER_MCV_V1;
+ break;
}
- }
- else
- {
+ default:
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
reply_with_error (oc,
@@ -2698,35 +3136,11 @@ parse_order (struct OrderContext *oc)
"invalid version specified in order, supported are null,
'0' or '1'");
return;
}
- if (! TMH_test_exchange_configured_for_currency (
- oc->parse_order.brutto.currency))
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- reply_with_error (oc,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_CURRENCY_MISMATCH,
- "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 != oid)
+ if (NULL != order_id)
{
- oc->parse_order.order_id = GNUNET_strdup (oid);
+ oc->parse_order.order_id = GNUNET_strdup (order_id);
}
else
{
@@ -3000,6 +3414,217 @@ parse_order (struct OrderContext *oc)
}
+/**
+ * Parse the inputs for a particular choice.
+ *
+ * @param[in,out] oc order context
+ * @param[out] choice to parse inputs for
+ * @param jinputs array of inputs to parse
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR
+ * if an error was encountered (and already handled)
+ */
+static enum GNUNET_GenericReturnValue
+parse_order_inputs (struct OrderContext *oc,
+ struct TALER_MerchantContractChoice *choice,
+ const json_t *jinputs)
+{
+ const json_t *jinput;
+ size_t idx;
+
+ json_array_foreach ((json_t *) jinputs, idx, jinput)
+ {
+ struct TALER_MerchantContractInput input = {
+ .details.token.count = 1
+ };
+ const char *kind;
+ const char *ierror_name;
+ unsigned int ierror_line;
+ struct GNUNET_JSON_Specification ispec[] = {
+ // FIXME: define spec parser for 'kind'...
+ GNUNET_JSON_spec_string ("kind",
+ &kind),
+ GNUNET_JSON_spec_string ("token_family_slug",
+ &input.details.token.token_family_slug),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("count",
+ &input.details.token.count),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (jinput,
+ ispec,
+ &ierror_name,
+ &ierror_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid input #%u for field %s\n",
+ (unsigned int) idx,
+ ierror_name);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ ierror_name);
+ return GNUNET_SYSERR;
+ }
+
+ input.type = TMH_contract_input_type_from_string (kind);
+ if (TALER_MCIT_INVALID == input.type)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Field 'kind' invalid in input #%u\n",
+ (unsigned int) idx);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "kind");
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == input.details.token.count)
+ {
+ /* Ignore inputs with 'number' field set to 0 */
+ continue;
+ }
+
+ if (GNUNET_OK !=
+ add_input_token_family (oc,
+ input.details.token.token_family_slug))
+ {
+ /* error is already scheduled, return. */
+ return GNUNET_SYSERR;
+ }
+
+ GNUNET_array_append (choice->inputs,
+ choice->inputs_len,
+ input);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Parse the outputs for a particular choice.
+ *
+ * @param[in,out] oc order context
+ * @param[out] choice to parse inputs for
+ * @param joutputs array of outputs to parse
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR
+ * if an error was encountered (and already handled)
+ */
+static enum GNUNET_GenericReturnValue
+parse_order_outputs (struct OrderContext *oc,
+ struct TALER_MerchantContractChoice *choice,
+ const json_t *joutputs)
+{
+ const json_t *joutput;
+ size_t idx;
+
+ json_array_foreach ((json_t *) joutputs, idx, joutput)
+ {
+ struct TALER_MerchantContractOutput output = {
+ .details.token.count = 1
+ };
+ const char *kind;
+ const char *ierror_name;
+ unsigned int ierror_line;
+ bool nots;
+ struct GNUNET_TIME_Timestamp valid_at;
+ struct GNUNET_JSON_Specification ispec[] = {
+ // FIXME: define spec parser for 'kind'...
+ GNUNET_JSON_spec_string ("kind",
+ &kind),
+ GNUNET_JSON_spec_string ("token_family_slug",
+ &output.details.token.token_family_slug),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("count",
+ &output.details.token.count),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_timestamp ("valid_at",
+ &valid_at),
+ ¬s),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (joutput,
+ ispec,
+ &ierror_name,
+ &ierror_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Invalid output #%u for field %s\n",
+ (unsigned int) idx,
+ ierror_name);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ ierror_name);
+ return GNUNET_SYSERR;
+ }
+ if (nots)
+ {
+ valid_at = oc->parse_order.pay_deadline;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Looking for output token valid at pay deadline %s\n",
+ GNUNET_TIME_timestamp2s (valid_at));
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Looking for output token valid at %s\n",
+ GNUNET_TIME_timestamp2s (valid_at));
+ }
+ if (GNUNET_TIME_timestamp_cmp (valid_at,
+ <,
+ oc->parse_order.pay_deadline))
+ {
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "valid_at before pay_deadline");
+ return GNUNET_SYSERR;
+ }
+
+ output.type = TMH_contract_output_type_from_string (kind);
+ if (TALER_MCOT_INVALID == output.type)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Field 'kind' invalid in output #%u\n",
+ (unsigned int) idx);
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "kind");
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == output.details.token.count)
+ {
+ /* Ignore outputs with 'number' field set to 0. */
+ continue;
+ }
+
+ if (GNUNET_OK !=
+ add_output_token_family (oc,
+ output.details.token.token_family_slug,
+ valid_at,
+ &output.details.token.key_index))
+ {
+ /* Error is already scheduled, return. */
+ return GNUNET_SYSERR;
+ }
+
+ GNUNET_array_append (choice->outputs,
+ choice->outputs_len,
+ output);
+ }
+ return GNUNET_OK;
+}
+
+
/**
* Parse contract choices. Upon success, continue
* processing with merge_inventory().
@@ -3009,15 +3634,24 @@ parse_order (struct OrderContext *oc)
static void
parse_choices (struct OrderContext *oc)
{
- if (NULL == oc->parse_order.choices)
+ const json_t *choices;
+
+ switch (oc->parse_order.version)
{
+ case TALER_MCV_V0:
oc->phase++;
return;
+ case TALER_MCV_V1:
+ /* handle below */
+ break;
+ default:
+ GNUNET_assert (0);
}
+ choices = oc->parse_order.details.v1.choices;
GNUNET_array_grow (oc->parse_choices.choices,
oc->parse_choices.choices_len,
- json_array_size (oc->parse_order.choices));
+ json_array_size (choices));
for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
{
struct TALER_MerchantContractChoice *choice
@@ -3026,13 +3660,14 @@ parse_choices (struct OrderContext *oc)
unsigned int error_line;
const json_t *jinputs;
const json_t *joutputs;
+ bool no_fee;
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount_any ("amount",
&choice->amount),
GNUNET_JSON_spec_mark_optional (
TALER_JSON_spec_amount_any ("max_fee",
&choice->max_fee),
- NULL),
+ &no_fee),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_array_const ("inputs",
&jinputs),
@@ -3045,7 +3680,7 @@ parse_choices (struct OrderContext *oc)
};
enum GNUNET_GenericReturnValue ret;
- ret = GNUNET_JSON_parse (json_array_get (oc->parse_order.choices,
+ ret = GNUNET_JSON_parse (json_array_get (choices,
i),
spec,
&error_name,
@@ -3062,6 +3697,33 @@ parse_choices (struct OrderContext *oc)
"choice");
return;
}
+ if ( (! no_fee) &&
+ (GNUNET_OK !=
+ TALER_amount_cmp_currency (&choice->amount,
+ &choice->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;
+ }
+
+ if (! TMH_test_exchange_configured_for_currency (
+ choice->amount.currency))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ // FIXME: use CONFLICT and a different EC!
+ reply_with_error (oc,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_CURRENCY_MISMATCH,
+ "no trusted exchange for this currency");
+ return;
+ }
+
if ( (0 == json_array_size (jinputs)) &&
(0 == json_array_size (joutputs)) )
@@ -3075,195 +3737,17 @@ parse_choices (struct OrderContext *oc)
"choice");
return;
}
-
- {
- // TODO: Maybe move to a separate function
- const json_t *jinput;
- size_t idx;
- json_array_foreach ((json_t *) jinputs, idx, jinput)
- {
- struct TALER_MerchantContractInput input = {
- .details.token.count = 1
- };
- const char *kind;
- const char *ierror_name;
- unsigned int ierror_line;
- struct GNUNET_JSON_Specification ispec[] = {
- GNUNET_JSON_spec_string ("kind",
- &kind),
- GNUNET_JSON_spec_string ("token_family_slug",
- &input.details.token.token_family_slug),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_uint32 ("count",
- &input.details.token.count),
- NULL),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (jinput,
- ispec,
- &ierror_name,
- &ierror_line))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid input #%u for field %s\n",
- (unsigned int) idx,
- ierror_name);
- reply_with_error (oc,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- ierror_name);
- return;
- }
-
- input.type = TMH_contract_input_type_from_string (kind);
-
- if (TALER_MCIT_INVALID == input.type)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Field 'kind' invalid in input #%u\n",
- (unsigned int) idx);
- reply_with_error (oc,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "kind");
- return;
- }
-
- if (0 == input.details.token.count)
- {
- /* Ignore inputs with 'number' field set to 0 */
- continue;
- }
-
- if (GNUNET_OK !=
- add_input_token_family (oc,
- input.details.token.token_family_slug))
- {
- /* error is already scheduled, return. */
- return;
- }
-
- GNUNET_array_append (choice->inputs,
- choice->inputs_len,
- input);
- }
- }
-
- {
- const json_t *joutput;
- size_t idx;
- json_array_foreach ((json_t *) joutputs, idx, joutput)
- {
- struct TALER_MerchantContractOutput output = {
- .details.token.count = 1
- };
- const char *kind;
- const char *ierror_name;
- unsigned int ierror_line;
- bool nots;
- struct GNUNET_TIME_Timestamp valid_at;
- struct GNUNET_JSON_Specification ispec[] = {
- GNUNET_JSON_spec_string ("kind",
- &kind),
- GNUNET_JSON_spec_string ("token_family_slug",
- &output.details.token.token_family_slug),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_uint32 ("count",
- &output.details.token.count),
- NULL),
- GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_timestamp ("valid_at",
- &valid_at),
- ¬s),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (joutput,
- ispec,
- &ierror_name,
- &ierror_line))
- {
- GNUNET_JSON_parse_free (spec);
- GNUNET_JSON_parse_free (ispec);
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid output #%u for field %s\n",
- (unsigned int) idx,
- ierror_name);
- reply_with_error (oc,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- ierror_name);
- return;
- }
- if (nots)
- {
- valid_at = oc->parse_order.pay_deadline;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Looking for output token valid at pay deadline %s\n",
- GNUNET_TIME_timestamp2s (valid_at));
- }
- else
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Looking for output token valid at %s\n",
- GNUNET_TIME_timestamp2s (valid_at));
- }
-
-
- if (GNUNET_TIME_timestamp_cmp (valid_at,
- <,
- oc->parse_order.pay_deadline))
- {
- GNUNET_JSON_parse_free (spec);
- GNUNET_JSON_parse_free (ispec);
- reply_with_error (oc,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "valid_at before pay_deadline");
- return;
- }
-
- output.type = TMH_contract_output_type_from_string (kind);
- if (TALER_MCOT_INVALID == output.type)
- {
- GNUNET_JSON_parse_free (spec);
- GNUNET_JSON_parse_free (ispec);
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Field 'kind' invalid in output #%u\n",
- (unsigned int) idx);
- reply_with_error (oc,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "kind");
- return;
- }
-
- if (0 == output.details.token.count)
- {
- /* Ignore outputs with 'number' field set to 0. */
- continue;
- }
-
- if (GNUNET_OK !=
- add_output_token_family (oc,
- output.details.token.token_family_slug,
- valid_at,
- &output.details.token.key_index))
- {
- /* Error is already scheduled, return. */
- return;
- }
-
- GNUNET_array_append (choice->outputs,
- choice->outputs_len,
- output);
- }
- }
+ if (GNUNET_OK !=
+ parse_order_inputs (oc,
+ choice,
+ jinputs))
+ return;
+ if (GNUNET_OK !=
+ parse_order_outputs (oc,
+ choice,
+ joutputs))
+ return;
}
-
oc->phase++;
}
diff --git a/src/include/taler_merchant_testing_lib.h
b/src/include/taler_merchant_testing_lib.h
index d3bf0d1c..e405d49a 100644
--- a/src/include/taler_merchant_testing_lib.h
+++ b/src/include/taler_merchant_testing_lib.h
@@ -613,7 +613,7 @@ TALER_TESTING_cmd_merchant_post_orders3 (
* @param merchant_url base URL of the merchant serving
* the proposal request.
* @param http_status expected HTTP status.
- * @param token_family_reference label of the POST /tokenfamilies cmd.
+ * @param token_family_slug slug of the token family to use
* @param num_inputs number of input tokens.
* @param num_outputs number of output tokens.
* @param order_id the name of the order to add.
@@ -629,7 +629,7 @@ TALER_TESTING_cmd_merchant_post_orders_choices (
const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *merchant_url,
unsigned int http_status,
- const char *token_family_reference,
+ const char *token_family_slug,
unsigned int num_inputs,
unsigned int num_outputs,
const char *order_id,
diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c
index 24118031..99a54281 100644
--- a/src/testing/test_merchant_api.c
+++ b/src/testing/test_merchant_api.c
@@ -1714,7 +1714,7 @@ run (void *cls,
cred.cfg,
merchant_url,
MHD_HTTP_OK,
- "create-upcoming-tokenfamily",
+ "subscription-upcoming",
0,
1,
"5-upcoming-output",
@@ -1740,7 +1740,7 @@ run (void *cls,
cred.cfg,
merchant_url,
MHD_HTTP_OK,
- "create-tokenfamily",
+ "subscription-1",
0,
1,
"5-output",
@@ -1763,7 +1763,7 @@ run (void *cls,
cred.cfg,
merchant_url,
MHD_HTTP_OK,
- "create-tokenfamily",
+ "subscription-1",
1,
1,
"5-input-output",
@@ -1796,7 +1796,7 @@ run (void *cls,
cred.cfg,
merchant_url,
MHD_HTTP_OK,
- "create-tokenfamily",
+ "subscription-1",
1,
1,
"5-input-output-2",
diff --git a/src/testing/test_merchant_order_creation.sh
b/src/testing/test_merchant_order_creation.sh
index d55f7ce1..cb70c22e 100755
--- a/src/testing/test_merchant_order_creation.sh
+++ b/src/testing/test_merchant_order_creation.sh
@@ -266,11 +266,11 @@ fi
echo " OK"
echo "curl 'http://localhost:9966/private/orders' \
- -d
'{\"order\":{\"version\":1,\"amount\":\"TESTKUDOS:7\",\"summary\":\"with_subscription\",\"fulfillment_message\":\"Paid
successfully\",\"choices\":[{\"inputs\":[{\"kind\":\"token\",\"count\":1,\"token_family_slug\":\"test-sub\",\"valid_after\":{\"t_s\":'$NOW'}}],\"outputs\":[{\"kind\":\"token\",\"count\":1,\"token_family_slug\":\"test-sub\",\"valid_after\":{\"t_s\":'$NOW'}}]}]}}'"
+ -d
'{\"order\":{\"version\":1,\"summary\":\"with_subscription\",\"fulfillment_message\":\"Paid
successfully\",\"choices\":[\{\"amount\":\"TESTKUDOS:7","inputs\":[{\"kind\":\"token\",\"count\":1,\"token_family_slug\":\"test-sub\",\"valid_after\":{\"t_s\":'$NOW'}}],\"outputs\":[{\"kind\":\"token\",\"count\":1,\"token_family_slug\":\"test-sub\",\"valid_after\":{\"t_s\":'$NOW'}}]}]}}'"
echo -n "Creating v1 order with token family ..."
STATUS=$(curl 'http://localhost:9966/private/orders' \
- -d
'{"order":{"version":1,"amount":"TESTKUDOS:7","summary":"with_subscription","fulfillment_message":"Paid
successfully","choices":[{"inputs":[{"kind":"token","count":1,"token_family_slug":"test-sub","valid_after":{"t_s":'$NOW'}}],"outputs":[{"kind":"token","count":1,"token_family_slug":"test-sub","valid_after":{"t_s":'$NOW'}}]}]}}'
\
+ -d
'{"order":{"version":1,"summary":"with_subscription","fulfillment_message":"Paid
successfully","choices":[{"amount":"TESTKUDOS:7","inputs":[{"kind":"token","count":1,"token_family_slug":"test-sub","valid_after":{"t_s":'$NOW'}}],"outputs":[{"kind":"token","count":1,"token_family_slug":"test-sub","valid_after":{"t_s":'$NOW'}}]}]}}'
\
-w "%{http_code}" -s -o "$LAST_RESPONSE")
if [ "$STATUS" != "200" ]
diff --git a/src/testing/testing_api_cmd_post_orders.c
b/src/testing/testing_api_cmd_post_orders.c
index 04b258eb..bef760bf 100644
--- a/src/testing/testing_api_cmd_post_orders.c
+++ b/src/testing/testing_api_cmd_post_orders.c
@@ -59,23 +59,6 @@ struct OrdersState
*/
const char *expected_order_id;
- /**
- * Reference to a POST /tokenfamilies command. Can be NULL.
- */
- const char *token_family_reference;
-
- /**
- * How many tokens of the token family created in
- * @a token_family_reference are required as inputs.
- */
- unsigned int num_inputs;
-
- /**
- * How many tokens of the token family created in
- * @a token_family_reference should be issued as outputs.
- */
- unsigned int num_outputs;
-
/**
* Contract terms obtained from the backend.
*/
@@ -86,11 +69,6 @@ struct OrdersState
*/
json_t *order_terms;
- /**
- * Choices array with inputs and outputs for v1 order.
- */
- json_t *choices;
-
/**
* Contract terms hash code.
*/
@@ -588,48 +566,6 @@ orders_run2 (void *cls,
}
-/**
- * Constructs the json for a the choices of an order request.
- *
- * @param input_slug the name of the token family to use for input, can be NULL
- * @param output_slug the name of the token family to use for the output, can
be NULL.
- * @param input_count number of token inputs to require
- * @param output_count number of tokens to output
- * @param input_valid_after validity date for the input token.
- * @param output_valid_after validity date for the output token.
- * @param[out] choices where to write the json string.
- */
-static void
-make_choices_json (
- const char *input_slug,
- const char *output_slug,
- uint16_t input_count,
- uint16_t output_count,
- struct GNUNET_TIME_Timestamp input_valid_after,
- struct GNUNET_TIME_Timestamp output_valid_after,
- json_t **choices)
-{
- /* FIXME: ugly code should return c, use GNUNET_JSON_PACK() for more
type-safety */
- json_t *c;
-
- c = json_pack ("[{s:o, s:o}]",
- "inputs", json_pack ("[{s:s, s:i, s:s, s:o}]",
- "kind", "token",
- "count", input_count,
- "token_family_slug", input_slug,
- "valid_after", GNUNET_JSON_from_timestamp
- (input_valid_after)),
- "outputs", json_pack ("[{s:s, s:i, s:s, s:o}]",
- "kind", "token",
- "count", output_count,
- "token_family_slug", output_slug,
- "valid_after",
GNUNET_JSON_from_timestamp
- (output_valid_after)));
-
- *choices = c;
-}
-
-
/**
* Run a "orders" CMD.
*
@@ -644,7 +580,6 @@ orders_run3 (void *cls,
{
struct OrdersState *ps = cls;
struct GNUNET_TIME_Absolute now;
- const char *slug;
ps->is = is;
now = GNUNET_TIME_absolute_get_monotonic (ps->cfg);
@@ -663,37 +598,6 @@ orders_run3 (void *cls,
GNUNET_free (order_id);
}
- {
- const struct TALER_TESTING_Command *token_family_cmd;
- token_family_cmd =
- TALER_TESTING_interpreter_lookup_command (is,
- ps->token_family_reference);
- if (NULL == token_family_cmd)
- TALER_TESTING_FAIL (is);
- if (GNUNET_OK !=
- TALER_TESTING_get_trait_token_family_slug (token_family_cmd,
- &slug))
- TALER_TESTING_FAIL (is);
- }
- make_choices_json (slug, slug,
- ps->num_inputs,
- ps->num_outputs,
- GNUNET_TIME_absolute_to_timestamp (now),
- GNUNET_TIME_absolute_to_timestamp (now),
- &ps->choices);
-
- GNUNET_assert (0 ==
- json_object_set_new (ps->order_terms,
- "choices",
- ps->choices)
- );
- GNUNET_assert (0 ==
- json_object_set_new (ps->order_terms,
- "version",
- json_integer (1))
- );
-
-
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&ps->nonce,
sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
@@ -770,7 +674,7 @@ mark_forgettable (void *cls,
* @param order_id the name of the order to add, can be NULL.
* @param refund_deadline the deadline for refunds on this order.
* @param pay_deadline the deadline for payment on this order.
- * @param amount the amount this order is for.
+ * @param amount the amount this order is for, NULL for v1 orders
* @param[out] order where to write the json string.
*/
static void
@@ -786,7 +690,7 @@ make_order_json (const char *order_id,
/* Include required fields and some dummy objects to test forgetting. */
contract_terms = json_pack (
- "{s:s, s:s?, s:s, s:s, s:o, s:o, s:s, s:[{s:s}, {s:s}, {s:s}]}",
+ "{s:s, s:s?, s:s?, s:s, s:o, s:o, s:s, s:[{s:s}, {s:s}, {s:s}]}",
"summary", "merchant-lib testcase",
"order_id", order_id,
"amount", amount,
@@ -981,7 +885,7 @@ TALER_TESTING_cmd_merchant_post_orders_choices (
const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *merchant_url,
unsigned int http_status,
- const char *token_family_reference,
+ const char *token_family_slug,
unsigned int num_inputs,
unsigned int num_outputs,
const char *order_id,
@@ -990,18 +894,75 @@ TALER_TESTING_cmd_merchant_post_orders_choices (
const char *amount)
{
struct OrdersState *ps;
+ struct TALER_Amount brutto;
+ json_t *choice;
+ json_t *choices;
+ json_t *inputs;
+ json_t *outputs;
ps = GNUNET_new (struct OrdersState);
ps->cfg = cfg;
make_order_json (order_id,
refund_deadline,
pay_deadline,
- amount,
+ NULL,
&ps->order_terms);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (amount,
+ &brutto));
+ inputs = json_array ();
+ GNUNET_assert (NULL != inputs);
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ inputs,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("kind",
+ "token"),
+ GNUNET_JSON_pack_uint64 ("count",
+ num_inputs),
+ GNUNET_JSON_pack_string ("token_family_slug",
+ token_family_slug)
+ )));
+ outputs = json_array ();
+ GNUNET_assert (NULL != outputs);
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ outputs,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("kind",
+ "token"),
+ GNUNET_JSON_pack_uint64 ("count",
+ num_outputs),
+ GNUNET_JSON_pack_string ("token_family_slug",
+ token_family_slug)
+ )));
+ choice
+ = GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("amount",
+ &brutto),
+ GNUNET_JSON_pack_array_steal ("inputs",
+ inputs),
+ GNUNET_JSON_pack_array_steal ("outputs",
+ outputs));
+ choices = json_array ();
+ GNUNET_assert (NULL != choices);
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ choices,
+ choice));
+ GNUNET_assert (0 ==
+ json_object_set_new (ps->order_terms,
+ "choices",
+ choices)
+ );
+ GNUNET_assert (0 ==
+ json_object_set_new (ps->order_terms,
+ "version",
+ json_integer (1))
+ );
+
+
ps->http_status = http_status;
- ps->token_family_reference = token_family_reference;
- ps->num_inputs = num_inputs;
- ps->num_outputs = num_outputs;
ps->expected_order_id = order_id;
ps->merchant_url = merchant_url;
ps->with_claim = true;
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-merchant] branch master updated: -fix order creation logic for contract v1,
gnunet <=