merchant

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

commit 22350d314c0437888ad76fa4cb7b8639807c59b3
parent bb1e888f8b6a520dd66ac1f5b6eaf4417cad161b
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon, 25 May 2026 18:45:41 +0200

fix remaining FTBFS issues

Diffstat:
Msrc/backend/taler-merchant-httpd_post-private-orders.c | 449+++++++++++++++++++++++++++++++------------------------------------------------
Msrc/include/taler/taler_merchant_util.h | 2+-
Msrc/util/contract_choice_serialize.c | 5++---
Msrc/util/meson.build | 1+
Asrc/util/order_choice_serialize.c | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 334 insertions(+), 276 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_post-private-orders.c b/src/backend/taler-merchant-httpd_post-private-orders.c @@ -344,7 +344,7 @@ struct OrderContext * from by selecting the respective index when signing the deposit * confirmation. */ - struct TALER_MERCHANT_OrderChoice *choices; + struct TALER_MERCHANT_ContractChoice *choices; /** * Length of the @e choices array. @@ -829,7 +829,7 @@ clean_order (void *cls) } for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) { - TALER_MERCHANT_order_choice_free (&oc->parse_choices.choices[i]); + TALER_MERCHANT_contract_choice_free (&oc->parse_choices.choices[i]); } GNUNET_array_grow (oc->parse_choices.choices, oc->parse_choices.choices_len, @@ -2074,7 +2074,7 @@ output_contract_choices (struct OrderContext *oc) oc->set_max_fee.details.v1.max_fees[i]; GNUNET_assert (0 == json_array_append_new ( choices, - TALER_MERCHANT_json_from_order_choice ( + TALER_MERCHANT_json_from_contract_choice ( &oc->parse_choices.choices[i]))); } return choices; @@ -2177,19 +2177,21 @@ phase_serialize_order (struct OrderContext *oc) { json_t *xtra; - switch (oc->parse_order.version) + switch (oc->parse_order.order->base->version) { case TALER_MERCHANT_CONTRACT_VERSION_0: xtra = GNUNET_JSON_PACK ( TALER_JSON_pack_amount ("max_fee", &oc->set_max_fee.details.v0.max_fee), GNUNET_JSON_pack_allow_null ( - TALER_JSON_pack_amount ("tip", - oc->parse_order.details.v0.no_tip + TALER_JSON_pack_amount ( + "tip", + oc->parse_order.order->details.v0.no_tip ? NULL - : &oc->parse_order.details.v0.tip)), - TALER_JSON_pack_amount ("amount", - &oc->parse_order.details.v0.brutto)); + : &oc->parse_order.order->details.v0.tip)), + TALER_JSON_pack_amount ( + "amount", + &oc->parse_order.order->details.v0.brutto)); break; case TALER_MERCHANT_CONTRACT_VERSION_1: { @@ -2221,19 +2223,22 @@ phase_serialize_order (struct OrderContext *oc) /* 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))); + json_object_set_new ( + oc->serialize_order.contract, + "refund_deadline", + GNUNET_JSON_from_timestamp ( + oc->parse_order.order->refund_deadline))); /* auto_refund should only be set if it is not 0 */ - if (! GNUNET_TIME_relative_is_zero (oc->parse_order.auto_refund)) + if (! GNUNET_TIME_relative_is_zero ( + oc->parse_order.order->base->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", - GNUNET_JSON_from_time_rel ( - oc->parse_order.auto_refund))); + json_object_set_new ( + oc->serialize_order.contract, + "auto_refund", + GNUNET_JSON_from_time_rel ( + oc->parse_order.order->base->auto_refund))); } oc->phase++; @@ -2294,12 +2299,12 @@ compute_fee (struct OrderContext *oc, static void phase_set_max_fee (struct OrderContext *oc) { - switch (oc->parse_order.version) + switch (oc->parse_order.order->base->version) { case TALER_MERCHANT_CONTRACT_VERSION_0: compute_fee (oc, - &oc->parse_order.details.v0.brutto, - &oc->parse_order.details.v0.max_fee, + &oc->parse_order.order->details.v0.brutto, + &oc->parse_order.order->details.v0.max_fee, &oc->set_exchanges.details.v0.max_stefan_fee, &oc->set_max_fee.details.v0.max_fee); break; @@ -2347,11 +2352,11 @@ phase_select_wire_method (struct OrderContext *oc) { unsigned int num_choices = 0; - switch (oc->parse_order.version) + switch (oc->parse_order.order->base->version) { case TALER_MERCHANT_CONTRACT_VERSION_0: want_choices = 1; - ea = &oc->parse_order.details.v0.brutto; + ea = &oc->parse_order.order->details.v0.brutto; if (TALER_amount_is_zero (ea) || TALER_amount_set_test_above (&wmc->total_exchange_limits, ea)) @@ -2536,11 +2541,11 @@ static void update_stefan (struct OrderContext *oc, const struct TALER_EXCHANGE_Keys *keys) { - switch (oc->parse_order.version) + switch (oc->parse_order.order->base->version) { case TALER_MERCHANT_CONTRACT_VERSION_0: compute_stefan_fee (keys, - &oc->parse_order.details.v0.brutto, + &oc->parse_order.order->details.v0.brutto, &oc->set_exchanges.details.v0.max_stefan_fee); break; case TALER_MERCHANT_CONTRACT_VERSION_1: @@ -3041,14 +3046,14 @@ static void phase_add_payment_details (struct OrderContext *oc) { /* First, determine the maximum amounts that could be paid per currency */ - switch (oc->parse_order.version) + switch (oc->parse_order.order->base->version) { case TALER_MERCHANT_CONTRACT_VERSION_0: GNUNET_array_append (oc->add_payment_details.max_choice_limits, oc->add_payment_details.num_max_choice_limits, - oc->parse_order.details.v0.brutto); + oc->parse_order.order->details.v0.brutto); if (! TALER_amount_is_zero ( - &oc->parse_order.details.v0.brutto)) + &oc->parse_order.order->details.v0.brutto)) { oc->add_payment_details.need_exchange = true; } @@ -3167,25 +3172,26 @@ uint64_cmp (const void *a, static void phase_merge_inventory (struct OrderContext *oc) { - uint64_t pots[oc->parse_order.products_len + 1]; + uint64_t pots[oc->parse_order.order->products_len + 1]; size_t pots_off = 0; - if (0 != oc->parse_order.order_default_money_pot) - pots[pots_off++] = oc->parse_order.order_default_money_pot; + if (0 != oc->parse_order.order->base->default_money_pot) + pots[pots_off++] = oc->parse_order.order->base->default_money_pot; /** * parse_request.inventory_products => instructions to add products to contract terms * parse_order.products => contains products that are not from the backend-managed inventory. */ oc->merge_inventory.products = json_array (); - for (size_t i = 0; i<oc->parse_order.products_len; i++) + for (size_t i = 0; i<oc->parse_order.order->products_len; i++) { GNUNET_assert ( 0 == json_array_append_new ( oc->merge_inventory.products, - TALER_MERCHANT_product_sold_serialize (&oc->parse_order.products[i]))); - if (0 != oc->parse_order.products[i].product_money_pot) - pots[pots_off++] = oc->parse_order.products[i].product_money_pot; + TALER_MERCHANT_product_sold_serialize ( + &oc->parse_order.order->products[i]))); + if (0 != oc->parse_order.order->products[i].product_money_pot) + pots[pots_off++] = oc->parse_order.order->products[i].product_money_pot; } /* make sure pots array only has distinct elements */ @@ -3309,8 +3315,8 @@ phase_merge_inventory (struct OrderContext *oc) return; } GNUNET_free (categories); - oc->parse_order.minimum_age - = GNUNET_MAX (oc->parse_order.minimum_age, + oc->parse_order.order->base->minimum_age + = GNUNET_MAX (oc->parse_order.order->base->minimum_age, pd.minimum_age); { const char *eparam; @@ -3361,7 +3367,7 @@ phase_merge_inventory (struct OrderContext *oc) .prices_are_net = pd.price_is_net, .image = pd.image, .taxes = pd.taxes, - .delivery_date = oc->parse_order.delivery_date, + .delivery_date = oc->parse_order.order->base->delivery_date, .product_money_pot = pd.money_pot_id, .unit = pd.unit, @@ -3450,7 +3456,7 @@ add_donau_url (void *cls, */ static bool add_donau_output (struct OrderContext *oc, - struct TALER_MERCHANT_OrderOutput *output) + struct TALER_MERCHANT_ContractOutput *output) { enum GNUNET_DB_QueryStatus qs; @@ -3488,9 +3494,7 @@ add_donau_output (struct OrderContext *oc, static void phase_parse_choices (struct OrderContext *oc) { - const json_t *jchoices; - - switch (oc->parse_order.version) + switch (oc->parse_order.order->base->version) { case TALER_MERCHANT_CONTRACT_VERSION_0: oc->phase++; @@ -3502,253 +3506,155 @@ phase_parse_choices (struct OrderContext *oc) GNUNET_assert (0); } - jchoices = oc->parse_order.details.v1.choices; - - if (! json_is_array (jchoices)) - GNUNET_assert (0); - if (0 == json_array_size (jchoices)) - { - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "choices"); - return; - } + /* Convert order choices to contract choices */ GNUNET_array_grow (oc->parse_choices.choices, oc->parse_choices.choices_len, - json_array_size (jchoices)); + oc->parse_order.order->details.v1.choices_len); for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) { - struct TALER_MERCHANT_OrderChoice *choice + const struct TALER_MERCHANT_OrderChoice *ochoice + = &oc->parse_order.order->details.v1.choices[i]; + struct TALER_MERCHANT_ContractChoice *cchoice = &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 ("tip", - &choice->tip), - &choice->no_tip), - 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_string_copy ("description", - &choice->description), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_copy ("description_i18n", - &choice->description_i18n), - NULL), - 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 ( (! choice->no_tip) && - (GNUNET_OK != - TALER_amount_cmp_currency (&choice->amount, - &choice->tip)) ) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_CURRENCY_MISMATCH, - "tip and amount"); - return; - } + unsigned int off; if (! TMH_test_exchange_configured_for_currency ( - choice->amount.currency)) + ochoice->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); + ochoice->amount.currency); return; } - - if (NULL != jinputs) + cchoice->amount = ochoice->amount; + cchoice->tip = ochoice->tip; + cchoice->no_tip = ochoice->no_tip; + cchoice->description = GNUNET_strdup (ochoice->description); + cchoice->description_i18n = json_incref (ochoice->description_i18n); + cchoice->max_fee = ochoice->max_fee; + + /* convert inputs */ + GNUNET_array_grow (cchoice->inputs, + cchoice->inputs_len, + ochoice->inputs_len); + off = 0; + for (unsigned int j = 0; j < ochoice->inputs_len; j++) { - const json_t *jinput; - size_t idx; - json_array_foreach ((json_t *) jinputs, idx, jinput) - { - struct TALER_MERCHANT_OrderInput input = { - .details.token.count = 1 - }; + const struct TALER_MERCHANT_OrderInput *order_input + = &ochoice->inputs[j]; + struct TALER_MERCHANT_ContractInput *contract_input + = &cchoice->inputs[off]; + contract_input->type = order_input->type; + switch (order_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 == order_input->details.token.count) + continue; + contract_input->details.token.count + = order_input->details.token.count; + contract_input->details.token.token_family_slug + = order_input->details.token.token_family_slug; if (GNUNET_OK != - TALER_MERCHANT_parse_order_choice_input ( - (json_t *) jinput, - &input, - idx)) + add_input_token_family (oc, + contract_input->details.token.token_family_slug)) { GNUNET_break_op (0); reply_with_error (oc, MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "input"); + TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_TOKEN_FAMILY_SLUG_UNKNOWN, + contract_input->details.token.token_family_slug); return; } + off++; + continue; + } /* switch input type */ + GNUNET_assert (0); + } /* for all inputs */ + GNUNET_array_grow (cchoice->inputs, + cchoice->inputs_len, + off); + + /* convert outputs */ + GNUNET_array_grow (cchoice->outputs, + cchoice->outputs_len, + ochoice->outputs_len); + off = 0; + for (unsigned int j = 0; j < ochoice->outputs_len; j++) + { + const struct TALER_MERCHANT_OrderOutput *order_output + = &ochoice->outputs[j]; + struct TALER_MERCHANT_ContractOutput *contract_output + = &cchoice->outputs[off]; - switch (input.type) + switch (order_output->type) + { + case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: + GNUNET_assert (0); + break; + case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: + if (! order_output->details.donation_receipt.no_amount) { - 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; + contract_output->details.donation_receipt.amount + = ochoice->amount; } - GNUNET_assert (0); - } - } - - if (NULL != joutputs) - { - const json_t *joutput; - size_t idx; - json_array_foreach ((json_t *) joutputs, idx, joutput) - { - struct TALER_MERCHANT_OrderOutput output = { - .details.token.count = 1 - }; - - if (GNUNET_OK != - TALER_MERCHANT_parse_order_choice_output ( - (json_t *) joutput, - &output, - idx)) + else { - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "output"); + contract_output->details.donation_receipt.amount + = order_output->details.donation_receipt.amount; + } + if (! add_donau_output (oc, + contract_output)) + { + GNUNET_break (0); return; } + off++; + continue; + case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: + /* Ignore inputs tokens with 'count' field set to 0 */ + if (0 == order_output->details.token.count) + continue; + + contract_output->details.token.token_family_slug + = order_output->details.token.token_family_slug; + contract_output->details.token.count + = order_output->details.token.count; + if (0 == order_output->details.token.valid_at.abs_time.abs_value_us) + contract_output->details.token.valid_at + = GNUNET_TIME_timestamp_get (); + else + contract_output->details.token.valid_at + = order_output->details.token.valid_at; + if (GNUNET_OK != + add_output_token_family ( + oc, + contract_output->details.token.token_family_slug, + contract_output->details.token.valid_at, + &contract_output->details.token.key_index)) - switch (output.type) { - case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: - GNUNET_assert (0); - break; - case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: - output.details.donation_receipt.amount = choice->amount; - // FIXME: This is also already a transformation, - // not reflected in the "Order" type! - if (! add_donau_output (oc, - &output)) - { - GNUNET_break (0); - return; - } - 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 (); - // FIXME: This is another transformation, - // not reflected in the "Order" type! - // => need yet another type for the data that is - // actually POSTed to this endpoint! - // (Note: the 'metadata' is another one that - // is currently *wrong*!) - if (GNUNET_OK != - add_output_token_family (oc, - output.details.token.token_family_slug, - output.details.token.valid_at, - &output.details.token.key_index)) - - { - /* note: reply_with_error() was already called */ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Could not handle output token family `%s'\n", - output.details.token.token_family_slug); - return; - } - - GNUNET_array_append (choice->outputs, - choice->outputs_len, - output); - continue; + /* note: reply_with_error() was already called */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Could not handle output token family `%s'\n", + contract_output->details.token.token_family_slug); + return; } - GNUNET_assert (0); - } - } - } + off++; + continue; + } /* end switch */ + GNUNET_assert (0); + } /* for outputs */ + GNUNET_array_grow (cchoice->outputs, + cchoice->outputs_len, + off); + } /* for all choices */ oc->phase++; } @@ -3767,7 +3673,6 @@ phase_parse_order (struct OrderContext *oc) { const struct TALER_MERCHANTDB_InstanceSettings *settings = &oc->hc->instance->settings; - enum GNUNET_GenericReturnValue ret; bool computed_refund_deadline = false; oc->parse_order.order @@ -3790,7 +3695,6 @@ phase_parse_order (struct OrderContext *oc) oc->parse_order.order->details.v0.brutto.currency)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); reply_with_error ( oc, MHD_HTTP_CONFLICT, @@ -3803,7 +3707,6 @@ phase_parse_order (struct OrderContext *oc) break; default: GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); reply_with_error (oc, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_VERSION_MALFORMED, @@ -3825,7 +3728,6 @@ phase_parse_order (struct OrderContext *oc) tm_info = localtime (&timer); if (NULL == tm_info) { - GNUNET_JSON_parse_free (spec); reply_with_error ( oc, MHD_HTTP_INTERNAL_SERVER_ERROR, @@ -3860,7 +3762,7 @@ phase_parse_order (struct OrderContext *oc) { const char *pos; - pos = strstr (oc->parse_order.odrer->base->fulfillment_url, + pos = strstr (oc->parse_order.order->base->fulfillment_url, "${ORDER_ID}"); if (NULL != pos) { @@ -3899,8 +3801,9 @@ phase_parse_order (struct OrderContext *oc) (GNUNET_TIME_absolute_is_never ( oc->parse_order.order->pay_deadline.abs_time)) ) { - oc->parse_order.pay_deadline = GNUNET_TIME_relative_to_timestamp ( - settings->default_pay_delay); + oc->parse_order.order->pay_deadline + = GNUNET_TIME_relative_to_timestamp ( + settings->default_pay_delay); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Pay deadline was zero (or never), setting to %s\n", GNUNET_TIME_timestamp2s ( @@ -3986,14 +3889,14 @@ phase_parse_order (struct OrderContext *oc) } if (GNUNET_TIME_absolute_is_never ( - oc->parse_order.order->wire_deadline.abs_time)) + oc->parse_order.order->wire_transfer_deadline.abs_time)) { struct GNUNET_TIME_Absolute start; start = GNUNET_TIME_absolute_max ( oc->parse_order.order->refund_deadline.abs_time, oc->parse_order.order->pay_deadline.abs_time); - oc->parse_order.order->wire_deadline + oc->parse_order.order->wire_transfer_deadline = GNUNET_TIME_absolute_to_timestamp ( GNUNET_TIME_round_up ( GNUNET_TIME_absolute_add ( @@ -4001,7 +3904,7 @@ phase_parse_order (struct OrderContext *oc) settings->default_wire_transfer_delay), settings->default_wire_transfer_rounding_interval)); if (GNUNET_TIME_absolute_is_never ( - oc->parse_order.order->wire_deadline.abs_time)) + oc->parse_order.order->wire_transfer_deadline.abs_time)) { GNUNET_break_op (0); reply_with_error ( @@ -4018,12 +3921,14 @@ phase_parse_order (struct OrderContext *oc) and did have a configured wire_deadline, make sure that the refund_deadline is at or below the wire_deadline. */ oc->parse_order.order->refund_deadline - = GNUNET_TIME_timestamp_min (oc->parse_order.order->refund_deadline, - oc->parse_order.order->wire_deadline); + = GNUNET_TIME_timestamp_min ( + oc->parse_order.order->refund_deadline, + oc->parse_order.order->wire_transfer_deadline); } - if (GNUNET_TIME_timestamp_cmp (oc->parse_order.order->wire_deadline, - <, - oc->parse_order.order->refund_deadline)) + if (GNUNET_TIME_timestamp_cmp ( + oc->parse_order.order->wire_transfer_deadline, + <, + oc->parse_order.order->refund_deadline)) { GNUNET_break_op (0); reply_with_error ( diff --git a/src/include/taler/taler_merchant_util.h b/src/include/taler/taler_merchant_util.h @@ -472,7 +472,7 @@ struct TALER_MERCHANT_ContractOutput struct { /** - * Amount of the donation. (optional) + * Amount of the donation. */ struct TALER_Amount amount; diff --git a/src/util/contract_choice_serialize.c b/src/util/contract_choice_serialize.c @@ -109,9 +109,8 @@ json_from_contract_output ( "tax-receipt"), GNUNET_JSON_pack_array_steal ("donau_urls", donau_urls), - GNUNET_JSON_pack_allow_null ( - TALER_JSON_pack_amount ("amount", - &output->details.donation_receipt.amount))); + TALER_JSON_pack_amount ("amount", + &output->details.donation_receipt.amount)); } } GNUNET_log (GNUNET_ERROR_TYPE_ERROR, diff --git a/src/util/meson.build b/src/util/meson.build @@ -26,6 +26,7 @@ libtalermerchantutil_SOURCES = [ 'merchant_parse.c', 'mfa.c', 'order_choice_parse.c', + 'order_choice_serialize.c', 'order_parse.c', 'os_installation.c', 'product_parse.c', diff --git a/src/util/order_choice_serialize.c b/src/util/order_choice_serialize.c @@ -0,0 +1,153 @@ +/* + This file is part of GNU Taler + Copyright (C) 2024, 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file src/util/order_choice_serialize.c + * @brief shared logic for order choice serialization + * @author Iván Ávalos + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_json_lib.h> +#include <gnunet/gnunet_common.h> +#include <taler/taler_json_lib.h> +#include <jansson.h> +#include "taler/taler_util.h" +#include "taler/taler_merchant_util.h" + +/** + * Get JSON representation of order choice input. + * + * @param[in] input order terms choice input + * @return JSON representation of @a input; NULL on error + */ +static json_t * +json_from_order_input ( + const struct TALER_MERCHANT_OrderInput *input) +{ + switch (input->type) + { + case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "invalid order input type"); + GNUNET_assert (0); + return NULL; + case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN: + return GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("type", + "token"), + GNUNET_JSON_pack_string ("token_family_slug", + input->details.token.token_family_slug), + GNUNET_JSON_pack_int64 ("count", + input->details.token.count)); + } + + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "unsupported order input type %d", + input->type); + GNUNET_assert (0); + return NULL; +} + + +/** + * Get JSON representation of order choice output. + * + * @param[in] output order terms choice output + * @return JSON representation of @a output; NULL on error + */ +static json_t * +json_from_order_output ( + const struct TALER_MERCHANT_OrderOutput *output) +{ + switch (output->type) + { + case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "invalid order output type"); + GNUNET_assert (0); + return NULL; + case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: + return GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("type", + "token"), + GNUNET_JSON_pack_string ("token_family_slug", + output->details.token.token_family_slug), + GNUNET_JSON_pack_uint64 ("count", + output->details.token.count)); + case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: + return GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("type", + "tax-receipt"), + GNUNET_JSON_pack_allow_null ( + TALER_JSON_pack_amount ("amount", + output->details.donation_receipt.no_amount + ? NULL + : &output->details.donation_receipt.amount))); + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unsupported order output type %d", + output->type); + GNUNET_assert (0); + return NULL; +} + + +json_t * +TALER_MERCHANT_json_from_order_choice ( + const struct TALER_MERCHANT_OrderChoice *choice) +{ + json_t *inputs; + json_t *outputs; + + inputs = json_array (); + GNUNET_assert (NULL != inputs); + for (unsigned int i = 0; i < choice->inputs_len; i++) + GNUNET_assert (0 == + json_array_append_new (inputs, + json_from_order_input ( + &choice->inputs[i]))); + outputs = json_array (); + GNUNET_assert (NULL != outputs); + for (unsigned int i = 0; i < choice->outputs_len; i++) + GNUNET_assert (0 == + json_array_append_new (outputs, + json_from_order_output ( + &choice->outputs[i]))); + + return GNUNET_JSON_PACK ( + TALER_JSON_pack_amount ("amount", + &choice->amount), + GNUNET_JSON_pack_allow_null ( + TALER_JSON_pack_amount ("tip", + choice->no_tip + ? NULL + : &choice->tip)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("description", + choice->description)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_incref ("description_i18n", + choice->description_i18n)), + GNUNET_JSON_pack_allow_null ( + TALER_JSON_pack_amount ("max_fee", + choice->no_max_fee + ? NULL + : &choice->max_fee)), + GNUNET_JSON_pack_array_steal ("inputs", + inputs), + GNUNET_JSON_pack_array_steal ("outputs", + outputs)); +}