From 129380c78c4fa631a2b1aeb0197255afdb5b469f Mon Sep 17 00:00:00 2001 From: Christian Blättler Date: Tue, 12 Dec 2023 10:12:14 +0100 Subject: start with post orders handler refactoring --- .../taler-merchant-httpd_private-post-orders.c | 1038 ++++++++++++-------- 1 file changed, 612 insertions(+), 426 deletions(-) (limited to 'src/backend/taler-merchant-httpd_private-post-orders.c') diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c index 31b99536..8a2acb5d 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders.c +++ b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -24,7 +24,9 @@ * @author Marcello Stanisci */ #include "platform.h" +#include #include +#include #include #include #include "taler-merchant-httpd_private-post-orders.h" @@ -132,147 +134,308 @@ struct RekeyExchange */ struct OrderContext { - /** - * Connection of the request. + * Information set in the ORDER_PHASE_INIT phase. */ - struct MHD_Connection *connection; + struct { + /** + * Order field of the request + */ + json_t *order; - /** - * Kept in a DLL while suspended. - */ - struct OrderContext *next; + /** + * Set to how long refunds will be allowed. + */ + struct GNUNET_TIME_Relative refund_delay; - /** - * Kept in a DLL while suspended. - */ - struct OrderContext *prev; + /** + * RFC8905 payment target type to find a matching merchant account + */ + const char *payment_target; - /** - * Handler context for the request. - */ - struct TMH_HandlerContext *hc; + /** + * Shared key to use with @e pos_algorithm. + */ + const char *pos_key; - /** - * Hash of the POST request data, used to detect - * idempotent requests. - */ - struct TALER_MerchantPostDataHashP h_post_data; + /** + * Selected algorithm (by template) when we are to + * generate an OTP code for payment confirmation. + */ + enum TALER_MerchantConfirmationAlgorithm pos_algorithm; - /** - * Payment deadline. - */ - struct GNUNET_TIME_Timestamp pay_deadline; + /** + * Hash of the POST request data, used to detect + * idempotent requests. + */ + struct TALER_MerchantPostDataHashP h_post_data; - /** - * Set to how long refunds will be allowed. - */ - struct GNUNET_TIME_Relative refund_delay; + /** + * Length of the @e inventory_products array. + */ + unsigned int inventory_products_length; - /** - * Order we are building (modified as we process - * the request). - */ - json_t *order; + /** + * Array of inventory products in the @e order. + */ + struct InventoryProduct *inventory_products; - /** - * 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; + /** + * Length of the @e uuids array. + */ + unsigned int uuids_length; - /** - * Maximum fee as given by the client request. - */ - struct TALER_Amount max_fee; + /** + * array of UUIDs used to reserve products from @a inventory_products. + */ + struct GNUNET_Uuid *uuids; - /** - * Gross amount value of the contract. Used to - * compute @e max_stefan_fee. - */ - struct TALER_Amount brutto; + /** + * Claim token for the request. + */ + struct TALER_ClaimTokenP claim_token; - /** - * Array of exchanges we find acceptable for this - * order. - */ - json_t *exchanges; + } init; - /** - * RFC8905 payment target type to find a matching merchant account - */ - const char *payment_target; /** - * Wire method (and our bank account) we have selected - * to be included for this order. + * Information set in the ORDER_PHASE_MERGE_INVENTORY phase. */ - const struct TMH_WireMethod *wm; + struct { + /** + * Merged array of products in the @e order. + */ + json_t *products; + } merge_inventory; /** - * Forced requests to /keys to update our exchange - * information. + * Information set in the ORDER_PHASE_ADD_PAYMENT_DETAILS phase. */ - struct RekeyExchange *pending_reload_head; + struct { + /** + * Wire method (and our bank account) we have selected + * to be included for this order. + */ + const struct TMH_WireMethod *wm; + } add_payment_details; /** - * Forced requests to /keys to update our exchange - * information. + * Information set in the ORDER_PHASE_PATCH_ORDER phase. */ - struct RekeyExchange *pending_reload_tail; + struct { + /** + * Our order ID. + */ + const char *order_id; - /** - * Claim token for the request. - */ - struct TALER_ClaimTokenP claim_token; + /** + * Summary of the order. + */ + const char *summary; + + /** + * Internationalized summary. + */ + json_t *summary_i18n; + + /** + * URL where the same contract could be ordered again (if available). + */ + const char *public_reorder_url; + + /** + * URL that will show that the order was successful + * after it has been paid for. + */ + const char *fulfillment_url; + + /** + * Message shown to the customer after paying for the order. + * 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; + + /** + * Merchant base URL. + */ + const char *merchant_base_url; + + /** + * Timestamp of the order. + */ + struct GNUNET_TIME_Timestamp timestamp; + + /** + * Deadline for refunds. + */ + struct GNUNET_TIME_Timestamp refund_deadline; + + /** + * Delivery date. + */ + struct GNUNET_TIME_Timestamp delivery_date; + + /** + * Payment deadline. + */ + struct GNUNET_TIME_Timestamp pay_deadline; + + /** + * Wire transfer deadline. + */ + struct GNUNET_TIME_Timestamp wire_deadline; + + /** + * TODO: Maybe remove this and set it from settings where we serialize + * the order to JSON? + * + * Information like name, website, email, etc. about the merchant. + */ + json_t *merchant; + + /** + * TODO: Maybe remove this and set it from settings where we serialize + * the order to JSON? + * + * Merchant's public key + */ + struct TALER_MerchantPublicKeyP merchant_pub; + + /** + * 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; + + /** + * Delivery location. + */ + const json_t *delivery_location; + + /** + * Specifies for how long the wallet should try to get an + * automatic refund for the purchase. + */ + struct GNUNET_TIME_Relative auto_refund; + + /** + * Nonce generated by the wallet and echoed by the merchant + * in this field when the proposal is generated. + */ + const char *nonce; + + /** + * Extra data that is only interpreted by the merchant frontend. + */ + json_t *extra; + + } patch_order; /** - * Length of the @e inventory_products array. + * Information set in the ORDER_PHASE_SET_EXCHANGES phase. */ - unsigned int inventory_products_length; + struct { + /** + * Array of exchanges we find acceptable for this + * order. + */ + json_t *exchanges; + + /** + * Forced requests to /keys to update our exchange + * information. + */ + struct RekeyExchange *pending_reload_head; + + /** + * Forced requests to /keys to update our exchange + * information. + */ + struct RekeyExchange *pending_reload_tail; + + /** + * Did we previously force reloading of /keys from + * all exchanges? Set to 'true' to prevent us from + * doing it again (and again...). + */ + bool forced_reload; + + /** + * Set to true once we are sure that we have at + * least one good exchange. + */ + 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. + */ + struct TALER_Amount max_stefan_fee; + } set_exchanges; /** - * Array of inventory products in the @e order. + * Information set in the ORDER_PHASE_SET_MAX_FEE phase. */ - struct InventoryProduct *inventory_products; + struct { + /** + * Maximum fee + */ + struct TALER_Amount max_fee; + } set_max_fee; /** - * array of UUIDs used to reserve products from @a inventory_products. + * Information set in the ORDER_PHASE_EXECUTE_ORDER phase. */ - struct GNUNET_Uuid *uuids; + struct { + /** + * Which product (by offset) is out of stock, UINT_MAX if all were in-stock. + */ + unsigned int out_of_stock_index; + } execute_order; + + struct { + /** + * Contract terms to store in the database. + */ + json_t *contract; + } serialize_order; /** - * Shared key to use with @e pos_algorithm. + * Connection of the request. */ - const char *pos_key; + struct MHD_Connection *connection; /** - * Our order ID. Pointer into @e order. + * Kept in a DLL while suspended. */ - const char *order_id; + struct OrderContext *next; /** - * which product (by offset) is out of stock, UINT_MAX if all were in-stock + * Kept in a DLL while suspended. */ - unsigned int out_of_stock_index; + struct OrderContext *prev; /** - * Length of the @e uuids array. + * Handler context for the request. */ - unsigned int uuids_length; + struct TMH_HandlerContext *hc; /** * #GNUNET_YES if suspended. */ enum GNUNET_GenericReturnValue suspended; - /** - * Selected algorithm (by template) when we are to - * generate an OTP code for payment confirmation. - */ - enum TALER_MerchantConfirmationAlgorithm pos_algorithm; - /** * Current phase of setting up the order. */ @@ -284,6 +447,7 @@ struct OrderContext ORDER_PHASE_PATCH_ORDER, ORDER_PHASE_SET_EXCHANGES, ORDER_PHASE_SET_MAX_FEE, + ORDER_PHASE_SERIALIZE_ORDER, ORDER_PHASE_CHECK_CONTRACT, ORDER_PHASE_EXECUTE_ORDER, @@ -298,18 +462,6 @@ struct OrderContext ORDER_PHASE_FINISHED_MHD_NO } phase; - /** - * Set to true once we are sure that we have at - * least one good exchange. - */ - bool exchange_good; - - /** - * Did we previously force reloading of /keys from - * all exchanges? Set to 'true' to prevent us from - * doing it again (and again...). - */ - bool forced_reload; }; @@ -413,27 +565,29 @@ clean_order (void *cls) struct OrderContext *oc = cls; struct RekeyExchange *rx; - while (NULL != (rx = oc->pending_reload_head)) + while (NULL != (rx = oc->set_exchanges.pending_reload_head)) { - GNUNET_CONTAINER_DLL_remove (oc->pending_reload_head, - oc->pending_reload_tail, + GNUNET_CONTAINER_DLL_remove (oc->set_exchanges.pending_reload_head, + oc->set_exchanges.pending_reload_tail, rx); TMH_EXCHANGES_keys4exchange_cancel (rx->fo); GNUNET_free (rx->url); GNUNET_free (rx); } - if (NULL != oc->exchanges) + if (NULL != oc->set_exchanges.exchanges) { - json_decref (oc->exchanges); - oc->exchanges = NULL; + json_decref (oc->set_exchanges.exchanges); + oc->set_exchanges.exchanges = NULL; } - GNUNET_array_grow (oc->inventory_products, - oc->inventory_products_length, + GNUNET_array_grow (oc->init.inventory_products, + oc->init.inventory_products_length, 0); - GNUNET_array_grow (oc->uuids, - oc->uuids_length, + GNUNET_array_grow (oc->init.uuids, + oc->init.uuids_length, 0); - json_decref (oc->order); + json_decref (oc->init.order); + /* TODO: Check that all other fields are cleaned up! */ + json_decref (oc->serialize_order.contract); GNUNET_free (oc); } @@ -461,14 +615,14 @@ execute_transaction (struct OrderContext *oc) /* Setup order */ qs = TMH_db->insert_order (TMH_db->cls, oc->hc->instance->settings.id, - oc->order_id, + oc->patch_order.order_id, NULL /* session ID! FIXME: protocol v6! */, - &oc->h_post_data, - oc->pay_deadline, - &oc->claim_token, - oc->order, /* called 'contract terms' at database. */ - oc->pos_key, - oc->pos_algorithm); + &oc->init.h_post_data, + oc->patch_order.pay_deadline, + &oc->init.claim_token, + oc->serialize_order.contract, /* called 'contract terms' at database. */ + oc->init.pos_key, + oc->init.pos_algorithm); if (qs <= 0) { /* qs == 0: probably instance does not exist (anymore) */ @@ -476,10 +630,10 @@ execute_transaction (struct OrderContext *oc) return qs; } /* Migrate locks from UUIDs to new order: first release old locks */ - for (unsigned int i = 0; iuuids_length; i++) + for (unsigned int i = 0; iinit.uuids_length; i++) { qs = TMH_db->unlock_inventory (TMH_db->cls, - &oc->uuids[i]); + &oc->init.uuids[i]); if (qs < 0) { TMH_db->rollback (TMH_db->cls); @@ -492,14 +646,14 @@ execute_transaction (struct OrderContext *oc) (note: this can basically ONLY fail on serializability OR because the UUID locks were insufficient for the desired quantities). */ - for (unsigned int i = 0; iinventory_products_length; i++) + for (unsigned int i = 0; iinit.inventory_products_length; i++) { qs = TMH_db->insert_order_lock ( TMH_db->cls, oc->hc->instance->settings.id, - oc->order_id, - oc->inventory_products[i].product_id, - oc->inventory_products[i].quantity); + oc->patch_order.order_id, + oc->init.inventory_products[i].product_id, + oc->init.inventory_products[i].quantity); if (qs < 0) { TMH_db->rollback (TMH_db->cls); @@ -509,17 +663,17 @@ execute_transaction (struct OrderContext *oc) { /* qs == 0: lock acquisition failed due to insufficient stocks */ TMH_db->rollback (TMH_db->cls); - oc->out_of_stock_index = i; /* indicate which product is causing the issue */ + oc->execute_order.out_of_stock_index = i; /* indicate which product is causing the issue */ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } } - oc->out_of_stock_index = UINT_MAX; + oc->execute_order.out_of_stock_index = UINT_MAX; /* Get the order serial and timestamp for the order we just created to update long-poll clients. */ qs = TMH_db->lookup_order_summary (TMH_db->cls, oc->hc->instance->settings.id, - oc->order_id, + oc->patch_order.order_id, ×tamp, &order_serial); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) @@ -552,63 +706,64 @@ execute_order (struct OrderContext *oc) { const struct TALER_MERCHANTDB_InstanceSettings *settings = &oc->hc->instance->settings; - const char *summary; - const char *fulfillment_msg = NULL; - const json_t *products; - const json_t *merchant; - struct GNUNET_TIME_Timestamp timestamp; - struct GNUNET_TIME_Timestamp refund_deadline = { 0 }; - struct GNUNET_TIME_Timestamp wire_transfer_deadline; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("order_id", - &oc->order_id), - TALER_JSON_spec_i18n_str ("summary", - &summary), + // const char *summary; + // const char *fulfillment_msg = NULL; + // const json_t *products; + // This was never actually used, not even checked if it is well-formed. + // const json_t *merchant; + // struct GNUNET_TIME_Timestamp timestamp; + // struct GNUNET_TIME_Timestamp refund_deadline = { 0 }; + // struct GNUNET_TIME_Timestamp wire_transfer_deadline; + // struct GNUNET_JSON_Specification spec[] = { + // GNUNET_JSON_spec_string ("order_id", + // &oc->patch_order.order_id), + // TALER_JSON_spec_i18n_str ("summary", + // &summary), /** * The following entries we don't actually need, * except to check that the order is well-formed */ - GNUNET_JSON_spec_array_const ("products", - &products), - GNUNET_JSON_spec_object_const ("merchant", - &merchant), - GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_i18n_str ("fulfillment_message", - &fulfillment_msg), - NULL), - GNUNET_JSON_spec_timestamp ("timestamp", - ×tamp), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_timestamp ("refund_deadline", - &refund_deadline), - NULL), - GNUNET_JSON_spec_timestamp ("pay_deadline", - &oc->pay_deadline), - GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", - &wire_transfer_deadline), - GNUNET_JSON_spec_end () - }; + // GNUNET_JSON_spec_array_const ("products", + // &products), + // GNUNET_JSON_spec_object_const ("merchant", + // &merchant), + // GNUNET_JSON_spec_mark_optional ( + // TALER_JSON_spec_i18n_str ("fulfillment_message", + // &fulfillment_msg), + // NULL), + // GNUNET_JSON_spec_timestamp ("timestamp", + // ×tamp), + // GNUNET_JSON_spec_mark_optional ( + // GNUNET_JSON_spec_timestamp ("refund_deadline", + // &refund_deadline), + // NULL), + // GNUNET_JSON_spec_timestamp ("pay_deadline", + // &oc->patch_order.pay_deadline), + // GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", + // &wire_transfer_deadline), + // GNUNET_JSON_spec_end () + // }; enum GNUNET_DB_QueryStatus qs; /* extract fields we need to sign separately */ - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (oc->connection, - oc->order, - spec); - if (GNUNET_OK != res) - { - GNUNET_break_op (0); - finalize_order2 (oc, - res); - return; - } - } + // { + // enum GNUNET_GenericReturnValue res; + + // res = TALER_MHD_parse_json_data (oc->connection, + // oc->order, + // spec); + // if (GNUNET_OK != res) + // { + // GNUNET_break_op (0); + // finalize_order2 (oc, + // res); + // return; + // } + // } /* check product list in contract is well-formed */ - if (! TMH_products_array_valid (products)) + if (! TMH_products_array_valid (oc->merge_inventory.products)) { - GNUNET_JSON_parse_free (spec); + // GNUNET_JSON_parse_free (spec); GNUNET_break_op (0); reply_with_error (oc, MHD_HTTP_BAD_REQUEST, @@ -626,7 +781,7 @@ execute_order (struct OrderContext *oc) TMH_db->preflight (TMH_db->cls); qs = TMH_db->lookup_order (TMH_db->cls, oc->hc->instance->settings.id, - oc->order_id, + oc->patch_order.order_id, &token, &orig_post, &contract_terms); @@ -635,7 +790,7 @@ execute_order (struct OrderContext *oc) { GNUNET_break (0); TMH_db->rollback (TMH_db->cls); - GNUNET_JSON_parse_free (spec); + // GNUNET_JSON_parse_free (spec); reply_with_error (oc, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, @@ -650,7 +805,7 @@ execute_order (struct OrderContext *oc) /* Comparing the contract terms is sufficient because all the other params get added to it at some point. */ if (0 == GNUNET_memcmp (&orig_post, - &oc->h_post_data)) + &oc->init.h_post_data)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Order creation idempotent\n"); @@ -658,7 +813,7 @@ execute_order (struct OrderContext *oc) oc->connection, MHD_HTTP_OK, GNUNET_JSON_pack_string ("order_id", - oc->order_id), + oc->patch_order.order_id), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_data_varsize ( "token", @@ -666,7 +821,7 @@ execute_order (struct OrderContext *oc) ? NULL : &token, sizeof (token)))); - GNUNET_JSON_parse_free (spec); + // GNUNET_JSON_parse_free (spec); finalize_order (oc, ret); return; @@ -677,14 +832,14 @@ execute_order (struct OrderContext *oc) oc, MHD_HTTP_CONFLICT, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS, - oc->order_id); - GNUNET_JSON_parse_free (spec); + oc->patch_order.order_id); + // GNUNET_JSON_parse_free (spec); return; } } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing database transaction to create order '%s' for instance '%s'\n", - oc->order_id, + oc->patch_order.order_id, settings->id); for (unsigned int i = 0; i= qs) { - GNUNET_JSON_parse_free (spec); + // GNUNET_JSON_parse_free (spec); /* Special report if retries insufficient */ if (GNUNET_DB_STATUS_SOFT_ERROR == qs) { @@ -714,7 +869,7 @@ execute_order (struct OrderContext *oc) oc, MHD_HTTP_CONFLICT, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS, - oc->order_id); + oc->patch_order.order_id); return; } /* Other hard transaction error (disk full, etc.) */ @@ -728,7 +883,7 @@ execute_order (struct OrderContext *oc) } /* DB transaction succeeded, check for out-of-stock */ - if (oc->out_of_stock_index < UINT_MAX) + if (oc->execute_order.out_of_stock_index < UINT_MAX) { /* We had a product that has insufficient quantities, generate the details for the response. */ @@ -741,9 +896,9 @@ execute_order (struct OrderContext *oc) qs = TMH_db->lookup_product ( TMH_db->cls, oc->hc->instance->settings.id, - oc->inventory_products[oc->out_of_stock_index].product_id, + oc->init.inventory_products[oc->execute_order.out_of_stock_index].product_id, &pd); - GNUNET_JSON_parse_free (spec); + // GNUNET_JSON_parse_free (spec); switch (qs) { case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: @@ -754,10 +909,10 @@ execute_order (struct OrderContext *oc) MHD_HTTP_GONE, GNUNET_JSON_pack_string ( "product_id", - oc->inventory_products[oc->out_of_stock_index].product_id), + oc->init.inventory_products[oc->execute_order.out_of_stock_index].product_id), GNUNET_JSON_pack_uint64 ( "requested_quantity", - oc->inventory_products[oc->out_of_stock_index].quantity), + oc->init.inventory_products[oc->execute_order.out_of_stock_index].quantity), GNUNET_JSON_pack_uint64 ( "available_quantity", pd.total_stock - pd.total_sold - pd.total_lost), @@ -778,11 +933,11 @@ execute_order (struct OrderContext *oc) MHD_HTTP_GONE, GNUNET_JSON_pack_string ( "product_id", - oc->inventory_products[oc->out_of_stock_index]. + oc->init.inventory_products[oc->execute_order.out_of_stock_index]. product_id), GNUNET_JSON_pack_uint64 ( "requested_quantity", - oc->inventory_products[oc->out_of_stock_index]. + oc->init.inventory_products[oc->execute_order.out_of_stock_index]. quantity), GNUNET_JSON_pack_uint64 ( "available_quantity", @@ -820,15 +975,15 @@ execute_order (struct OrderContext *oc) oc->connection, MHD_HTTP_OK, GNUNET_JSON_pack_string ("order_id", - oc->order_id), + oc->patch_order.order_id), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_data_varsize ( "token", - GNUNET_is_zero (&oc->claim_token) + GNUNET_is_zero (&oc->init.claim_token) ? NULL - : &oc->claim_token, - sizeof (oc->claim_token)))); - GNUNET_JSON_parse_free (spec); + : &oc->init.claim_token, + sizeof (oc->init.claim_token)))); + // GNUNET_JSON_parse_free (spec); finalize_order (oc, ret); } @@ -845,7 +1000,7 @@ check_contract (struct OrderContext *oc) { struct TALER_PrivateContractHashP h_control; - switch (TALER_JSON_contract_hash (oc->order, + switch (TALER_JSON_contract_hash (oc->serialize_order.contract, &h_control)) { case GNUNET_SYSERR: @@ -886,33 +1041,33 @@ update_stefan (struct OrderContext *oc, if (GNUNET_SYSERR != TALER_EXCHANGE_keys_stefan_b2n (keys, - &oc->brutto, + &oc->patch_order.brutto, &net)) { struct TALER_Amount fee; TALER_EXCHANGE_keys_stefan_round (keys, &net); - if (-1 == TALER_amount_cmp (&oc->brutto, + if (-1 == TALER_amount_cmp (&oc->patch_order.brutto, &net)) { /* brutto < netto! */ /* => after rounding, there is no real difference */ - net = oc->brutto; + net = oc->patch_order.brutto; } GNUNET_assert (0 <= TALER_amount_subtract (&fee, - &oc->brutto, + &oc->patch_order.brutto, &net)); if ( (GNUNET_OK != - TALER_amount_is_valid (&oc->max_stefan_fee)) || - (-1 == TALER_amount_cmp (&oc->max_stefan_fee, + TALER_amount_is_valid (&oc->set_exchanges.max_stefan_fee)) || + (-1 == TALER_amount_cmp (&oc->set_exchanges.max_stefan_fee, &fee)) ) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Updated STEFAN-based fee to %s\n", TALER_amount2s (&fee)); - oc->max_stefan_fee = fee; + oc->set_exchanges.max_stefan_fee = fee; } } } @@ -937,7 +1092,7 @@ get_acceptable (void *cls, enum GNUNET_GenericReturnValue res; res = TMH_exchange_check_debit (exchange, - oc->wm); + oc->add_payment_details.wm); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Exchange %s evaluated at %d\n", url, @@ -946,16 +1101,16 @@ get_acceptable (void *cls, { case GNUNET_OK: priority = 1024; /* high */ - oc->exchange_good = true; + oc->set_exchanges.exchange_good = true; break; case GNUNET_NO: - if (oc->forced_reload) + if (oc->set_exchanges.forced_reload) priority = 0; /* fresh negative response */ else priority = 512; /* stale negative response */ break; case GNUNET_SYSERR: - if (oc->forced_reload) + if (oc->set_exchanges.forced_reload) priority = 256; /* fresh, no accounts yet */ else priority = 768; /* stale, no accounts yet */ @@ -970,7 +1125,7 @@ get_acceptable (void *cls, TMH_EXCHANGES_get_master_pub (exchange))); GNUNET_assert (NULL != j_exchange); GNUNET_assert (0 == - json_array_append_new (oc->exchanges, + json_array_append_new (oc->set_exchanges.exchanges, j_exchange)); } @@ -986,7 +1141,7 @@ resume_with_keys (struct OrderContext *oc) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Resuming order processing after /keys downloads (now have %u accounts)\n", - (unsigned int) json_array_size (oc->exchanges)); + (unsigned int) json_array_size (oc->set_exchanges.exchanges)); GNUNET_assert (GNUNET_YES == oc->suspended); GNUNET_CONTAINER_DLL_remove (oc_head, oc_tail, @@ -1017,8 +1172,8 @@ keys_cb ( &oc->hc->instance->settings; rx->fo = NULL; - GNUNET_CONTAINER_DLL_remove (oc->pending_reload_head, - oc->pending_reload_tail, + GNUNET_CONTAINER_DLL_remove (oc->set_exchanges.pending_reload_head, + oc->set_exchanges.pending_reload_tail, rx); if (NULL == keys) { @@ -1033,7 +1188,7 @@ keys_cb ( rx->url); if ( (settings->use_stefan) && (GNUNET_OK != - TALER_amount_is_valid (&oc->max_fee)) ) + TALER_amount_is_valid (&oc->patch_order.max_fee)) ) update_stefan (oc, keys); get_acceptable (oc, @@ -1042,7 +1197,7 @@ keys_cb ( } GNUNET_free (rx->url); GNUNET_free (rx); - if (NULL != oc->pending_reload_head) + if (NULL != oc->set_exchanges.pending_reload_head) return; resume_with_keys (oc); } @@ -1068,20 +1223,90 @@ get_exchange_keys (void *cls, rx = GNUNET_new (struct RekeyExchange); rx->oc = oc; rx->url = GNUNET_strdup (url); - GNUNET_CONTAINER_DLL_insert (oc->pending_reload_head, - oc->pending_reload_tail, + GNUNET_CONTAINER_DLL_insert (oc->set_exchanges.pending_reload_head, + oc->set_exchanges.pending_reload_tail, rx); - if (oc->forced_reload) + if (oc->set_exchanges.forced_reload) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Forcing download of %skeys\n", url); rx->fo = TMH_EXCHANGES_keys4exchange (url, - oc->forced_reload, + oc->set_exchanges.forced_reload, &keys_cb, rx); } +/** + * Serialize @a oc->init.order into @a oc->execute_order.contract_terms. + * + * @param[in,out] oc order context + */ +static void +serialize_order (struct OrderContext *oc) +{ + /* TODO: add public_reorder_url + fulfillment_message + fulfillment_message_i18n + nonce + extra (?) + */ + + oc->serialize_order.contract = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("summary", + oc->patch_order.summary), + GNUNET_JSON_pack_object_steal ("summary_i18n", + oc->patch_order.summary_i18n), + GNUNET_JSON_pack_string ("public_reorder_url", + oc->patch_order.public_reorder_url), + GNUNET_JSON_pack_string ("fulfillment_message", + oc->patch_order.fulfillment_message), + GNUNET_JSON_pack_object_steal ("fulfillment_message_i18n", + oc->patch_order.fulfillment_message_i18n), + GNUNET_JSON_pack_object_steal ("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->patch_order.order_id), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("fulfillment_url", + oc->patch_order.fulfillment_url)), + GNUNET_JSON_pack_timestamp ("timestamp", + oc->patch_order.timestamp), + GNUNET_JSON_pack_timestamp ("refund_deadline", + oc->patch_order.refund_deadline), + GNUNET_JSON_pack_timestamp ("pay_deadline", + oc->patch_order.pay_deadline), + GNUNET_JSON_pack_timestamp ("wire_transfer_deadline", + oc->patch_order.wire_deadline), + GNUNET_JSON_pack_time_rel ("auto_refund", + oc->patch_order.auto_refund), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_timestamp ("delivery_date", + oc->patch_order.delivery_date)), + GNUNET_JSON_pack_string ("merchant_base_url", + oc->patch_order.merchant_base_url), + GNUNET_JSON_pack_object_steal ("merchant", + oc->patch_order.merchant), + GNUNET_JSON_pack_data_auto ("merchant_pub", + &oc->hc->instance->merchant_pub), + GNUNET_JSON_pack_array_steal ("exchanges", + oc->set_exchanges.exchanges), + TALER_JSON_pack_amount ("max_fee", + &oc->set_max_fee.max_fee), + TALER_JSON_pack_amount ("amount", + &oc->patch_order.brutto), + GNUNET_JSON_pack_string ("nonce", + oc->patch_order.nonce), + GNUNET_JSON_pack_object_steal ("extra", + oc->patch_order.extra) + ); +} + + /** * Set max_fee in @a oc based on STEFAN value if * not yet present. @@ -1095,23 +1320,19 @@ set_max_fee (struct OrderContext *oc) &oc->hc->instance->settings; if (GNUNET_OK != - TALER_amount_is_valid (&oc->max_fee)) + TALER_amount_is_valid (&oc->patch_order.max_fee)) { struct TALER_Amount stefan; if ( (settings->use_stefan) && (GNUNET_OK == - TALER_amount_is_valid (&oc->max_stefan_fee)) ) - stefan = oc->max_stefan_fee; + 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->brutto.currency, + TALER_amount_set_zero (oc->patch_order.brutto.currency, &stefan)); - GNUNET_assert (0 == - json_object_set_new ( - oc->order, - "max_fee", - TALER_JSON_from_amount (&stefan))); + oc->set_max_fee.max_fee = stefan; } oc->phase++; } @@ -1129,28 +1350,28 @@ set_exchanges (struct OrderContext *oc) /* Note: re-building 'oc->exchanges' every time here might be a tad expensive; could likely consider caching the result if it starts to matter. */ - if (NULL == oc->exchanges) + if (NULL == oc->set_exchanges.exchanges) { - oc->exchanges = json_array (); + oc->set_exchanges.exchanges = json_array (); TMH_exchange_get_trusted (&get_exchange_keys, oc); } - else if (! oc->exchange_good) + else if (! oc->set_exchanges.exchange_good) { - if (! oc->forced_reload) + if (! oc->set_exchanges.forced_reload) { - oc->forced_reload = true; + oc->set_exchanges.forced_reload = true; GNUNET_assert (0 == - json_array_clear (oc->exchanges)); + json_array_clear (oc->set_exchanges.exchanges)); TMH_exchange_get_trusted (&get_exchange_keys, oc); } } - if (NULL != oc->pending_reload_head) + if (NULL != oc->set_exchanges.pending_reload_head) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Still trying to (re)load %skeys\n", - oc->pending_reload_head->url); + oc->set_exchanges.pending_reload_head->url); MHD_suspend_connection (oc->connection); oc->suspended = GNUNET_YES; GNUNET_CONTAINER_DLL_insert (oc_head, @@ -1158,7 +1379,7 @@ set_exchanges (struct OrderContext *oc) oc); return true; /* reloads pending */ } - if (0 == json_array_size (oc->exchanges)) + if (0 == json_array_size (oc->set_exchanges.exchanges)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Cannot create order: lacking trusted exchanges\n"); @@ -1166,19 +1387,14 @@ set_exchanges (struct OrderContext *oc) oc, MHD_HTTP_CONFLICT, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGES_FOR_WIRE_METHOD, - oc->wm->wire_method); + oc->add_payment_details.wm->wire_method); return false; } - if (! oc->exchange_good) + if (! oc->set_exchanges.exchange_good) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Creating order, but possibly without usable trusted exchanges\n"); } - /* 'set' is correct here: reference in oc->exchanges released in cleanup_order() */ - GNUNET_assert (0 == - json_object_set (oc->order, - "exchanges", - oc->exchanges)); oc->phase++; return false; } @@ -1195,28 +1411,45 @@ patch_order (struct OrderContext *oc) { const struct TALER_MERCHANTDB_InstanceSettings *settings = &oc->hc->instance->settings; - const char *order_id = NULL; - const char *fulfillment_url = NULL; - const char *merchant_base_url = NULL; + + /* Setting default values of patch_order, maybe not needed (?) */ + oc->patch_order.order_id = NULL; + oc->patch_order.fulfillment_url = NULL; + oc->patch_order.merchant_base_url = NULL; + oc->patch_order.timestamp = GNUNET_TIME_UNIT_ZERO_TS; + oc->patch_order.refund_deadline = GNUNET_TIME_UNIT_FOREVER_TS; + oc->patch_order.pay_deadline = GNUNET_TIME_UNIT_ZERO_TS; + oc->patch_order.wire_deadline = GNUNET_TIME_UNIT_FOREVER_TS; + oc->patch_order.delivery_location = NULL; + + const json_t *jmerchant = NULL; - const json_t *delivery_location = NULL; - struct GNUNET_TIME_Timestamp timestamp - = GNUNET_TIME_UNIT_ZERO_TS; - struct GNUNET_TIME_Timestamp delivery_date - = GNUNET_TIME_UNIT_ZERO_TS; - struct GNUNET_TIME_Timestamp refund_deadline - = GNUNET_TIME_UNIT_FOREVER_TS; - struct GNUNET_TIME_Timestamp wire_deadline - = GNUNET_TIME_UNIT_FOREVER_TS; /* auto_refund only needs to be type-checked, * mostly because in GNUnet relative times can't * be negative. */ - struct GNUNET_TIME_Relative auto_refund; bool no_fee; struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string("summary", + &oc->patch_order.summary), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_json ("summary_i18n", + &oc->patch_order.summary_i18n), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("public_reorder_url", + &oc->patch_order.public_reorder_url), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string("fulfillment_message", + &oc->patch_order.fulfillment_message), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_json ("fulfillment_message_i18n", + &oc->patch_order.fulfillment_message_i18n), + NULL), GNUNET_JSON_spec_mark_optional ( TALER_JSON_spec_web_url ("merchant_base_url", - &merchant_base_url), + &oc->patch_order.merchant_base_url), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_object_const ("merchant", @@ -1224,52 +1457,60 @@ patch_order (struct OrderContext *oc) NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("order_id", - &order_id), + &oc->patch_order.order_id), NULL), TALER_JSON_spec_amount_any ("amount", - &oc->brutto), + &oc->patch_order.brutto), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("fulfillment_url", - &fulfillment_url), + &oc->patch_order.fulfillment_url), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("timestamp", - ×tamp), + &oc->patch_order.timestamp), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("refund_deadline", - &refund_deadline), + &oc->patch_order.refund_deadline), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("pay_deadline", - &oc->pay_deadline), + &oc->patch_order.pay_deadline), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", - &wire_deadline), + &oc->patch_order.wire_deadline), NULL), GNUNET_JSON_spec_mark_optional ( TALER_JSON_spec_amount_any ("max_fee", - &oc->max_fee), + &oc->patch_order.max_fee), &no_fee), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_object_const ("delivery_location", + &oc->patch_order.delivery_location), + NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("delivery_date", - &delivery_date), + &oc->patch_order.delivery_date), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("nonce", + &oc->patch_order.nonce), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_relative_time ("auto_refund", - &auto_refund), + &oc->patch_order.auto_refund), NULL), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("delivery_location", - &delivery_location), + GNUNET_JSON_spec_json ("extra", + &oc->patch_order.extra), NULL), GNUNET_JSON_spec_end () }; enum GNUNET_GenericReturnValue ret; ret = TALER_MHD_parse_json_data (oc->connection, - oc->order, + oc->init.order, spec); if (GNUNET_OK != ret) { @@ -1278,7 +1519,7 @@ patch_order (struct OrderContext *oc) ret); return; } - if (! TMH_test_exchange_configured_for_currency (oc->brutto.currency)) + if (! TMH_test_exchange_configured_for_currency (oc->patch_order.brutto.currency)) { GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); @@ -1290,8 +1531,8 @@ patch_order (struct OrderContext *oc) } if ( (! no_fee) && (GNUNET_OK != - TALER_amount_cmp_currency (&oc->brutto, - &oc->max_fee)) ) + TALER_amount_cmp_currency (&oc->patch_order.brutto, + &oc->patch_order.max_fee)) ) { GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); @@ -1303,7 +1544,7 @@ patch_order (struct OrderContext *oc) } /* Add order_id if it doesn't exist. */ - if (NULL == order_id) + if (NULL == oc->patch_order.order_id) { char buf[256]; time_t timer; @@ -1311,7 +1552,6 @@ patch_order (struct OrderContext *oc) size_t off; uint64_t rand; char *last; - json_t *jbuf; time (&timer); tm_info = localtime (&timer); @@ -1340,25 +1580,21 @@ patch_order (struct OrderContext *oc) sizeof (buf) - off); GNUNET_assert (NULL != last); *last = '\0'; - jbuf = json_string (buf); - GNUNET_assert (NULL != jbuf); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Assigning order ID `%s' server-side\n", buf); - GNUNET_break (0 == - json_object_set_new (oc->order, - "order_id", - jbuf)); - order_id = json_string_value (jbuf); - GNUNET_assert (NULL != order_id); + + oc->patch_order.order_id = GNUNET_strdup (buf); + GNUNET_assert (NULL != oc->patch_order.order_id); } /* Patch fulfillment URL with order_id (implements #6467). */ - if (NULL != fulfillment_url) + if (NULL != oc->patch_order.fulfillment_url) { const char *pos; - pos = strstr (fulfillment_url, + pos = strstr (oc->patch_order.fulfillment_url, "${ORDER_ID}"); if (NULL != pos) { @@ -1380,17 +1616,15 @@ patch_order (struct OrderContext *oc) GNUNET_asprintf (&nurl, "%.*s%s%s", /* first output URL until ${ORDER_ID} */ - (int) (pos - fulfillment_url), - fulfillment_url, + (int) (pos - oc->patch_order.fulfillment_url), + oc->patch_order.fulfillment_url, /* replace ${ORDER_ID} with the right order_id */ - order_id, + oc->patch_order.order_id, /* append rest of original URL */ pos + strlen ("${ORDER_ID}")); - /* replace in JSON of the order */ - GNUNET_break (0 == - json_object_set_new (oc->order, - "fulfillment_url", - json_string (nurl))); + + oc->patch_order.fulfillment_url = GNUNET_strdup (nurl); + GNUNET_free (nurl); } } @@ -1401,35 +1635,28 @@ patch_order (struct OrderContext *oc) struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); /* Add timestamp if it doesn't exist (or is zero) */ - if (GNUNET_TIME_absolute_is_zero (timestamp.abs_time)) + if (GNUNET_TIME_absolute_is_zero (oc->patch_order.timestamp.abs_time)) { - GNUNET_assert (0 == - json_object_set_new (oc->order, - "timestamp", - GNUNET_JSON_from_timestamp (now))); + oc->patch_order.timestamp = now; } /* If no refund_deadline given, set one based on refund_delay. */ - if (GNUNET_TIME_absolute_is_never (refund_deadline.abs_time)) + if (GNUNET_TIME_absolute_is_never (oc->patch_order.refund_deadline.abs_time)) { - if (GNUNET_TIME_relative_is_zero (oc->refund_delay)) + if (GNUNET_TIME_relative_is_zero (oc->init.refund_delay)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Refund delay is zero, no refunds are possible for this order\n"); - refund_deadline = GNUNET_TIME_UNIT_ZERO_TS; + oc->patch_order.refund_deadline = GNUNET_TIME_UNIT_ZERO_TS; } else { - refund_deadline = GNUNET_TIME_relative_to_timestamp (oc->refund_delay); + oc->patch_order.refund_deadline = GNUNET_TIME_relative_to_timestamp (oc->init.refund_delay); } - GNUNET_assert (0 == - json_object_set_new (oc->order, - "refund_deadline", - GNUNET_JSON_from_timestamp ( - refund_deadline))); } - if ( (! GNUNET_TIME_absolute_is_zero (delivery_date.abs_time)) && - (GNUNET_TIME_absolute_is_past (delivery_date.abs_time)) ) + + if ( (! GNUNET_TIME_absolute_is_zero (oc->patch_order.delivery_date.abs_time)) && + (GNUNET_TIME_absolute_is_past (oc->patch_order.delivery_date.abs_time)) ) { GNUNET_break_op (0); reply_with_error ( @@ -1441,17 +1668,12 @@ patch_order (struct OrderContext *oc) } } - if (GNUNET_TIME_absolute_is_zero (oc->pay_deadline.abs_time)) + if (GNUNET_TIME_absolute_is_zero (oc->patch_order.pay_deadline.abs_time)) { - oc->pay_deadline = GNUNET_TIME_relative_to_timestamp ( + oc->patch_order.pay_deadline = GNUNET_TIME_relative_to_timestamp ( settings->default_pay_delay); - GNUNET_assert (0 == - json_object_set_new (oc->order, - "pay_deadline", - GNUNET_JSON_from_timestamp ( - oc->pay_deadline))); } - else if (GNUNET_TIME_absolute_is_past (oc->pay_deadline.abs_time)) + else if (GNUNET_TIME_absolute_is_past (oc->patch_order.pay_deadline.abs_time)) { GNUNET_break_op (0); reply_with_error ( @@ -1461,8 +1683,9 @@ patch_order (struct OrderContext *oc) NULL); return; } - if ( (! GNUNET_TIME_absolute_is_zero (refund_deadline.abs_time)) && - (GNUNET_TIME_absolute_is_past (refund_deadline.abs_time)) ) + + if ( (! GNUNET_TIME_absolute_is_zero (oc->patch_order.refund_deadline.abs_time)) && + (GNUNET_TIME_absolute_is_past (oc->patch_order.refund_deadline.abs_time)) ) { GNUNET_break_op (0); reply_with_error ( @@ -1473,16 +1696,16 @@ patch_order (struct OrderContext *oc) return; } - if (GNUNET_TIME_absolute_is_never (wire_deadline.abs_time)) + if (GNUNET_TIME_absolute_is_never (oc->patch_order.wire_deadline.abs_time)) { struct GNUNET_TIME_Timestamp t; t = GNUNET_TIME_relative_to_timestamp ( GNUNET_TIME_relative_max (settings->default_wire_transfer_delay, - oc->refund_delay)); - wire_deadline = GNUNET_TIME_timestamp_max (refund_deadline, + oc->init.refund_delay)); + oc->patch_order.wire_deadline = GNUNET_TIME_timestamp_max (oc->patch_order.refund_deadline, t); - if (GNUNET_TIME_absolute_is_never (wire_deadline.abs_time)) + if (GNUNET_TIME_absolute_is_never (oc->patch_order.wire_deadline.abs_time)) { GNUNET_break_op (0); reply_with_error ( @@ -1492,15 +1715,10 @@ patch_order (struct OrderContext *oc) "order:wire_transfer_deadline"); return; } - GNUNET_assert (0 == - json_object_set_new (oc->order, - "wire_transfer_deadline", - GNUNET_JSON_from_timestamp ( - wire_deadline))); } - if (GNUNET_TIME_timestamp_cmp (wire_deadline, + if (GNUNET_TIME_timestamp_cmp (oc->patch_order.wire_deadline, <, - refund_deadline)) + oc->patch_order.refund_deadline)) { GNUNET_break_op (0); reply_with_error ( @@ -1511,7 +1729,7 @@ patch_order (struct OrderContext *oc) return; } - if (NULL == merchant_base_url) + if (NULL == oc->patch_order.merchant_base_url) { char *url; @@ -1527,14 +1745,11 @@ patch_order (struct OrderContext *oc) "order:merchant_base_url"); return; } - GNUNET_assert (0 == - json_object_set_new (oc->order, - "merchant_base_url", - json_string (url))); + oc->patch_order.merchant_base_url = GNUNET_strdup (url); GNUNET_free (url); } - else if (('\0' == *merchant_base_url) || - ('/' != merchant_base_url[strlen (merchant_base_url) - 1])) + else if (('\0' == *oc->patch_order.merchant_base_url) || + ('/' != oc->patch_order.merchant_base_url[strlen (oc->patch_order.merchant_base_url) - 1])) { GNUNET_break_op (0); reply_with_error ( @@ -1558,9 +1773,7 @@ patch_order (struct OrderContext *oc) } { - json_t *jm; - - jm = GNUNET_JSON_PACK ( + oc->patch_order.merchant = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("name", settings->name), GNUNET_JSON_pack_allow_null ( @@ -1572,7 +1785,7 @@ patch_order (struct OrderContext *oc) GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("logo", settings->logo))); - GNUNET_assert (NULL != jm); + GNUNET_assert (NULL != oc->patch_order.merchant); { json_t *loca; @@ -1582,7 +1795,7 @@ patch_order (struct OrderContext *oc) { loca = json_deep_copy (loca); GNUNET_assert (0 == - json_object_set_new (jm, + json_object_set_new (oc->patch_order.merchant, "address", loca)); } @@ -1596,36 +1809,29 @@ patch_order (struct OrderContext *oc) { locj = json_deep_copy (locj); GNUNET_assert (0 == - json_object_set_new (jm, + json_object_set_new (oc->patch_order.merchant, "jurisdiction", locj)); } } - GNUNET_assert (0 == - json_object_set_new (oc->order, - "merchant", - jm)); } - GNUNET_assert (0 == - json_object_set_new (oc->order, - "merchant_pub", - GNUNET_JSON_from_data_auto ( - &oc->hc->instance->merchant_pub))); - - if (GNUNET_OK != - TALER_JSON_contract_seed_forgettable (oc->order)) - { - reply_with_error ( - oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_JSON_INVALID, - "could not compute hash of order due to bogus forgettable fields"); - return; - } - - if ( (NULL != delivery_location) && - (! TMH_location_object_valid (delivery_location)) ) + oc->patch_order.merchant_pub = oc->hc->instance->merchant_pub; + + /* TODO: Not sure yet how to properly handle this in the refactored code */ + // if (GNUNET_OK != + // TALER_JSON_contract_seed_forgettable (oc->order)) + // { + // reply_with_error ( + // oc, + // MHD_HTTP_BAD_REQUEST, + // TALER_EC_GENERIC_JSON_INVALID, + // "could not compute hash of order due to bogus forgettable fields"); + // return; + // } + + if ( (NULL != oc->patch_order.delivery_location) && + (! TMH_location_object_valid (oc->patch_order.delivery_location)) ) { GNUNET_break_op (0); reply_with_error (oc, @@ -1634,6 +1840,7 @@ patch_order (struct OrderContext *oc) "delivery_location"); return; } + oc->phase++; } @@ -1654,8 +1861,8 @@ add_payment_details (struct OrderContext *oc) /* Locate wire method that has a matching payment target */ while ( (NULL != wm) && ( (! wm->active) || - ( (NULL != oc->payment_target) && - (0 != strcasecmp (oc->payment_target, + ( (NULL != oc->init.payment_target) && + (0 != strcasecmp (oc->init.payment_target, wm->wire_method) ) ) ) ) wm = wm->next; if (NULL == wm) @@ -1666,19 +1873,10 @@ add_payment_details (struct OrderContext *oc) reply_with_error (oc, MHD_HTTP_NOT_FOUND, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_INSTANCE_CONFIGURATION_LACKS_WIRE, - oc->payment_target); + oc->init.payment_target); return; } - oc->wm = wm; - GNUNET_assert (0 == - json_object_set_new (oc->order, - "h_wire", - GNUNET_JSON_from_data_auto ( - &wm->h_wire))); - GNUNET_assert (0 == - json_object_set_new (oc->order, - "wire_method", - json_string (wm->wire_method))); + oc->add_payment_details.wm = wm; oc->phase++; } @@ -1698,16 +1896,13 @@ merge_inventory (struct OrderContext *oc) * order.products => contains products that are not from the backend-managed inventory. */ { - json_t *jprod = json_object_get (oc->order, + oc->merge_inventory.products = json_object_get (oc->init.order, "products"); - if (NULL == jprod) + if (NULL == oc->merge_inventory.products) { - GNUNET_assert (0 == - json_object_set_new (oc->order, - "products", - json_array ())); + oc->merge_inventory.products = json_array (); } - else if (! TMH_products_array_valid (jprod)) + else if (! TMH_products_array_valid (oc->merge_inventory.products)) { reply_with_error (oc, MHD_HTTP_BAD_REQUEST, @@ -1719,17 +1914,15 @@ merge_inventory (struct OrderContext *oc) /* Populate products from inventory product array and database */ { - json_t *np = json_array (); - - GNUNET_assert (NULL != np); - for (unsigned int i = 0; iinventory_products_length; i++) + GNUNET_assert (NULL != oc->merge_inventory.products); + for (unsigned int i = 0; iinit.inventory_products_length; i++) { struct TALER_MERCHANTDB_ProductDetails pd; enum GNUNET_DB_QueryStatus qs; qs = TMH_db->lookup_product (TMH_db->cls, oc->hc->instance->settings.id, - oc->inventory_products[i].product_id, + oc->init.inventory_products[i].product_id, &pd); if (qs <= 0) { @@ -1751,7 +1944,7 @@ merge_inventory (struct OrderContext *oc) case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Product %s from order unknown\n", - oc->inventory_products[i].product_id); + oc->init.inventory_products[i].product_id); http_status = MHD_HTTP_NOT_FOUND; ec = TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN; break; @@ -1759,11 +1952,11 @@ merge_inventory (struct OrderContext *oc) /* case listed to make compilers happy */ GNUNET_assert (0); } - json_decref (np); + json_decref (oc->merge_inventory.products); reply_with_error (oc, http_status, ec, - oc->inventory_products[i].product_id); + oc->init.inventory_products[i].product_id); return; } { @@ -1784,10 +1977,10 @@ merge_inventory (struct OrderContext *oc) pd.image), GNUNET_JSON_pack_uint64 ( "quantity", - oc->inventory_products[i].quantity)); + oc->init.inventory_products[i].quantity)); GNUNET_assert (NULL != p); GNUNET_assert (0 == - json_array_append_new (np, + json_array_append_new (oc->merge_inventory.products, p)); } GNUNET_free (pd.description); @@ -1795,16 +1988,6 @@ merge_inventory (struct OrderContext *oc) GNUNET_free (pd.image); json_decref (pd.address); } - /* merge into existing products list */ - { - json_t *xp; - - xp = json_object_get (oc->order, - "products"); - GNUNET_assert (NULL != xp); - json_array_extend (xp, np); - json_decref (np); - } } oc->phase++; } @@ -1824,14 +2007,14 @@ parse_order_request (struct OrderContext *oc) bool create_token = true; /* default */ struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_json ("order", - &oc->order), + &oc->init.order), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_relative_time ("refund_delay", - &oc->refund_delay), + &oc->init.refund_delay), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("payment_target", - &oc->payment_target), + &oc->init.payment_target), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_array_const ("inventory_products", @@ -1865,7 +2048,7 @@ parse_order_request (struct OrderContext *oc) } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Refund delay is %s\n", - GNUNET_TIME_relative2s (oc->refund_delay, + GNUNET_TIME_relative2s (oc->init.refund_delay, false)); TMH_db->expire_locks (TMH_db->cls); if (NULL != otp_id) @@ -1902,14 +2085,14 @@ parse_order_request (struct OrderContext *oc) case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; } - oc->pos_key = td.otp_key; - oc->pos_algorithm = td.otp_algorithm; + oc->init.pos_key = td.otp_key; + oc->init.pos_algorithm = td.otp_algorithm; } if (create_token) { GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &oc->claim_token, - sizeof (oc->claim_token)); + &oc->init.claim_token, + sizeof (oc->init.claim_token)); } /* Compute h_post_data (for idempotency check) */ { @@ -1932,19 +2115,19 @@ parse_order_request (struct OrderContext *oc) } GNUNET_CRYPTO_hash (req_body_enc, strlen (req_body_enc), - &oc->h_post_data.hash); + &oc->init.h_post_data.hash); GNUNET_free (req_body_enc); } /* parse the inventory_products (optionally given) */ if (NULL != ip) { - GNUNET_array_grow (oc->inventory_products, - oc->inventory_products_length, + GNUNET_array_grow (oc->init.inventory_products, + oc->init.inventory_products_length, json_array_size (ip)); - for (unsigned int i = 0; iinventory_products_length; i++) + for (unsigned int i = 0; iinit.inventory_products_length; i++) { - struct InventoryProduct *ipr = &oc->inventory_products[i]; + struct InventoryProduct *ipr = &oc->init.inventory_products[i]; const char *error_name; unsigned int error_line; struct GNUNET_JSON_Specification ispec[] = { @@ -1980,10 +2163,10 @@ parse_order_request (struct OrderContext *oc) /* parse the lock_uuids (optionally given) */ if (NULL != uuid) { - GNUNET_array_grow (oc->uuids, - oc->uuids_length, + GNUNET_array_grow (oc->init.uuids, + oc->init.uuids_length, json_array_size (uuid)); - for (unsigned int i = 0; iuuids_length; i++) + for (unsigned int i = 0; iinit.uuids_length; i++) { json_t *ui = json_array_get (uuid, i); @@ -2001,7 +2184,7 @@ parse_order_request (struct OrderContext *oc) return; } TMH_uuid_from_string (json_string_value (ui), - &oc->uuids[i]); + &oc->init.uuids[i]); } } oc->phase++; @@ -2050,6 +2233,9 @@ TMH_private_post_orders ( case ORDER_PHASE_SET_MAX_FEE: set_max_fee (oc); break; + case ORDER_PHASE_SERIALIZE_ORDER: + serialize_order (oc); + break; case ORDER_PHASE_CHECK_CONTRACT: check_contract (oc); break; -- cgit v1.2.3