merchant

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

commit ce56e46e30e9f65ce60042d4b8df1e95ff0cfd24
parent 1a5f7b296e1c7440a1bba164de503196e479c320
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date:   Tue, 11 Mar 2025 11:53:31 +0100

moving the processing of the tokens to the new function

Diffstat:
Msrc/backend/taler-merchant-httpd_post-orders-ID-pay.c | 307++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
1 file changed, 227 insertions(+), 80 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c @@ -2726,6 +2726,217 @@ find_family (const struct PayContext *pc, /** + * Handle contract output of type TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN. + * Looks up the token family, loads the matching private key, + * and signs the corresponding token envelopes from the wallet. + * + * @param pc context for the pay request + * @param output contract output we need to process + * @param output_index index of this output in the contract's outputs array + * @return GNUNET_OK on success, GNUNET_NO if an error was encountered + */ +static enum GNUNET_GenericReturnValue +handle_output_token(struct PayContext *pc, + const struct TALER_MERCHANT_ContractOutput *output, + unsigned int output_index) +{ + const struct TALER_MERCHANT_ContractTokenFamily *family; + struct TALER_MERCHANT_ContractTokenFamilyKey *key; + struct TALER_MERCHANTDB_TokenFamilyKeyDetails details; + enum GNUNET_DB_QueryStatus qs; + bool mandatory; + + /* Locate token family in the contract. This should never fail if + * the contract passed validation before insertion. */ + family = find_family(pc, + output->details.token.token_family_slug); + if (NULL == family) + { + /* This "should never happen", so treat it as an internal error */ + GNUNET_break(0); + pay_end(pc, + TALER_MHD_reply_with_error( + pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "token family not found in order")); + return GNUNET_NO; /* signals error */ + } + + /* Check the key_index field from the output. */ + if (output->details.token.key_index >= family->keys_len) + { + /* Also "should never happen", contract was presumably validated on insert */ + GNUNET_break(0); + pay_end(pc, + TALER_MHD_reply_with_error( + pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "key index invalid for token family")); + return GNUNET_NO; + } + + /* Pick the correct key inside that family. */ + key = &family->keys[output->details.token.key_index]; + + /* Fetch the private key from the DB for the merchant instance and + * this particular family/time interval. */ + qs = TMH_db->lookup_token_family_key( + TMH_db->cls, + pc->hc->instance->settings.id, + family->slug, + pc->check_contract.contract_terms->timestamp, + pc->check_contract.contract_terms->pay_deadline, + &details); + if (qs <= 0) + { + GNUNET_log( + GNUNET_ERROR_TYPE_ERROR, + "Did not find key for %s at [%llu,%llu]\n", + family->slug, + (unsigned long long) pc->check_contract.contract_terms->timestamp.abs_time.abs_value_us, + (unsigned long long) pc->check_contract.contract_terms->pay_deadline.abs_time.abs_value_us); + GNUNET_break(0); + pay_end(pc, + TALER_MHD_reply_with_error( + pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL)); + return GNUNET_NO; + } + GNUNET_assert(NULL != details.priv.private_key); + + /* Depending on the token family, decide if the token envelope + * is mandatory or optional. (Simplified logic here: adapt as needed.) */ + mandatory = test_tfk_mandatory(details.token_family.kind); + + /* Actually sign the number of token envelopes specified in 'count'. + * 'output_index' is the offset into the parse_wallet_data arrays. */ + if (GNUNET_OK != + sign_token_envelopes(pc, + key, + &details.priv, + mandatory, + output_index, + output->details.token.count)) + { + /* sign_token_envelopes() already queued up an error via pay_end() */ + return GNUNET_NO; + } + + /* Everything OK. */ + return GNUNET_OK; +} + +/** + * Handle contract output of type TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN. + * Looks up the token family, loads the matching private key, + * and signs the corresponding token envelopes from the wallet. + * + * @param pc context for the pay request + * @param output contract output we need to process + * @param output_index index of this output in the contract's outputs array + * @return GNUNET_OK on success, GNUNET_NO if an error was encountered + */ +static enum GNUNET_GenericReturnValue +handle_output_validate_token(struct PayContext *pc, + const struct TALER_MERCHANT_ContractOutput *output, + unsigned int output_index) +{ + const struct TALER_MERCHANT_ContractTokenFamily *family; + struct TALER_MERCHANT_ContractTokenFamilyKey *key; + struct TALER_MERCHANTDB_TokenFamilyKeyDetails details; + enum GNUNET_DB_QueryStatus qs; + bool mandatory; + + /* Locate token family in the contract. This should never fail if + * the contract passed validation before insertion. */ + family = find_family(pc, + output->details.token.token_family_slug); + if (NULL == family) + { + /* This "should never happen", so treat it as an internal error */ + GNUNET_break(0); + pay_end(pc, + TALER_MHD_reply_with_error( + pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "token family not found in order")); + return GNUNET_NO; /* signals error */ + } + + /* Check the key_index field from the output. */ + if (output->details.token.key_index >= family->keys_len) + { + /* Also "should never happen", contract was presumably validated on insert */ + GNUNET_break(0); + pay_end(pc, + TALER_MHD_reply_with_error( + pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "key index invalid for token family")); + return GNUNET_NO; + } + + /* Pick the correct key inside that family. */ + key = &family->keys[output->details.token.key_index]; + + /* Fetch the private key from the DB for the merchant instance and + * this particular family/time interval. */ + qs = TMH_db->lookup_token_family_key( + TMH_db->cls, + pc->hc->instance->settings.id, + family->slug, + pc->check_contract.contract_terms->timestamp, + pc->check_contract.contract_terms->pay_deadline, + &details); + if (qs <= 0) + { + GNUNET_log( + GNUNET_ERROR_TYPE_ERROR, + "Did not find key for %s at [%llu,%llu]\n", + family->slug, + (unsigned long long) pc->check_contract.contract_terms->timestamp.abs_time.abs_value_us, + (unsigned long long) pc->check_contract.contract_terms->pay_deadline.abs_time.abs_value_us); + GNUNET_break(0); + pay_end(pc, + TALER_MHD_reply_with_error( + pc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL)); + return GNUNET_NO; + } + GNUNET_assert(NULL != details.priv.private_key); + + /* Depending on the token family, decide if the token envelope + * is mandatory or optional. (Simplified logic here: adapt as needed.) */ + mandatory = test_tfk_mandatory(details.token_family.kind); + + /* Actually sign the number of token envelopes specified in 'count'. + * 'output_index' is the offset into the parse_wallet_data arrays. */ + if (GNUNET_OK != + sign_token_envelopes(pc, + key, + &details.priv, + mandatory, + output_index, + output->details.token.count)) + { + /* sign_token_envelopes() already queued up an error via pay_end() */ + return GNUNET_NO; + } + + /* Everything OK. */ + return GNUNET_OK; +} + + +/** * Validate tokens and token envelopes. First, we check if all tokens listed * in the 'inputs' array of the selected choice are present in the 'tokens' * array of the request. Then, we validate the signatures of each provided @@ -2799,92 +3010,28 @@ phase_validate_tokens (struct PayContext *pc) for (unsigned int i = 0; i<selected->outputs_len; i++) { - enum GNUNET_DB_QueryStatus qs; - struct TALER_MERCHANTDB_TokenFamilyKeyDetails details; const struct TALER_MERCHANT_ContractOutput *output = &selected->outputs[i]; - const struct TALER_MERCHANT_ContractTokenFamily *family; - struct TALER_MERCHANT_ContractTokenFamilyKey *key; // FIXME: check donau outputs are good choices // (allowed donau, total amount below max, correct year, ...) // change 'if' to switch... - if (output->type != TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN) - { - /* only validate outputs of type tokens (for now) */ - continue; - } - // FIXME: move this into a function for the switch case on token... - family = find_family (pc, - output->details.token.token_family_slug); - if (NULL == family) - { - /* this should never happen, since the choices and - token families are validated on insert. */ - GNUNET_break (0); - pay_end (pc, - TALER_MHD_reply_with_error ( - pc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "token family not found in order")); - return; - } - if (output->details.token.key_index >= family->keys_len) - { - /* this should never happen, since the choices and - token families are validated on insert. */ - GNUNET_break (0); - pay_end (pc, - TALER_MHD_reply_with_error ( - pc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "key index invalid for token family")); - return; - } - key = &family->keys[output->details.token.key_index]; - qs = TMH_db->lookup_token_family_key ( - TMH_db->cls, - pc->hc->instance->settings.id, - family->slug, - pc->check_contract.contract_terms->timestamp, - pc->check_contract.contract_terms->pay_deadline, - &details); - if (qs <= 0) - { - GNUNET_log ( - GNUNET_ERROR_TYPE_ERROR, - "Did not find key for %s at [%llu,%llu]\n", - family->slug, - (unsigned long long) pc->check_contract.contract_terms->timestamp. - abs_time. - abs_value_us, - (unsigned long long) pc->check_contract.contract_terms->pay_deadline - .abs_time. - abs_value_us); - GNUNET_break (0); - pay_end (pc, - TALER_MHD_reply_with_error ( - pc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL)); - return; - } - - GNUNET_assert (NULL != details.priv.private_key); - if (GNUNET_OK != - sign_token_envelopes ( - pc, - key, - &details.priv, - test_tfk_mandatory (details.token_family.kind), - i, - output->details.token.count)) - { - /* Error is already scheduled from sign_token_envelopes. */ - return; + switch(output->type){ + case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: + if (GNUNET_NO == + handle_output_validate_token (pc, + output, + i)) + { + /* Error is already scheduled from handle_output_token. */ + return; + } + break; + case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: + //FIXME: Add function to process donations + continue; + default: + continue; } } }