merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit f2b5027ba37abbbfe7ff0711aa9aff2c0e08ac6c
parent 9300bca7e76714d4562d691c93894d36809283a3
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date:   Wed, 23 Jul 2025 09:30:18 +0200

fixing merge issues

Diffstat:
Msrc/backend/taler-merchant-httpd_private-post-orders.c | 539+++++++++++--------------------------------------------------------------------
1 file changed, 71 insertions(+), 468 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -3221,6 +3221,59 @@ phase_merge_inventory (struct OrderContext *oc) } +#ifdef HAVE_DONAU_DONAU_SERVICE_H +/** + * Callback function that is called for each donau instance. + * It simply adds the provided donau_url to the json. + */ +/* FIXME: very-very bad */ +static void +add_donau_url (void *cls, + const char *donau_url, + const char *charity_name, + const struct DONAU_CharityPublicKeyP *charity_pub_key, + uint64_t charity_id, + const struct TALER_Amount *charity_max_per_year, + const struct TALER_Amount *charity_receipts_to_date, + int64_t current_year, + const json_t *donau_keys_json) +{ + struct TALER_MERCHANT_ContractOutput *output = cls; + + GNUNET_array_append (output->details.donation_receipt.donau_urls, + output->details.donation_receipt.donau_urls_len, + GNUNET_strdup (donau_url)); +} + + +static void +parse_donau_instances (struct OrderContext *oc, + struct TALER_MERCHANT_ContractOutput *output) +{ + enum GNUNET_DB_QueryStatus qs; + + /* Invoke the database call, accumulating URLs in a JSON array */ + /* FIXME: select_donau_instanceS */ + qs = TMH_db->select_donau_instance (TMH_db->cls, + &add_donau_url, + output); + + /* REVIEW: If no switch */ + if (qs < 0) + { + GNUNET_break_op (0); + reply_with_error (oc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR, + "donau url parsing db call"); + return; + } +} + + +#endif + + /* ***************** ORDER_PHASE_PARSE_CHOICES **************** */ /** @@ -3418,8 +3471,24 @@ phase_parse_choices (struct OrderContext *oc) GNUNET_assert (0); break; case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: - GNUNET_break (0); /* FIXME-#9059: not yet implemented! */ - break; + output.details.donation_receipt.amount = choice->amount; +#ifdef HAVE_DONAU_DONAU_SERVICE_H + /* FIXME: Change to fetch and move to new phase */ + /* filter by currency */ + parse_donau_instances (oc, + &output); +#endif + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Output details donation receipt donau urls %p\n", + output.details.donation_receipt.donau_urls); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Output details donation receipt donau urls len %u\n", + output.details.donation_receipt.donau_urls_len); + + GNUNET_array_append (choice->outputs, + choice->outputs_len, + output); + continue; case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: /* Ignore inputs tokens with 'count' field set to 0 */ if (0 == output.details.token.count) @@ -3457,7 +3526,6 @@ phase_parse_choices (struct OrderContext *oc) /* ***************** ORDER_PHASE_PARSE_ORDER **************** */ - /** * Parse the order field of the request. Upon success, continue * processing with parse_choices(). @@ -3979,471 +4047,6 @@ phase_parse_order (struct OrderContext *oc) } -#ifdef HAVE_DONAU_DONAU_SERVICE_H -/** - * Callback function that is called for each donau instance. - * It simply adds the provided donau_url to the json. - */ -/* FIXME: very-very bad */ -static void -add_donau_url (void *cls, - const char *donau_url, - const char *charity_name, - const struct DONAU_CharityPublicKeyP *charity_pub_key, - uint64_t charity_id, - const struct TALER_Amount *charity_max_per_year, - const struct TALER_Amount *charity_receipts_to_date, - int64_t current_year, - const json_t *donau_keys_json) -{ - struct TALER_MERCHANT_ContractOutput *output = cls; - - GNUNET_array_append (output->details.donation_receipt.donau_urls, - output->details.donation_receipt.donau_urls_len, - GNUNET_strdup (donau_url)); -} - - -static void -parse_donau_instances (struct OrderContext *oc, - struct TALER_MERCHANT_ContractOutput *output) -{ - enum GNUNET_DB_QueryStatus qs; - - /* Invoke the database call, accumulating URLs in a JSON array */ - /* FIXME: select_donau_instanceS */ - qs = TMH_db->select_donau_instance (TMH_db->cls, - &add_donau_url, - output); - - /* REVIEW: If no switch */ - if (qs < 0) - { - GNUNET_break_op (0); - reply_with_error (oc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR, - "donau url parsing db call"); - return; - } -} - - -#endif - - -/** - * Parse contract choices. Upon success, continue - * processing with merge_inventory(). - * - * @param[in,out] oc order context - */ -static void -parse_choices (struct OrderContext *oc) -{ - const json_t *jchoices; - - switch (oc->parse_order.version) - { - case TALER_MERCHANT_CONTRACT_VERSION_0: - oc->phase++; - return; - case TALER_MERCHANT_CONTRACT_VERSION_1: - /* handle below */ - break; - default: - GNUNET_assert (0); - } - - jchoices = oc->parse_order.details.v1.choices; - - if (! json_is_array (jchoices)) - GNUNET_assert (0); - - GNUNET_array_grow (oc->parse_choices.choices, - oc->parse_choices.choices_len, - json_array_size (jchoices)); - for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) - { - struct TALER_MERCHANT_ContractChoice *choice - = &oc->parse_choices.choices[i]; - const char *error_name; - 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), - &no_fee), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ("inputs", - &jinputs), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ("outputs", - &joutputs), - NULL), - GNUNET_JSON_spec_end () - }; - enum GNUNET_GenericReturnValue ret; - - ret = GNUNET_JSON_parse (json_array_get (jchoices, - i), - spec, - &error_name, - &error_line); - if (GNUNET_OK != ret) - { - GNUNET_break_op (0); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Choice parsing failed: %s:%u\n", - error_name, - error_line); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "choice"); - 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); - reply_with_error (oc, - MHD_HTTP_CONFLICT, - TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGE_FOR_CURRENCY, - choice->amount.currency); - return; - } - - if (NULL != jinputs) - { - const json_t *jinput; - size_t idx; - json_array_foreach ((json_t *) jinputs, idx, jinput) - { - struct TALER_MERCHANT_ContractInput input = { - .details.token.count = 1 - }; - - if (GNUNET_OK != - TALER_MERCHANT_parse_choice_input ((json_t *) jinput, - &input, - idx, - true)) - { - GNUNET_break_op (0); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "input"); - return; - } - - switch (input.type) - { - case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID: - GNUNET_assert (0); - break; - case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN: - /* Ignore inputs tokens with 'count' field set to 0 */ - if (0 == input.details.token.count) - continue; - - if (GNUNET_OK != - add_input_token_family (oc, - input.details.token.token_family_slug)) - - { - GNUNET_break_op (0); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_TOKEN_FAMILY_SLUG_UNKNOWN, - input.details.token.token_family_slug); - return; - } - - GNUNET_array_append (choice->inputs, - choice->inputs_len, - input); - continue; - } - GNUNET_assert (0); - } - } - - if (NULL != joutputs) - { - const json_t *joutput; - size_t idx; - json_array_foreach ((json_t *) joutputs, idx, joutput) - { - struct TALER_MERCHANT_ContractOutput output = { - .details.token.count = 1 - }; - - if (GNUNET_OK != - TALER_MERCHANT_parse_choice_output ((json_t *) joutput, - &output, - idx, - true)) - { - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "output"); - return; - } - - switch (output.type) - { - case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: - GNUNET_assert (0); - break; - case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: - /* FIXME-#9059: not yet implemented! */ - - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Output details donation receipt amount currency %s\n", - output.details.donation_receipt.amount.currency); - - /* If amount wasn't set, we need to get it from the overall amount */ - if (GNUNET_OK != - TALER_amount_is_valid (&output.details.donation_receipt.amount)) - { - output.details.donation_receipt.amount = choice->amount; - } - - /* If the system was complied with donau support, we can parse the donau instances */ -#ifdef HAVE_DONAU_DONAU_SERVICE_H - /* FIXME: Change to fetch and move to new phase */ - /* filter by currency */ - parse_donau_instances (oc, &output); -#endif - - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Output details donation receipt donau urls %p\n", - output.details.donation_receipt.donau_urls); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Output details donation receipt donau urls len %u\n", - output.details.donation_receipt.donau_urls_len); - - GNUNET_array_append (choice->outputs, - choice->outputs_len, - output); - continue; - case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: - /* Ignore inputs tokens with 'count' field set to 0 */ - if (0 == output.details.token.count) - continue; - - if (0 == output.details.token.valid_at.abs_time.abs_value_us) - output.details.token.valid_at - = GNUNET_TIME_timestamp_get (); - if (GNUNET_OK != - add_output_token_family (oc, - output.details.token.token_family_slug, - output.details.token.valid_at, - &output.details.token.key_index)) - - { - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_TOKEN_FAMILY_SLUG_UNKNOWN, - output.details.token.token_family_slug); - return; - } - - GNUNET_array_append (choice->outputs, - choice->outputs_len, - output); - continue; - } - GNUNET_assert (0); - } - } - } - oc->phase++; -} - - -/** - * Process the @a payment_target and add the details of how the - * order could be paid to @a order. On success, continue - * processing with set_exchanges(). - * - * @param[in,out] oc order context - */ -static void -add_payment_details (struct OrderContext *oc) -{ - struct TMH_WireMethod *wm; - - wm = oc->hc->instance->wm_head; - /* Locate wire method that has a matching payment target */ - while ( (NULL != wm) && - ( (! wm->active) || - ( (NULL != oc->parse_request.payment_target) && - (0 != strcasecmp (oc->parse_request.payment_target, - wm->wire_method) ) ) ) ) - wm = wm->next; - if (NULL == wm) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "No wire method available for instance '%s'\n", - oc->hc->instance->settings.id); - reply_with_error (oc, - MHD_HTTP_NOT_FOUND, - TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_INSTANCE_CONFIGURATION_LACKS_WIRE, - oc->parse_request.payment_target); - return; - } - oc->add_payment_details.wm = wm; - oc->phase++; -} - - -/** - * Merge the inventory products into products, querying the - * database about the details of those products. Upon success, - * continue processing by calling add_payment_details(). - * - * @param[in,out] oc order context to process - */ -static void -merge_inventory (struct OrderContext *oc) -{ - /** - * parse_request.inventory_products => instructions to add products to contract terms - * parse_order.products => contains products that are not from the backend-managed inventory. - */ - if (NULL != oc->parse_order.products) - oc->merge_inventory.products - = json_deep_copy (oc->parse_order.products); - else - oc->merge_inventory.products - = json_array (); - /* Populate products from inventory product array and database */ - { - GNUNET_assert (NULL != oc->merge_inventory.products); - for (unsigned int i = 0; i<oc->parse_request.inventory_products_length; i++) - { - const struct InventoryProduct *ip - = &oc->parse_request.inventory_products[i]; - struct TALER_MERCHANTDB_ProductDetails pd; - enum GNUNET_DB_QueryStatus qs; - size_t num_categories = 0; - uint64_t *categories = NULL; - - qs = TMH_db->lookup_product (TMH_db->cls, - oc->hc->instance->settings.id, - ip->product_id, - &pd, - &num_categories, - &categories); - if (qs <= 0) - { - enum TALER_ErrorCode ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; - unsigned int http_status = 0; - - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; - ec = TALER_EC_GENERIC_DB_FETCH_FAILED; - break; - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; - ec = TALER_EC_GENERIC_DB_SOFT_FAILURE; - break; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Product %s from order unknown\n", - ip->product_id); - http_status = MHD_HTTP_NOT_FOUND; - ec = TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN; - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - /* case listed to make compilers happy */ - GNUNET_assert (0); - } - reply_with_error (oc, - http_status, - ec, - ip->product_id); - return; - } - GNUNET_free (categories); - oc->parse_order.minimum_age - = GNUNET_MAX (oc->parse_order.minimum_age, - pd.minimum_age); - { - json_t *p; - - p = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("product_name", - pd.product_name), - GNUNET_JSON_pack_string ("description", - pd.description), - GNUNET_JSON_pack_object_steal ("description_i18n", - pd.description_i18n), - GNUNET_JSON_pack_string ("unit", - pd.unit), - TALER_JSON_pack_amount ("price", - &pd.price), - GNUNET_JSON_pack_array_steal ("taxes", - pd.taxes), - GNUNET_JSON_pack_string ("image", - pd.image), - GNUNET_JSON_pack_uint64 ( - "quantity", - ip->quantity)); - GNUNET_assert (NULL != p); - GNUNET_assert (0 == - json_array_append_new (oc->merge_inventory.products, - p)); - } - GNUNET_free (pd.description); - GNUNET_free (pd.unit); - GNUNET_free (pd.image); - json_decref (pd.address); - } - } - /* check if final product list is well-formed */ - if (! TMH_products_array_valid (oc->merge_inventory.products)) - { - GNUNET_break_op (0); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "order:products"); - return; - } - oc->phase++; -} - - /* ***************** ORDER_PHASE_PARSE_REQUEST **************** */ /**