merchant

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

commit c926288c9c36394efda1007c7d130cad86832c13
parent ac8357c5a6a45c114214395a1684dd306ade1c10
Author: Florian Dold <florian@dold.me>
Date:   Thu, 16 Oct 2025 18:33:34 +0200

fix idempotent pay requests with tokens

Diffstat:
Msrc/backend/taler-merchant-httpd_post-orders-ID-pay.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
1 file changed, 62 insertions(+), 18 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 @@ -464,6 +464,13 @@ struct PayContext unsigned int output_index_gen; /** + * Counter used to generate the output index in append_output_token_sig(). + * + * Counts the generated tokens _within_ the current output_index_gen. + */ + unsigned int output_token_cnt; + + /** * HTTP status code to use for the reply, i.e 200 for "OK". * Special value UINT_MAX is used to indicate hard errors * (no reply, return #MHD_NO). @@ -3484,6 +3491,37 @@ handle_output_donation_receipt ( /** + * Count tokens produced by an output. + * + * @param pc pay context + * @param output output to consider + * @returns number of output tokens + */ +static unsigned int +count_output_tokens (const struct PayContext *pc, + const struct TALER_MERCHANT_ContractOutput *output) +{ + switch (output->type) + { + case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: + GNUNET_assert (0); + break; + case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: + return output->details.token.count; + case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: +#ifdef HAVE_DONAU_DONAU_SERVICE_H + return pc->parse_wallet_data.num_bkps; +#else + return 0; +#endif + break; + } + /* Not reached. */ + GNUNET_assert (0); +} + + +/** * 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 @@ -3648,23 +3686,8 @@ phase_validate_tokens (struct PayContext *pc) const struct TALER_MERCHANT_ContractOutput *output = &selected->outputs[i]; - cnt = 0; - switch (output->type) - { - case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: - GNUNET_assert (0); - break; - case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: - cnt = output->details.token.count; - output_off += cnt; - break; - case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: -#ifdef HAVE_DONAU_DONAU_SERVICE_H - cnt = pc->parse_wallet_data.num_bkps; - output_off += pc->parse_wallet_data.num_bkps; -#endif - break; - } + output_off += count_output_tokens (pc, + output); for (unsigned int j = 0; j<cnt; j++) pc->output_tokens[output_off + j].output_index = i; } @@ -3850,13 +3873,34 @@ append_output_token_sig (void *cls, { struct PayContext *pc = cls; struct SignedOutputToken out; + struct TALER_MERCHANT_ContractChoice *choice; + const struct TALER_MERCHANT_ContractOutput *output; + unsigned int cnt; + + GNUNET_assert (TALER_MERCHANT_CONTRACT_VERSION_1 == + pc->check_contract.contract_terms->version); - out.output_index = pc->output_index_gen++; + choice = &pc->check_contract.contract_terms->details.v1 + .choices[pc->parse_wallet_data.choice_index]; + output = &choice->outputs[pc->output_index_gen]; + cnt = count_output_tokens (pc, + output); + + out.output_index = pc->output_index_gen; out.h_issue.hash = *h_issue; out.sig.signature = sig; GNUNET_array_append (pc->output_tokens, pc->output_tokens_len, out); + + + /* Go to next output once we've output all tokens for the current one. */ + pc->output_token_cnt++; + if (pc->output_token_cnt >= cnt) + { + pc->output_token_cnt = 0; + pc->output_index_gen++; + } }