merchant

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

commit bb1e888f8b6a520dd66ac1f5b6eaf4417cad161b
parent e1cb7a087c986d8ce3d23dc2767e82fa5c72b11c
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon, 25 May 2026 00:07:06 +0200

fix most of backend build

Diffstat:
Msrc/backend/taler-merchant-httpd_get-orders-ORDER_ID.c | 23++++++++++++-----------
Msrc/backend/taler-merchant-httpd_get-private-orders-ORDER_ID.c | 38++++++++++++++++++++++++--------------
Msrc/backend/taler-merchant-httpd_get-private-orders.c | 20++++++++++----------
Msrc/backend/taler-merchant-httpd_post-orders-ORDER_ID-pay.c | 91++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/backend/taler-merchant-httpd_post-private-orders.c | 711+++++++++++++++++--------------------------------------------------------------
Msrc/include/taler/taler_merchant_util.h | 15++++++++++-----
Msrc/util/base_terms_parse.c | 3---
Msrc/util/base_terms_serialize.c | 2--
Msrc/util/contract_parse.c | 3+++
Msrc/util/contract_serialize.c | 2++
Msrc/util/order_parse.c | 5+++++
11 files changed, 264 insertions(+), 649 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_get-orders-ORDER_ID.c b/src/backend/taler-merchant-httpd_get-orders-ORDER_ID.c @@ -166,7 +166,7 @@ struct GetOrderData /** * Common terms from @e order or @e contract */ - const struct TALER_MERCHANT_CommonTerms *ct; + const struct TALER_MERCHANT_ContractBaseTerms *ct; /** * Total refunds granted for this payment. Only initialized @@ -792,7 +792,7 @@ phase_parse_contract (struct GetOrderData *god) return; } god->contract_parsed = true; - god->ct = &god->contract_terms->common; + god->ct = god->contract_terms->pc->base; } if (NULL != god->order_json) { @@ -807,7 +807,7 @@ phase_parse_contract (struct GetOrderData *god) return; } god->order_parsed = true; - god->ct = &god->order->common; + god->ct = god->order->base; } GNUNET_assert ( (NULL != god->order) || (NULL != god->contract_terms) ); @@ -1375,8 +1375,8 @@ phase_check_refunded (struct GetOrderData *god) switch (god->ct->version) { case TALER_MERCHANT_CONTRACT_VERSION_0: - refund_amount = god->contract_terms->details.v0.brutto; - refund_currency = god->contract_terms->details.v0.brutto.currency; + refund_amount = god->contract_terms->pc->details.v0.brutto; + refund_currency = god->contract_terms->pc->details.v0.brutto.currency; break; case TALER_MERCHANT_CONTRACT_VERSION_1: if (god->choice_index < 0) @@ -1386,11 +1386,12 @@ phase_check_refunded (struct GetOrderData *god) return false; } GNUNET_assert (god->choice_index < - god->contract_terms->details.v1.choices_len); - refund_currency = god->contract_terms->details.v1.choices[god->choice_index] - .amount.currency; - GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (refund_currency, - &refund_amount)); + god->contract_terms->pc->details.v1.choices_len); + refund_currency = god->contract_terms->pc->details.v1.choices[ + god->choice_index].amount.currency; + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (refund_currency, + &refund_amount)); break; default: { @@ -1570,7 +1571,7 @@ phase_return_status (struct GetOrderData *god) GNUNET_assert (NULL != god->contract_terms_json); GNUNET_assert (NULL != god->contract_terms); - uri = make_taler_refund_uri (god->ct->merchant_base_url, + uri = make_taler_refund_uri (god->contract_terms->pc->merchant_base_url, god->order_id); if (NULL == uri) { diff --git a/src/backend/taler-merchant-httpd_get-private-orders-ORDER_ID.c b/src/backend/taler-merchant-httpd_get-private-orders-ORDER_ID.c @@ -276,7 +276,12 @@ struct GetOrderRequestContext /** * Common terms of @e order and @e contract. */ - const struct TALER_MERCHANT_CommonTerms *ct; + const struct TALER_MERCHANT_ContractBaseTerms *ct; + + /** + * Timestamp of the contract or order. + */ + struct GNUNET_TIME_Timestamp timestamp; /** * Claim token of the order. @@ -790,7 +795,8 @@ phase_parse_contract (struct GetOrderRequestContext *gorc) hc->infix)); return; } - gorc->ct = &gorc->order->common; + gorc->ct = gorc->order->base; + gorc->timestamp = gorc->order->timestamp; } if ( (NULL == gorc->contract_terms) && (NULL != gorc->contract_terms_json) ) @@ -809,7 +815,8 @@ phase_parse_contract (struct GetOrderRequestContext *gorc) hc->infix)); return; } - gorc->ct = &gorc->contract_terms->common; + gorc->ct = gorc->contract_terms->pc->base; + gorc->timestamp = gorc->contract_terms->pc->timestamp; } switch (gorc->ct->version) @@ -817,14 +824,16 @@ phase_parse_contract (struct GetOrderRequestContext *gorc) case TALER_MERCHANT_CONTRACT_VERSION_0: gorc->contract_amount = (NULL != gorc->contract_terms) - ? gorc->contract_terms->details.v0.brutto + ? gorc->contract_terms->pc->details.v0.brutto : gorc->order->details.v0.brutto; break; case TALER_MERCHANT_CONTRACT_VERSION_1: if (gorc->choice_index >= 0) { if (gorc->choice_index >= - gorc->contract_terms->details.v1.choices_len) + (NULL != gorc->contract_terms) + ? gorc->contract_terms->pc->details.v1.choices_len + : gorc->order->details.v1.choices_len) { GNUNET_break (0); phase_end (gorc, @@ -837,16 +846,15 @@ phase_parse_contract (struct GetOrderRequestContext *gorc) } gorc->contract_amount = (NULL != gorc->contract_terms) - ? gorc->contract_terms->details.v1.choices[gorc->choice_index].amount + ? gorc->contract_terms->pc->details.v1.choices[gorc->choice_index].amount : gorc->order->details.v1.choices[gorc->choice_index].amount; } else { GNUNET_break (gorc->order_only); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Choice index %i for order %s is invalid or not yet available", - gorc->choice_index, - gorc->ct->order_id); + "Choice index %i is invalid", + gorc->choice_index); } break; default: @@ -1130,9 +1138,11 @@ phase_check_repurchase (struct GetOrderRequestContext *gorc) GNUNET_JSON_pack_string ("summary", gorc->ct->summary), GNUNET_JSON_pack_timestamp ("pay_deadline", - gorc->ct->pay_deadline), + NULL != gorc->contract_terms + ? gorc->contract_terms->pc->pay_deadline + : gorc->order->pay_deadline), GNUNET_JSON_pack_timestamp ("creation_time", - gorc->ct->timestamp)); + gorc->timestamp)); GNUNET_free (order_status_url); GNUNET_free (taler_pay_uri); @@ -1226,7 +1236,7 @@ phase_unpaid_finish (struct GetOrderRequestContext *gorc) GNUNET_JSON_pack_string ("summary", gorc->ct->summary), GNUNET_JSON_pack_timestamp ("creation_time", - gorc->ct->timestamp)); + gorc->timestamp)); check_reply (gorc, reply); json_decref (reply); @@ -1640,7 +1650,7 @@ phase_check_local_transfers (struct GetOrderRequestContext *gorc) TMH_notify_order_change (hc->instance, TMH_OSF_PAID | TMH_OSF_WIRED, - gorc->ct->timestamp, + gorc->timestamp, gorc->order_serial); } } @@ -1686,7 +1696,7 @@ phase_reply_result (struct GetOrderRequestContext *gorc) { GNUNET_break (GNUNET_YES == TALER_amount_is_zero (&gorc->contract_amount)); - gorc->last_payment = gorc->ct->timestamp; + gorc->last_payment = gorc->timestamp; } { json_t *reply; diff --git a/src/backend/taler-merchant-httpd_get-private-orders.c b/src/backend/taler-merchant-httpd_get-private-orders.c @@ -479,7 +479,7 @@ add_order (void *cls, char amount_buf[128]; char refund_buf[128]; char pending_buf[128]; - const struct TALER_MERCHANT_CommonTerms *ct = NULL; + const struct TALER_MERCHANT_ContractBaseTerms *ct = NULL; /* Bail early if we already have an error */ if (TALER_EC_NONE != po->result) @@ -541,7 +541,7 @@ add_order (void *cls, po->result = TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID; goto cleanup; } - ct = &contract->common; + ct = contract->pc->base; } } if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) @@ -566,7 +566,7 @@ add_order (void *cls, po->result = TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID; goto cleanup; } - ct = &order->common; + ct = order->base; } } if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) @@ -592,14 +592,14 @@ add_order (void *cls, switch (ct->version) { case TALER_MERCHANT_CONTRACT_VERSION_0: - brutto = &contract->details.v0.brutto; + brutto = &contract->pc->details.v0.brutto; break; case TALER_MERCHANT_CONTRACT_VERSION_1: { struct TALER_MERCHANT_ContractChoice *choice - = &contract->details.v1.choices[choice_index]; + = &contract->pc->details.v1.choices[choice_index]; - GNUNET_assert (choice_index < contract->details.v1.choices_len); + GNUNET_assert (choice_index < contract->pc->details.v1.choices_len); brutto = &choice->amount; } break; @@ -634,7 +634,7 @@ add_order (void *cls, if (0 > TALER_amount_cmp (&prc.total_refund_amount, brutto) && GNUNET_TIME_absolute_is_future ( - contract->common.refund_deadline.abs_time)) + contract->pc->refund_deadline.abs_time)) refundable = true; } @@ -645,7 +645,7 @@ add_order (void *cls, case TALER_MERCHANT_CONTRACT_VERSION_0: { amount = (NULL != contract) - ? &contract->details.v0.brutto + ? &contract->pc->details.v0.brutto : &order->details.v0.brutto; if (TALER_amount_is_zero (amount) && @@ -681,9 +681,9 @@ add_order (void *cls, if (NULL != contract) { struct TALER_MERCHANT_ContractChoice *choice - = &contract->details.v1.choices[choice_index]; + = &contract->pc->details.v1.choices[choice_index]; - GNUNET_assert (choice_index < contract->details.v1.choices_len); + GNUNET_assert (choice_index < contract->pc->details.v1.choices_len); amount = &choice->amount; /* Accumulate order total */ accumulate_total (po, diff --git a/src/backend/taler-merchant-httpd_post-orders-ORDER_ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ORDER_ID-pay.c @@ -1059,7 +1059,7 @@ batch_deposit_transaction ( dr->details.ok.deposit_timestamp, &pc->check_contract.h_contract_terms, eg->exchange_url, - pc->check_contract.contract_terms->common.wire_deadline, + pc->check_contract.contract_terms->pc->wire_deadline, &dr->details.ok.accumulated_total_without_fee, &eg->wire_fee, &pc->check_contract.wm->h_wire, @@ -1096,7 +1096,7 @@ batch_deposit_transaction ( &dc->deposit_fee, &dc->refund_fee, GNUNET_TIME_absolute_add ( - pc->check_contract.contract_terms->common.wire_deadline.abs_time, + pc->check_contract.contract_terms->pc->wire_deadline.abs_time, GNUNET_TIME_randomize (GNUNET_TIME_UNIT_MINUTES))); if (qs < 0) return qs; @@ -1220,8 +1220,8 @@ notify_kyc_required (const struct ExchangeGroup *eg) char *extra; hws = GNUNET_STRINGS_data_to_string_alloc ( - &eg->pc->check_contract.contract_terms->common.h_wire, - sizeof (eg->pc->check_contract.contract_terms->common.h_wire)); + &eg->pc->check_contract.contract_terms->pc->h_wire, + sizeof (eg->pc->check_contract.contract_terms->pc->h_wire)); GNUNET_asprintf (&extra, "%s %s", hws, @@ -1414,7 +1414,7 @@ do_batch_deposits (struct ExchangeGroup *eg) { struct TALER_EXCHANGE_DepositContractDetail dcd = { .wire_deadline - = pc->check_contract.contract_terms->common.wire_deadline, + = pc->check_contract.contract_terms->pc->wire_deadline, .merchant_payto_uri = pc->check_contract.wm->payto_uri, .extra_wire_subject_metadata @@ -1426,11 +1426,11 @@ do_batch_deposits (struct ExchangeGroup *eg) .wallet_data_hash = pc->parse_wallet_data.h_wallet_data, .wallet_timestamp - = pc->check_contract.contract_terms->common.timestamp, + = pc->check_contract.contract_terms->pc->timestamp, .merchant_pub = hc->instance->merchant_pub, .refund_deadline - = pc->check_contract.contract_terms->common.refund_deadline + = pc->check_contract.contract_terms->pc->refund_deadline }; /* Collect up to TALER_MAX_COINS eligible coins for this batch */ struct TALER_EXCHANGE_CoinDepositDetail cdds[group_size]; @@ -1682,7 +1682,7 @@ process_pay_with_keys ( is_age_restricted_denom = (0 != denom_details->key.age_mask.bits); if (is_age_restricted_denom && - (0 < pc->check_contract.contract_terms->common.minimum_age)) + (0 < pc->check_contract.contract_terms->pc->base->minimum_age)) { /* Minimum age given and restricted coin provided: We need to verify the * minimum age */ @@ -1706,7 +1706,7 @@ process_pay_with_keys ( if (GNUNET_OK != TALER_age_commitment_verify ( &dc->age_commitment, - pc->check_contract.contract_terms->common.minimum_age, + pc->check_contract.contract_terms->pc->base->minimum_age, &dc->minimum_age_sig)) code = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_AGE_VERIFICATION_FAILED; AGE_FAIL: @@ -1941,7 +1941,7 @@ phase_success_response (struct PayContext *pc) pc->check_contract.pos_key, pc->check_contract.pos_alg, &pc->validate_tokens.brutto, - pc->check_contract.contract_terms->common.timestamp); + pc->check_contract.contract_terms->pc->timestamp); pay_end (pc, TALER_MHD_REPLY_JSON_PACK ( pc->connection, @@ -2004,7 +2004,7 @@ phase_payment_notification (struct PayContext *pc) 0); } if ( (NULL != pc->parse_pay.session_id) && - (NULL != pc->check_contract.contract_terms->common.fulfillment_url) ) + (NULL != pc->check_contract.contract_terms->pc->base->fulfillment_url) ) { struct TMH_SessionEventP session_eh = { .header.size = htons (sizeof (session_eh)), @@ -2015,13 +2015,13 @@ phase_payment_notification (struct PayContext *pc) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Notifying clients about session change to %s for %s\n", pc->parse_pay.session_id, - pc->check_contract.contract_terms->common.fulfillment_url); + pc->check_contract.contract_terms->pc->base->fulfillment_url); GNUNET_CRYPTO_hash (pc->parse_pay.session_id, strlen (pc->parse_pay.session_id), &session_eh.h_session_id); GNUNET_CRYPTO_hash ( - pc->check_contract.contract_terms->common.fulfillment_url, - strlen (pc->check_contract.contract_terms->common.fulfillment_url), + pc->check_contract.contract_terms->pc->base->fulfillment_url, + strlen (pc->check_contract.contract_terms->pc->base->fulfillment_url), &session_eh.h_fulfillment_url); TALER_MERCHANTDB_event_notify (TMH_db, &session_eh.header, @@ -2404,10 +2404,10 @@ phase_compute_money_pots (struct PayContext *pc) TALER_amount_set_zero (pc->parse_pay.dc[0].cdd.amount.currency, &assigned)); GNUNET_assert (NULL != contract); - for (size_t i = 0; i<contract->common.products_len; i++) + for (size_t i = 0; i<contract->pc->products_len; i++) { const struct TALER_MERCHANT_ProductSold *product - = &contract->common.products[i]; + = &contract->pc->products[i]; const struct TALER_Amount *price = NULL; /* find price in the right currency */ @@ -2468,14 +2468,14 @@ phase_compute_money_pots (struct PayContext *pc) } if ( (! TALER_amount_is_zero (&left)) && - (0 != contract->common.default_money_pot) ) + (0 != contract->pc->base->default_money_pot) ) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Computing money pot %llu increment as %s\n", - (unsigned long long) contract->common.default_money_pot, + (unsigned long long) contract->pc->base->default_money_pot, TALER_amount2s (&left)); increment_pot (pc, - contract->common.default_money_pot, + contract->pc->base->default_money_pot, &left); } } @@ -3132,7 +3132,7 @@ phase_execute_pay_transaction (struct PayContext *pc) { const struct TALER_MERCHANT_ContractChoice *choice = - &pc->check_contract.contract_terms->details.v1 + &pc->check_contract.contract_terms->pc->details.v1 .choices[pc->parse_wallet_data.choice_index]; for (size_t i = 0; i<pc->output_tokens_len; i++) @@ -3203,7 +3203,7 @@ phase_execute_pay_transaction (struct PayContext *pc) TMH_notify_order_change ( hc->instance, TMH_OSF_CLAIMED | TMH_OSF_PAID, - pc->check_contract.contract_terms->common.timestamp, + pc->check_contract.contract_terms->pc->timestamp, pc->check_contract.order_serial); { enum GNUNET_DB_QueryStatus qs; @@ -3540,11 +3540,11 @@ find_family (const struct PayContext *pc, const char *slug) { for (unsigned int i = 0; - i < pc->check_contract.contract_terms->details.v1.token_authorities_len; + i < pc->check_contract.contract_terms->pc->details.v1.token_authorities_len; i++) { const struct TALER_MERCHANT_ContractTokenFamily *tfi - = &pc->check_contract.contract_terms->details.v1.token_authorities[i]; + = &pc->check_contract.contract_terms->pc->details.v1.token_authorities[i]; if (0 == strcmp (tfi->slug, slug)) @@ -3622,8 +3622,8 @@ handle_output_token (struct PayContext *pc, TMH_db, pc->hc->instance->settings.id, family->slug, - pc->check_contract.contract_terms->common.timestamp, - pc->check_contract.contract_terms->common.pay_deadline, + pc->check_contract.contract_terms->pc->timestamp, + pc->check_contract.contract_terms->pc->pay_deadline, &details); switch (qs) { @@ -3646,9 +3646,9 @@ handle_output_token (struct PayContext *pc, "Token-family key for %s not found at [%llu,%llu]\n", family->slug, (unsigned long long) - pc->check_contract.contract_terms->common.timestamp.abs_time.abs_value_us, + pc->check_contract.contract_terms->pc->timestamp.abs_time.abs_value_us, (unsigned long long) - pc->check_contract.contract_terms->common.pay_deadline.abs_time.abs_value_us + pc->check_contract.contract_terms->pc->pay_deadline.abs_time.abs_value_us ); GNUNET_break (0); pay_end (pc, @@ -3846,20 +3846,20 @@ phase_validate_tokens (struct PayContext *pc) /* We haven't seen a donau output yet. */ pc->validate_tokens.donau_output_index = -1; - switch (pc->check_contract.contract_terms->common.version) + switch (pc->check_contract.contract_terms->pc->base->version) { case TALER_MERCHANT_CONTRACT_VERSION_0: /* No tokens to validate */ pc->phase = PP_COMPUTE_MONEY_POTS; pc->validate_tokens.max_fee - = pc->check_contract.contract_terms->details.v0.max_fee; + = pc->check_contract.contract_terms->pc->details.v0.max_fee; pc->validate_tokens.brutto - = pc->check_contract.contract_terms->details.v0.brutto; + = pc->check_contract.contract_terms->pc->details.v0.brutto; break; case TALER_MERCHANT_CONTRACT_VERSION_1: { const struct TALER_MERCHANT_ContractChoice *selected - = &pc->check_contract.contract_terms->details.v1.choices[ + = &pc->check_contract.contract_terms->pc->details.v1.choices[ pc->parse_wallet_data.choice_index]; unsigned int output_off; unsigned int cnt; @@ -4184,8 +4184,8 @@ append_output_token_sig (void *cls, 0, sizeof (out)); GNUNET_assert (TALER_MERCHANT_CONTRACT_VERSION_1 == - pc->check_contract.contract_terms->common.version); - choice = &pc->check_contract.contract_terms->details.v1 + pc->check_contract.contract_terms->pc->base->version); + choice = &pc->check_contract.contract_terms->pc->details.v1 .choices[pc->parse_wallet_data.choice_index]; output = &choice->outputs[pc->output_index_gen]; cnt = count_output_tokens (pc, @@ -4472,7 +4472,7 @@ phase_check_contract (struct PayContext *pc) /* Check fundamentals */ { - switch (pc->check_contract.contract_terms->common.version) + switch (pc->check_contract.contract_terms->pc->base->version) { case TALER_MERCHANT_CONTRACT_VERSION_0: { @@ -4507,14 +4507,15 @@ phase_check_contract (struct PayContext *pc) return; } if (pc->parse_wallet_data.choice_index >= - pc->check_contract.contract_terms->details.v1.choices_len) + pc->check_contract.contract_terms->pc->details.v1.choices_len) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Order `%s' has choices array with %u elements but " - "request has 'choice_index' field with value %d\n", - pc->order_id, - pc->check_contract.contract_terms->details.v1.choices_len, - pc->parse_wallet_data.choice_index); + GNUNET_log ( + GNUNET_ERROR_TYPE_INFO, + "Order `%s' has choices array with %u elements but " + "request has 'choice_index' field with value %d\n", + pc->order_id, + pc->check_contract.contract_terms->pc->details.v1.choices_len, + pc->parse_wallet_data.choice_index); GNUNET_break (0); pay_end (pc, TALER_MHD_reply_with_error ( @@ -4540,9 +4541,9 @@ phase_check_contract (struct PayContext *pc) } if (GNUNET_TIME_timestamp_cmp ( - pc->check_contract.contract_terms->common.wire_deadline, + pc->check_contract.contract_terms->pc->wire_deadline, <, - pc->check_contract.contract_terms->common.refund_deadline)) + pc->check_contract.contract_terms->pc->refund_deadline)) { /* This should already have been checked when creating the order! */ GNUNET_break (0); @@ -4555,7 +4556,7 @@ phase_check_contract (struct PayContext *pc) return; } if (GNUNET_TIME_absolute_is_past ( - pc->check_contract.contract_terms->common.pay_deadline.abs_time)) + pc->check_contract.contract_terms->pc->pay_deadline.abs_time)) { /* too late */ pay_end (pc, @@ -4574,7 +4575,7 @@ phase_check_contract (struct PayContext *pc) wm = pc->hc->instance->wm_head; while (0 != GNUNET_memcmp ( - &pc->check_contract.contract_terms->common.h_wire, + &pc->check_contract.contract_terms->pc->h_wire, &wm->h_wire)) wm = wm->next; if (NULL == wm) diff --git a/src/backend/taler-merchant-httpd_post-private-orders.c b/src/backend/taler-merchant-httpd_post-private-orders.c @@ -318,169 +318,20 @@ struct OrderContext { /** - * Our order ID. + * The main order data as provided by the client. */ - char *order_id; + struct TALER_MERCHANT_Order *order; /** - * Summary of the contract. - */ - const char *summary; - - /** - * Internationalized summary. - */ - const json_t *summary_i18n; - - /** - * URL that will show that the contract was successful - * after it has been paid for. - */ - const char *fulfillment_url; - - /** - * Message shown to the customer after paying for the contract. - * Either fulfillment_url or fulfillment_message must be specified. - */ - const char *fulfillment_message; - - /** - * Map from IETF BCP 47 language tags to localized fulfillment messages. - */ - const json_t *fulfillment_message_i18n; - - /** - * Length of the @e products array. - */ - size_t products_len; - - /** - * Array of products that are being sold. - */ - struct TALER_MERCHANT_ProductSold *products; - - /** - * URL where the same contract could be ordered again (if available). - */ - const char *public_reorder_url; - - /** - * Merchant base URL. + * Base URL of this merchant. */ char *merchant_base_url; /** - * Timestamp of the order. - */ - struct GNUNET_TIME_Timestamp timestamp; - - /** - * Deadline for refunds. - */ - struct GNUNET_TIME_Timestamp refund_deadline; - - /** - * Payment deadline. - */ - struct GNUNET_TIME_Timestamp pay_deadline; - - /** - * Wire transfer deadline. - */ - struct GNUNET_TIME_Timestamp wire_deadline; - - /** * Wire transfer round-up interval to apply. */ enum GNUNET_TIME_RounderInterval wire_deadline_rounder; - /** - * Delivery date. - */ - struct GNUNET_TIME_Timestamp delivery_date; - - /** - * 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. - */ - const json_t *extra; - - /** - * Minimum age required by the order. - */ - uint32_t minimum_age; - - /** - * Money pot to increment for whatever order payment amount - * is not yet assigned to a pot via the Product. - */ - uint64_t order_default_money_pot; - - /** - * Version of the contract terms. - */ - enum TALER_MERCHANT_ContractVersion 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; - - /** - * Tip included by the customer (part of the total amount). - */ - struct TALER_Amount tip; - - /** - * True if @e tip was not provided. - */ - bool no_tip; - - /** - * 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; /** @@ -956,14 +807,20 @@ clean_order (void *cls) json_decref (oc->set_exchanges.exchange_rejections); oc->set_exchanges.exchange_rejections = NULL; } - switch (oc->parse_order.version) + if (NULL != oc->parse_order.order) { - case TALER_MERCHANT_CONTRACT_VERSION_0: - break; - case TALER_MERCHANT_CONTRACT_VERSION_1: - GNUNET_free (oc->set_max_fee.details.v1.max_fees); - GNUNET_free (oc->set_exchanges.details.v1.max_stefan_fees); - break; + switch (oc->parse_order.order->base->version) + { + case TALER_MERCHANT_CONTRACT_VERSION_0: + break; + case TALER_MERCHANT_CONTRACT_VERSION_1: + GNUNET_free (oc->set_max_fee.details.v1.max_fees); + GNUNET_free (oc->set_exchanges.details.v1.max_stefan_fees); + break; + } + TALER_MERCHANT_order_free (oc->parse_order.order); + oc->parse_order.order = NULL; + GNUNET_free (oc->parse_order.merchant_base_url); } if (NULL != oc->merge_inventory.products) { @@ -977,12 +834,6 @@ clean_order (void *cls) GNUNET_array_grow (oc->parse_choices.choices, oc->parse_choices.choices_len, 0); - for (size_t i = 0; i<oc->parse_order.products_len; i++) - { - TALER_MERCHANT_product_sold_free (&oc->parse_order.products[i]); - } - GNUNET_free (oc->parse_order.products); - oc->parse_order.products_len = 0; for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++) { struct TALER_MERCHANT_ContractTokenFamily *mctf @@ -1028,8 +879,6 @@ clean_order (void *cls) GNUNET_free (oc->parse_request.pos_key); json_decref (oc->parse_request.order); json_decref (oc->serialize_order.contract); - GNUNET_free (oc->parse_order.order_id); - GNUNET_free (oc->parse_order.merchant_base_url); GNUNET_free (oc); } @@ -1126,7 +975,7 @@ execute_transaction (struct OrderContext *oc) qs = TALER_MERCHANTDB_lookup_order (TMH_db, oc->hc->instance->settings.id, - oc->parse_order.order_id, + oc->parse_order.order->order_id, &oc->execute_order.token, &orig_post, &contract_terms); @@ -1160,10 +1009,10 @@ execute_transaction (struct OrderContext *oc) /* Setup order */ qs = TALER_MERCHANTDB_insert_order (TMH_db, oc->hc->instance->settings.id, - oc->parse_order.order_id, + oc->parse_order.order->order_id, oc->parse_request.session_id, &oc->parse_request.h_post_data, - oc->parse_order.pay_deadline, + oc->parse_order.order->pay_deadline, &oc->parse_request.claim_token, oc->serialize_order.contract, /* called 'contract terms' at database. */ oc->parse_request.pos_key, @@ -1196,7 +1045,7 @@ execute_transaction (struct OrderContext *oc) qs = TALER_MERCHANTDB_insert_order_lock ( TMH_db, oc->hc->instance->settings.id, - oc->parse_order.order_id, + oc->parse_order.order->order_id, oc->parse_request.inventory_products[i].product_id, oc->parse_request.inventory_products[i].quantity, oc->parse_request.inventory_products[i].quantity_frac); @@ -1217,11 +1066,12 @@ execute_transaction (struct OrderContext *oc) /* Get the order serial and timestamp for the order we just created to update long-poll clients. */ - qs = TALER_MERCHANTDB_lookup_order_summary (TMH_db, - oc->hc->instance->settings.id, - oc->parse_order.order_id, - &timestamp, - &order_serial); + qs = TALER_MERCHANTDB_lookup_order_summary ( + TMH_db, + oc->hc->instance->settings.id, + oc->parse_order.order->order_id, + &timestamp, + &order_serial); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) { TALER_MERCHANTDB_rollback (TMH_db); @@ -1233,7 +1083,7 @@ execute_transaction (struct OrderContext *oc) jhook = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("order_id", - oc->parse_order.order_id), + oc->parse_order.order->order_id), GNUNET_JSON_pack_object_incref ("contract", oc->serialize_order.contract), GNUNET_JSON_pack_string ("instance_id", @@ -1287,9 +1137,9 @@ yield_success_response (struct OrderContext *oc, oc->connection, MHD_HTTP_OK, GNUNET_JSON_pack_string ("order_id", - oc->parse_order.order_id), + oc->parse_order.order->order_id), GNUNET_JSON_pack_timestamp ("pay_deadline", - oc->parse_order.pay_deadline), + oc->parse_order.order->pay_deadline), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_data_auto ( "token", @@ -1315,7 +1165,7 @@ phase_execute_order (struct OrderContext *oc) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing database transaction to create order '%s' for instance '%s'\n", - oc->parse_order.order_id, + oc->parse_order.order->order_id, settings->id); for (unsigned int i = 0; i<MAX_RETRIES; i++) { @@ -1344,7 +1194,7 @@ phase_execute_order (struct OrderContext *oc) oc, MHD_HTTP_CONFLICT, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS, - oc->parse_order.order_id); + oc->parse_order.order->order_id); return; } /* Other hard transaction error (disk full, etc.) */ @@ -1372,7 +1222,7 @@ phase_execute_order (struct OrderContext *oc) oc, MHD_HTTP_CONFLICT, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS, - oc->parse_order.order_id); + oc->parse_order.order->order_id); return; } @@ -1797,7 +1647,7 @@ add_input_token_family (struct OrderContext *oc, const char *slug) { struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); - struct GNUNET_TIME_Timestamp end = oc->parse_order.pay_deadline; + struct GNUNET_TIME_Timestamp end = oc->parse_order.order->pay_deadline; enum GNUNET_DB_QueryStatus qs; enum TALER_ErrorCode ec = TALER_EC_INVALID; /* make compiler happy */ unsigned int http_status = 0; /* make compiler happy */ @@ -1945,12 +1795,13 @@ add_output_token_family (struct OrderContext *oc, valid_at, key_index)) ) return GNUNET_OK; - qs = TALER_MERCHANTDB_lookup_token_family_key (TMH_db, - oc->hc->instance->settings.id, - slug, - valid_at, - oc->parse_order.pay_deadline, - &key_details); + qs = TALER_MERCHANTDB_lookup_token_family_key ( + TMH_db, + oc->hc->instance->settings.id, + slug, + valid_at, + oc->parse_order.order->pay_deadline, + &key_details); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: @@ -2289,66 +2140,39 @@ phase_serialize_order (struct OrderContext *oc) } oc->serialize_order.contract = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_int64 ("version", - oc->parse_order.version), - 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_allow_null ( - GNUNET_JSON_pack_string ("public_reorder_url", - oc->parse_order.public_reorder_url)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("fulfillment_message", - oc->parse_order.fulfillment_message)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ( - "fulfillment_message_i18n", - (json_t *) oc->parse_order.fulfillment_message_i18n)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("fulfillment_url", - oc->parse_order.fulfillment_url)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_uint64 ("minimum_age", - oc->parse_order.minimum_age)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_uint64 ("default_money_pot", - oc->parse_order.order_default_money_pot)), - GNUNET_JSON_pack_array_incref ("products", - oc->merge_inventory.products), - GNUNET_JSON_pack_data_auto ("h_wire", - &oc->select_wire_method.wm->h_wire), - GNUNET_JSON_pack_string ("wire_method", - oc->select_wire_method.wm->wire_method), - GNUNET_JSON_pack_string ("order_id", - oc->parse_order.order_id), - GNUNET_JSON_pack_timestamp ("timestamp", - oc->parse_order.timestamp), - GNUNET_JSON_pack_timestamp ("pay_deadline", - oc->parse_order.pay_deadline), - GNUNET_JSON_pack_timestamp ("wire_transfer_deadline", - oc->parse_order.wire_deadline), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_timestamp ("delivery_date", - oc->parse_order.delivery_date)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ( - "delivery_location", - (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", - merchant), - GNUNET_JSON_pack_data_auto ("merchant_pub", - &oc->hc->instance->merchant_pub), - GNUNET_JSON_pack_array_incref ("exchanges", - oc->select_wire_method.exchanges), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("extra", - (json_t *) oc->parse_order.extra)) - ); + GNUNET_JSON_pack_object_steal ( + NULL, + TALER_MERCHANT_base_terms_serialize (oc->parse_order.order->base)), + GNUNET_JSON_pack_array_incref ( + "products", + oc->merge_inventory.products), + GNUNET_JSON_pack_data_auto ( + "h_wire", + &oc->select_wire_method.wm->h_wire), + GNUNET_JSON_pack_string ( + "wire_method", + oc->select_wire_method.wm->wire_method), + GNUNET_JSON_pack_timestamp ( + "timestamp", + oc->parse_order.order->timestamp), + GNUNET_JSON_pack_timestamp ( + "pay_deadline", + oc->parse_order.order->pay_deadline), + GNUNET_JSON_pack_timestamp ( + "wire_transfer_deadline", + oc->parse_order.order->wire_transfer_deadline), + GNUNET_JSON_pack_string ( + "merchant_base_url", + oc->parse_order.merchant_base_url), + GNUNET_JSON_pack_object_steal ( + "merchant", + merchant), + GNUNET_JSON_pack_data_auto ( + "merchant_pub", + &oc->hc->instance->merchant_pub), + GNUNET_JSON_pack_array_incref ( + "exchanges", + oc->select_wire_method.exchanges)); { json_t *xtra; @@ -3240,7 +3064,8 @@ phase_add_payment_details (struct OrderContext *oc) { oc->add_payment_details.need_exchange = true; } - for (unsigned int j = 0; j<oc->add_payment_details.num_max_choice_limits; + for (unsigned int j = 0; + j<oc->add_payment_details.num_max_choice_limits; j++) { struct TALER_Amount *mx = &oc->add_payment_details.max_choice_limits[j]; @@ -3942,246 +3767,40 @@ phase_parse_order (struct OrderContext *oc) { const struct TALER_MERCHANTDB_InstanceSettings *settings = &oc->hc->instance->settings; - const char *merchant_base_url = NULL; - uint64_t version = 0; - const json_t *jmerchant = NULL; - const json_t *products = NULL; - const char *order_id = NULL; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint64 ("version", - &version), - NULL), - GNUNET_JSON_spec_string ("summary", - &oc->parse_order.summary), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_array_const ("products", - &products), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("summary_i18n", - &oc->parse_order.summary_i18n), - NULL), - GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_slug ("order_id", - &order_id), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("fulfillment_message", - &oc->parse_order.fulfillment_message), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("fulfillment_message_i18n", - &oc->parse_order.fulfillment_message_i18n), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("fulfillment_url", - &oc->parse_order.fulfillment_url), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("public_reorder_url", - &oc->parse_order.public_reorder_url), - 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), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_timestamp ("timestamp", - &oc->parse_order.timestamp), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_timestamp ("refund_deadline", - &oc->parse_order.refund_deadline), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_timestamp ("pay_deadline", - &oc->parse_order.pay_deadline), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", - &oc->parse_order.wire_deadline), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("delivery_location", - &oc->parse_order.delivery_location), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_timestamp ("delivery_date", - &oc->parse_order.delivery_date), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("minimum_age", - &oc->parse_order.minimum_age), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_relative_time ("auto_refund", - &oc->parse_order.auto_refund), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_object_const ("extra", - &oc->parse_order.extra), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint64 ("order_default_money_pot", - &oc->parse_order.order_default_money_pot), - NULL), - GNUNET_JSON_spec_end () - }; enum GNUNET_GenericReturnValue ret; bool computed_refund_deadline = false; - oc->parse_order.refund_deadline = GNUNET_TIME_UNIT_FOREVER_TS; - oc->parse_order.wire_deadline = GNUNET_TIME_UNIT_FOREVER_TS; - ret = TALER_MHD_parse_json_data (oc->connection, - oc->parse_request.order, - spec); - if (GNUNET_OK != ret) + oc->parse_order.order + = TALER_MERCHANT_order_parse ( + oc->parse_request.order); + if (NULL == oc->parse_order.order) { GNUNET_break_op (0); - finalize_order2 (oc, - ret); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "order"); return; } - if ( (NULL != products) && - (0 != (oc->parse_order.products_len = json_array_size (products))) ) - { - size_t i; - json_t *p; - oc->parse_order.products - = GNUNET_new_array (oc->parse_order.products_len, - struct TALER_MERCHANT_ProductSold); - json_array_foreach (products, i, p) - { - if (GNUNET_OK != - TALER_MERCHANT_parse_product_sold (p, - &oc->parse_order.products[i])) - { - GNUNET_break_op (0); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "order.products"); - return; - } - } - } - switch (version) + switch (oc->parse_order.order->base->version) { - case 0: - { - 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 ( - "tip", - &oc->parse_order.details.v0.tip), - &oc->parse_order.details.v0.no_tip), - 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 ( (! oc->parse_order.details.v0.no_tip) && - (GNUNET_OK != - TALER_amount_cmp_currency (&oc->parse_order.details.v0.brutto, - &oc->parse_order.details.v0.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; - } - if (! TMH_test_exchange_configured_for_currency ( - oc->parse_order.details.v0.brutto.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, - oc->parse_order.details.v0.brutto.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_MERCHANT_CONTRACT_VERSION_0; - break; - } - case 1: + case TALER_MERCHANT_CONTRACT_VERSION_0: + if (! TMH_test_exchange_configured_for_currency ( + oc->parse_order.order->details.v0.brutto.currency)) { - 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_MERCHANT_CONTRACT_VERSION_1; - break; + 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, + oc->parse_order.order->details.v0.brutto.currency); + return; } + break; + case TALER_MERCHANT_CONTRACT_VERSION_1: + break; default: GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); @@ -4193,11 +3812,7 @@ phase_parse_order (struct OrderContext *oc) } /* Add order_id if it doesn't exist. */ - if (NULL != order_id) - { - oc->parse_order.order_id = GNUNET_strdup (order_id); - } - else + if (NULL == oc->parse_order.order->order_id) { char buf[256]; time_t timer; @@ -4237,17 +3852,15 @@ phase_parse_order (struct OrderContext *oc) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Assigning order ID `%s' server-side\n", buf); - - oc->parse_order.order_id = GNUNET_strdup (buf); - GNUNET_assert (NULL != oc->parse_order.order_id); + oc->parse_order.order->order_id = GNUNET_strdup (buf); } /* Patch fulfillment URL with order_id (implements #6467). */ - if (NULL != oc->parse_order.fulfillment_url) + if (NULL != oc->parse_order.order->base->fulfillment_url) { const char *pos; - pos = strstr (oc->parse_order.fulfillment_url, + pos = strstr (oc->parse_order.odrer->base->fulfillment_url, "${ORDER_ID}"); if (NULL != pos) { @@ -4266,32 +3879,35 @@ phase_parse_order (struct OrderContext *oc) return; } - GNUNET_asprintf (&nurl, - "%.*s%s%s", - /* first output URL until ${ORDER_ID} */ - (int) (pos - oc->parse_order.fulfillment_url), - oc->parse_order.fulfillment_url, - /* replace ${ORDER_ID} with the right order_id */ - oc->parse_order.order_id, - /* append rest of original URL */ - pos + strlen ("${ORDER_ID}")); - - oc->parse_order.fulfillment_url = GNUNET_strdup (nurl); - + GNUNET_asprintf ( + &nurl, + "%.*s%s%s", + /* first output URL until ${ORDER_ID} */ + (int) (pos - oc->parse_order.order->base->fulfillment_url), + oc->parse_order.order->base->fulfillment_url, + /* replace ${ORDER_ID} with the right order_id */ + oc->parse_order.order->order_id, + /* append rest of original URL */ + pos + strlen ("${ORDER_ID}")); + oc->parse_order.order->base->fulfillment_url = GNUNET_strdup (nurl); GNUNET_free (nurl); } } - if ( (GNUNET_TIME_absolute_is_zero (oc->parse_order.pay_deadline.abs_time)) || - (GNUNET_TIME_absolute_is_never (oc->parse_order.pay_deadline.abs_time)) ) + if ( (GNUNET_TIME_absolute_is_zero ( + oc->parse_order.order->pay_deadline.abs_time)) || + (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); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Pay deadline was zero (or never), setting to %s\n", - GNUNET_TIME_timestamp2s (oc->parse_order.pay_deadline)); + GNUNET_TIME_timestamp2s ( + oc->parse_order.order->pay_deadline)); } - else if (GNUNET_TIME_absolute_is_past (oc->parse_order.pay_deadline.abs_time)) + else if (GNUNET_TIME_absolute_is_past ( + oc->parse_order.order->pay_deadline.abs_time)) { GNUNET_break_op (0); reply_with_error ( @@ -4303,7 +3919,8 @@ phase_parse_order (struct OrderContext *oc) } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Pay deadline is %s\n", - GNUNET_TIME_timestamp2s (oc->parse_order.pay_deadline)); + GNUNET_TIME_timestamp2s ( + oc->parse_order.order->pay_deadline)); /* Check soundness of refund deadline, and that a timestamp * is actually present. */ @@ -4311,35 +3928,38 @@ phase_parse_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 (oc->parse_order.timestamp.abs_time)) + if (GNUNET_TIME_absolute_is_zero ( + oc->parse_order.order->timestamp.abs_time)) { - oc->parse_order.timestamp = now; + oc->parse_order.order->timestamp = now; } /* If no refund_deadline given, set one based on refund_delay. */ if (GNUNET_TIME_absolute_is_never ( - oc->parse_order.refund_deadline.abs_time)) + oc->parse_order.order->refund_deadline.abs_time)) { - if (GNUNET_TIME_relative_is_zero (oc->parse_request.refund_delay)) + if (GNUNET_TIME_relative_is_zero ( + oc->parse_request.refund_delay)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Refund delay is zero, no refunds are possible for this order\n"); - oc->parse_order.refund_deadline = GNUNET_TIME_UNIT_ZERO_TS; + oc->parse_order.order->refund_deadline = GNUNET_TIME_UNIT_ZERO_TS; } else { computed_refund_deadline = true; - oc->parse_order.refund_deadline + oc->parse_order.order->refund_deadline = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add (oc->parse_order.pay_deadline.abs_time, - oc->parse_request.refund_delay)); + GNUNET_TIME_absolute_add ( + oc->parse_order.order->pay_deadline.abs_time, + oc->parse_request.refund_delay)); } } if ( (! GNUNET_TIME_absolute_is_zero ( - oc->parse_order.delivery_date.abs_time)) && + oc->parse_order.order->base->delivery_date.abs_time)) && (GNUNET_TIME_absolute_is_past ( - oc->parse_order.delivery_date.abs_time)) ) + oc->parse_order.order->base->delivery_date.abs_time)) ) { GNUNET_break_op (0); reply_with_error ( @@ -4352,9 +3972,9 @@ phase_parse_order (struct OrderContext *oc) } if ( (! GNUNET_TIME_absolute_is_zero ( - oc->parse_order.refund_deadline.abs_time)) && + oc->parse_order.order->refund_deadline.abs_time)) && (GNUNET_TIME_absolute_is_past ( - oc->parse_order.refund_deadline.abs_time)) ) + oc->parse_order.order->refund_deadline.abs_time)) ) { GNUNET_break_op (0); reply_with_error ( @@ -4365,14 +3985,15 @@ phase_parse_order (struct OrderContext *oc) return; } - if (GNUNET_TIME_absolute_is_never (oc->parse_order.wire_deadline.abs_time)) + if (GNUNET_TIME_absolute_is_never ( + oc->parse_order.order->wire_deadline.abs_time)) { struct GNUNET_TIME_Absolute start; start = GNUNET_TIME_absolute_max ( - oc->parse_order.refund_deadline.abs_time, - oc->parse_order.pay_deadline.abs_time); - oc->parse_order.wire_deadline + oc->parse_order.order->refund_deadline.abs_time, + oc->parse_order.order->pay_deadline.abs_time); + oc->parse_order.order->wire_deadline = GNUNET_TIME_absolute_to_timestamp ( GNUNET_TIME_round_up ( GNUNET_TIME_absolute_add ( @@ -4380,7 +4001,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.wire_deadline.abs_time)) + oc->parse_order.order->wire_deadline.abs_time)) { GNUNET_break_op (0); reply_with_error ( @@ -4396,13 +4017,13 @@ phase_parse_order (struct OrderContext *oc) /* if we computed the refund_deadline from default settings and did have a configured wire_deadline, make sure that the refund_deadline is at or below the wire_deadline. */ - oc->parse_order.refund_deadline - = GNUNET_TIME_timestamp_min (oc->parse_order.refund_deadline, - oc->parse_order.wire_deadline); + oc->parse_order.order->refund_deadline + = GNUNET_TIME_timestamp_min (oc->parse_order.order->refund_deadline, + oc->parse_order.order->wire_deadline); } - if (GNUNET_TIME_timestamp_cmp (oc->parse_order.wire_deadline, + if (GNUNET_TIME_timestamp_cmp (oc->parse_order.order->wire_deadline, <, - oc->parse_order.refund_deadline)) + oc->parse_order.order->refund_deadline)) { GNUNET_break_op (0); reply_with_error ( @@ -4413,23 +4034,6 @@ phase_parse_order (struct OrderContext *oc) return; } - if (NULL != merchant_base_url) - { - if (('\0' == *merchant_base_url) || - ('/' != merchant_base_url[strlen (merchant_base_url) - 1])) - { - GNUNET_break_op (0); - reply_with_error ( - oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR, - "merchant_base_url is not valid"); - return; - } - oc->parse_order.merchant_base_url - = GNUNET_strdup (merchant_base_url); - } - else { char *url; @@ -4448,20 +4052,9 @@ phase_parse_order (struct OrderContext *oc) oc->parse_order.merchant_base_url = url; } - /* Merchant information must not already be present */ - if (NULL != jmerchant) - { - GNUNET_break_op (0); - reply_with_error ( - oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR, - "'merchant' field already set, but must be provided by backend"); - return; - } - - if ( (NULL != oc->parse_order.delivery_location) && - (! TMH_location_object_valid (oc->parse_order.delivery_location)) ) + // FIXME: move to util during parsing! + if ( (NULL != oc->parse_order.order->base->delivery_location) && + (! TMH_location_object_valid (oc->parse_order.order->base->delivery_location)) ) { GNUNET_break_op (0); reply_with_error (oc, diff --git a/src/include/taler/taler_merchant_util.h b/src/include/taler/taler_merchant_util.h @@ -1364,11 +1364,6 @@ struct TALER_MERCHANT_ContractBaseTerms json_t *summary_i18n; /** - * Our order ID. - */ - char *order_id; - - /** * URL where the same contract could be ordered again (if available). * Optional. */ @@ -1455,6 +1450,11 @@ struct TALER_MERCHANT_Order struct TALER_MERCHANT_ContractBaseTerms *base; /** + * Our order ID. Optional. + */ + char *order_id; + + /** * Array of products that are part of the purchase. */ struct TALER_MERCHANT_ProductSold *products; @@ -1567,6 +1567,11 @@ struct TALER_MERCHANT_ProtoContract struct TALER_MERCHANT_ContractBaseTerms *base; /** + * Our order ID. + */ + char *order_id; + + /** * Timestamp of the contract. */ struct GNUNET_TIME_Timestamp timestamp; diff --git a/src/util/base_terms_parse.c b/src/util/base_terms_parse.c @@ -46,8 +46,6 @@ TALER_MERCHANT_base_terms_parse ( GNUNET_JSON_spec_object_copy ("summary_i18n", &ct->summary_i18n), NULL), - GNUNET_JSON_spec_string_copy ("order_id", - &ct->order_id), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string_copy ("public_reorder_url", &ct->public_reorder_url), @@ -135,7 +133,6 @@ TALER_MERCHANT_base_terms_free ( if (NULL == ct) return; GNUNET_free (ct->public_reorder_url); - GNUNET_free (ct->order_id); GNUNET_free (ct->summary); GNUNET_free (ct->fulfillment_url); GNUNET_free (ct->fulfillment_message); diff --git a/src/util/base_terms_serialize.c b/src/util/base_terms_serialize.c @@ -46,8 +46,6 @@ TALER_MERCHANT_base_terms_serialize ( GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_object_steal ("summary_i18n", ct->summary_i18n)), - GNUNET_JSON_pack_string ("order_id", - ct->order_id), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("public_reorder_url", ct->public_reorder_url)), diff --git a/src/util/contract_parse.c b/src/util/contract_parse.c @@ -158,6 +158,8 @@ TALER_MERCHANT_proto_contract_parse ( { const json_t *products = NULL; struct GNUNET_JSON_Specification espec[] = { + GNUNET_JSON_spec_string_copy ("order_id", + &pc->order_id), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_array_const ("products", &products), @@ -296,6 +298,7 @@ TALER_MERCHANT_proto_contract_free ( json_decref (pc->exchanges); pc->exchanges = NULL; } + GNUNET_free (pc->order_id); GNUNET_free (pc); } diff --git a/src/util/contract_serialize.c b/src/util/contract_serialize.c @@ -135,6 +135,8 @@ success: return GNUNET_JSON_PACK ( GNUNET_JSON_pack_object_steal (NULL, bj), + GNUNET_JSON_pack_string ("order_id", + pc->order_id), GNUNET_JSON_pack_timestamp ("timestamp", pc->timestamp), GNUNET_JSON_pack_timestamp ("refund_deadline", diff --git a/src/util/order_parse.c b/src/util/order_parse.c @@ -164,6 +164,10 @@ TALER_MERCHANT_order_parse (json_t *input) &products), NULL), GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string_copy ("order_id", + &order->order_id), + NULL), + GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("timestamp", &order->timestamp), NULL), @@ -276,6 +280,7 @@ TALER_MERCHANT_order_free ( GNUNET_free (order->products); order->products_len = 0; } + GNUNET_free (order->order_id); if (NULL != order->base) { TALER_MERCHANT_base_terms_free (order->base);