merchant

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

commit 2dcbc692f955e9a5a40fd2570694ac0e2fae7222
parent d9cf9ee80fe28e01aa94c604c34e411b51a0d7ff
Author: bohdan-potuzhnyi <bohdan.potuzhnyi@gmail.com>
Date:   Mon, 14 Jul 2025 14:56:26 +0200

correct donau return code

Diffstat:
Msrc/backend/taler-merchant-httpd_post-orders-ID-pay.c | 352+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/include/taler_merchant_donau.h | 16++++++++--------
Msrc/testing/testing_api_cmd_pay_order.c | 2+-
3 files changed, 191 insertions(+), 179 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 @@ -692,35 +692,37 @@ struct PayContext } batch_deposits; #ifdef HAVE_DONAU_DONAU_SERVICE_H - struct { - /** - * The id of the charity as saved on the donau. - */ - uint64_t charity_id; - - /** - * Private key of the charity(related to the private key of the merchant). - */ - struct DONAU_CharityPrivateKeyP *charity_priv; - - } charity; - - struct { - /** - * Receipts for the donations from the Donau. - */ - struct DONAU_BlindedDonationUnitSignature *sigs; - - /** - * Number of receipts for the donations from the Donau. - */ - unsigned int num_sigs; - - /** - * The donation receipt in json format. - */ - json_t *donau_sigs_json; - } donau_receipt; + struct + { + /** + * The id of the charity as saved on the donau. + */ + uint64_t charity_id; + + /** + * Private key of the charity(related to the private key of the merchant). + */ + struct DONAU_CharityPrivateKeyP *charity_priv; + + } charity; + + struct + { + /** + * Receipts for the donations from the Donau. + */ + struct DONAU_BlindedDonationUnitSignature *sigs; + + /** + * Number of receipts for the donations from the Donau. + */ + unsigned int num_sigs; + + /** + * The donation receipt in json format. + */ + json_t *donau_sigs_json; + } donau_receipt; #endif }; @@ -1776,9 +1778,9 @@ phase_success_response (struct PayContext *pc) GNUNET_JSON_pack_array_steal ("token_sigs", token_sigs)), #ifdef HAVE_DONAU_DONAU_SERVICE_H - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_array_steal ("donau_sigs", - pc->donau_receipt.donau_sigs_json)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_array_steal ("donau_sigs", + pc->donau_receipt.donau_sigs_json)), #endif GNUNET_JSON_pack_data_auto ("sig", &sig))); @@ -1841,6 +1843,7 @@ phase_payment_notification (struct PayContext *pc) pc->phase = PP_SUCCESS_RESPONSE; } + #ifdef HAVE_DONAU_DONAU_SERVICE_H /** @@ -1869,6 +1872,7 @@ signatures_to_JSON (const size_t num_sig, } } + static void merchant_donau_issue_receipt_cb (void *cls, const struct DONAU_BatchIssueResponse *resp) @@ -1921,13 +1925,13 @@ merchant_donau_issue_receipt_cb (void *cls, return; } - /* If the HTTP status is 200 (OK), we can read the union contents. */ - if (MHD_HTTP_OK == http_status) + /* If the HTTP status is 201 (CREATED), we can read the union contents. */ + if (MHD_HTTP_CREATED == http_status) { pc->donau_receipt.sigs = resp->details.ok.blinded_sigs; /* array of signatures */ - pc->donau_receipt.donau_sigs_json = json_array(); + pc->donau_receipt.donau_sigs_json = json_array (); if (NULL == pc->donau_receipt.donau_sigs_json) { GNUNET_break_op (0); @@ -1964,6 +1968,7 @@ merchant_donau_issue_receipt_cb (void *cls, pay_resume (pc); } + /** * Parse a bkp encoded in JSON. * @@ -1996,6 +2001,7 @@ merchant_parse_json_bkp (struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkp, return GNUNET_OK; } + #endif @@ -2003,84 +2009,85 @@ static void phase_generate_donation_receipt (struct PayContext *pc) { #ifndef HAVE_DONAU_DONAU_SERVICE_H - /* If Donau is disabled at compile-time, skip. */ - pc->phase = PP_PAYMENT_NOTIFICATION; - return; + /* If Donau is disabled at compile-time, skip. */ + pc->phase = PP_PAYMENT_NOTIFICATION; + return; #else - /* 1) Parse the BUDIs array from JSON into an array of - * DONAU_BlindedUniqueDonorIdentifierKeyPair. */ - const json_t *budikeypairs = pc->parse_wallet_data.donau.budikeypairs; - size_t num_bkps = 0; - struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps = NULL; + /* 1) Parse the BUDIs array from JSON into an array of + * DONAU_BlindedUniqueDonorIdentifierKeyPair. */ + const json_t *budikeypairs = pc->parse_wallet_data.donau.budikeypairs; + size_t num_bkps = 0; + struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps = NULL; - if ((NULL == budikeypairs) || (! json_is_array (budikeypairs))) - { - /* Missing or invalid budikeypairs array */ - resume_pay_with_error (pc, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "Missing or invalid 'budikeypairs' array"); - return; - } + if ((NULL == budikeypairs) || (! json_is_array (budikeypairs))) + { + /* Missing or invalid budikeypairs array */ + resume_pay_with_error (pc, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "Missing or invalid 'budikeypairs' array"); + return; + } - num_bkps = json_array_size (budikeypairs); - /* Looks strange, but theoretically must work, at least donau uses it*/ - pc->donau_receipt.num_sigs = num_bkps; - if (0 == num_bkps) - { - /* Theoretically is part is never reached, how do we behave? */ - resume_pay_with_error (pc, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "No budikeypairs provided"); - return; - } + num_bkps = json_array_size (budikeypairs); + /* Looks strange, but theoretically must work, at least donau uses it*/ + pc->donau_receipt.num_sigs = num_bkps; + if (0 == num_bkps) + { + /* Theoretically is part is never reached, how do we behave? */ + resume_pay_with_error (pc, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "No budikeypairs provided"); + return; + } - bkps = GNUNET_new_array (num_bkps, - struct DONAU_BlindedUniqueDonorIdentifierKeyPair); + bkps = GNUNET_new_array (num_bkps, + struct DONAU_BlindedUniqueDonorIdentifierKeyPair); - /* Parse each BUDI object */ - for (size_t i = 0; i < num_bkps; i++) - { - const json_t *bkp_obj = json_array_get (budikeypairs, i); - if (GNUNET_SYSERR == merchant_parse_json_bkp (&bkps[i], bkp_obj)) - { - GNUNET_break_op (0); - GNUNET_free (bkps); - resume_pay_with_error (pc, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "Failed to parse budikeypairs"); - return; - } - } - - /* 2) Call DONAU_charity_issue_receipt() asynchronously. */ - struct DONAU_BatchIssueReceiptHandle *birh = - DONAU_charity_issue_receipt (TMH_curl_ctx, - pc->parse_wallet_data.donau.donau_url, - pc->charity.charity_priv, - pc->charity.charity_id, - pc->parse_wallet_data.donau.donation_year, - num_bkps, - bkps, - &merchant_donau_issue_receipt_cb, - pc); - if (NULL == birh) + /* Parse each BUDI object */ + for (size_t i = 0; i < num_bkps; i++) + { + const json_t *bkp_obj = json_array_get (budikeypairs, i); + if (GNUNET_SYSERR == merchant_parse_json_bkp (&bkps[i], bkp_obj)) { - /* The request was invalid from the library’s perspective. */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create Donau receipt request\n"); - resume_pay_with_error (pc, - TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR, - "Donau error"); + GNUNET_break_op (0); GNUNET_free (bkps); + resume_pay_with_error (pc, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "Failed to parse budikeypairs"); return; } + } - /* As we do in the batch_issue for the exchange*/ - MHD_suspend_connection (pc->connection); - pc->suspended = GNUNET_YES; + /* 2) Call DONAU_charity_issue_receipt() asynchronously. */ + struct DONAU_BatchIssueReceiptHandle *birh = + DONAU_charity_issue_receipt (TMH_curl_ctx, + pc->parse_wallet_data.donau.donau_url, + pc->charity.charity_priv, + pc->charity.charity_id, + pc->parse_wallet_data.donau.donation_year, + num_bkps, + bkps, + &merchant_donau_issue_receipt_cb, + pc); + if (NULL == birh) + { + /* The request was invalid from the library’s perspective. */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create Donau receipt request\n"); + resume_pay_with_error (pc, + TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR, + "Donau error"); + GNUNET_free (bkps); + return; + } + + /* As we do in the batch_issue for the exchange*/ + MHD_suspend_connection (pc->connection); + pc->suspended = GNUNET_YES; #endif } + /** * Function called with information about a coin that was deposited. * @@ -2687,7 +2694,7 @@ phase_execute_pay_transaction (struct PayContext *pc) { const struct TALER_MERCHANT_ContractChoice *choice = &pc->check_contract.contract_terms->details.v1 - .choices[pc->parse_wallet_data.choice_index]; + .choices[pc->parse_wallet_data.choice_index]; /* If the matching contract-output is a donation-receipt, we skip it. */ if (i < choice->outputs_len && @@ -2750,7 +2757,7 @@ phase_execute_pay_transaction (struct PayContext *pc) pc->charity.charity_priv = GNUNET_new (struct DONAU_CharityPrivateKeyP); /* Part where we fetch info about the charity*/ - qs = TMH_db->lookup_order_charity( + qs = TMH_db->lookup_order_charity ( TMH_db->cls, pc->parse_wallet_data.donau.donau_url, &pc->charity.charity_id, @@ -2878,7 +2885,7 @@ find_valid_input_tokens ( struct TokenUseConfirmation *tuc = &pc->parse_pay.tokens[index + j]; const struct TALER_MERCHANT_ContractTokenFamilyKey *key = NULL; - for (unsigned int i=0; i<family->keys_len; i++) + for (unsigned int i = 0; i<family->keys_len; i++) { const struct TALER_MERCHANT_ContractTokenFamilyKey *ki = &family->keys[i]; @@ -3121,9 +3128,9 @@ find_family (const struct PayContext *pc, * @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) +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; @@ -3133,18 +3140,18 @@ handle_output_token(struct PayContext *pc, /* 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); + 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")); + 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 */ } @@ -3152,13 +3159,13 @@ handle_output_token(struct PayContext *pc, 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")); + 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; } @@ -3167,7 +3174,7 @@ handle_output_token(struct PayContext *pc, /* Fetch the private key from the DB for the merchant instance and * this particular family/time interval. */ - qs = TMH_db->lookup_token_family_key( + qs = TMH_db->lookup_token_family_key ( TMH_db->cls, pc->hc->instance->settings.id, family->slug, @@ -3176,36 +3183,39 @@ handle_output_token(struct PayContext *pc, &details); if (qs <= 0) { - GNUNET_log( + 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)); + (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); + 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); + 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 (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; @@ -3215,6 +3225,7 @@ handle_output_token(struct PayContext *pc, return GNUNET_OK; } + /** * Handle checks for contract output of type TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT. * For now, this does nothing and simply returns GNUNET_OK. @@ -3226,7 +3237,8 @@ handle_output_token(struct PayContext *pc, */ static enum GNUNET_GenericReturnValue handle_output_donation_receipt (struct PayContext *pc, - const struct TALER_MERCHANT_ContractOutput *output, + const struct + TALER_MERCHANT_ContractOutput *output, unsigned int output_index) { /* FIXME: Implement this function. @@ -3241,7 +3253,6 @@ handle_output_donation_receipt (struct PayContext *pc, } - /** * 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' @@ -3319,29 +3330,30 @@ phase_validate_tokens (struct PayContext *pc) const struct TALER_MERCHANT_ContractOutput *output = &selected->outputs[i]; - switch(output->type){ - case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: - if (GNUNET_OK != - handle_output_token (pc, - output, - i)) - { - /* Error is already scheduled from handle_output_token. */ - return; - } - break; - case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: - if (GNUNET_OK != - handle_output_donation_receipt (pc, - output, - i)) - { - /* Error is already scheduled from handle_output_donation_receipt. */ - return; - } - continue; - default: - continue; + switch (output->type) + { + case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: + if (GNUNET_OK != + handle_output_token (pc, + output, + i)) + { + /* Error is already scheduled from handle_output_token. */ + return; + } + break; + case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: + if (GNUNET_OK != + handle_output_donation_receipt (pc, + output, + i)) + { + /* Error is already scheduled from handle_output_donation_receipt. */ + return; + } + continue; + default: + continue; } } } @@ -3959,14 +3971,14 @@ phase_parse_wallet_data (struct PayContext *pc) if (NULL != donau_url_tmp) pc->parse_wallet_data.donau.donau_url = GNUNET_strdup (donau_url_tmp); - //FIXME: Probably some error handling could happen here + // FIXME: Probably some error handling could happen here } pc->parse_wallet_data.donau.donation_year = 0; { pc->parse_wallet_data.donau.donation_year = donation_year_tmp; - //FIXME: What if the url is set but the year is not? + // FIXME: What if the url is set but the year is not? // add error throwing } @@ -3975,7 +3987,7 @@ phase_parse_wallet_data (struct PayContext *pc) { pc->parse_wallet_data.donau.budikeypairs = budikeypairs; - //FIXME: What if the url is set but the budis not? + // FIXME: What if the url is set but the budis not? } TALER_json_hash (pc->parse_pay.wallet_data, @@ -4021,7 +4033,7 @@ phase_parse_pay (struct PayContext *pc) char *dump = json_dumps (pc->hc->request_body, JSON_INDENT (2) /* human friendly */ | JSON_ENCODE_ANY /* handle UTF-8 as \uXXXX */ - | JSON_SORT_KEYS /* deterministic output */); + | JSON_SORT_KEYS /* deterministic output */); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "POST /orders/%s/pay – request body follows:\n%s\n", diff --git a/src/include/taler_merchant_donau.h b/src/include/taler_merchant_donau.h @@ -48,7 +48,7 @@ struct TALER_MERCHANT_DONAU_Charity struct DONAU_CharityPublicKeyP charity_pub; /** - * Max donation amout for this charitiy and @e current_year. + * Max donation amount for this charitiy and @e current_year. */ struct TALER_Amount max_per_year; @@ -145,7 +145,7 @@ typedef void const struct TALER_Amount *charity_receipts_to_date, int64_t current_year, const json_t *donau_keys_json -); + ); /** * SPECIFICATION FOR REQUESTS /donau @@ -262,7 +262,7 @@ typedef void * @return the instance handle; NULL upon error */ struct TALER_MERCHANT_DonauInstanceGetHandle * -TALER_MERCHANT_donau_instances_get( +TALER_MERCHANT_donau_instances_get ( struct GNUNET_CURL_Context *ctx, const char *backend_url, TALER_MERCHANT_DonauInstanceGetCallback cb, @@ -276,7 +276,7 @@ TALER_MERCHANT_donau_instances_get( * @param dgh request to cancel. */ void -TALER_MERCHANT_donau_instances_get_cancel( +TALER_MERCHANT_donau_instances_get_cancel ( struct TALER_MERCHANT_DonauInstanceGetHandle *dgh); @@ -310,7 +310,7 @@ typedef void * @return the instance handle, or NULL upon error */ struct TALER_MERCHANT_DonauInstancePostHandle * -TALER_MERCHANT_donau_instances_post( +TALER_MERCHANT_donau_instances_post ( struct GNUNET_CURL_Context *ctx, const char *backend_url, const struct TALER_MERCHANT_DONAU_Charity *charity, @@ -325,7 +325,7 @@ TALER_MERCHANT_donau_instances_post( * @param dph request to cancel */ void -TALER_MERCHANT_donau_instances_post_cancel( +TALER_MERCHANT_donau_instances_post_cancel ( struct TALER_MERCHANT_DonauInstancePostHandle *dph); @@ -356,7 +356,7 @@ typedef void * @return Handle for the DELETE operation, or NULL on error. */ struct TALER_MERCHANT_DonauInstanceDeleteHandle * -TALER_MERCHANT_donau_instance_delete( +TALER_MERCHANT_donau_instance_delete ( struct GNUNET_CURL_Context *ctx, const char *backend_url, uint64_t charity_id, @@ -369,7 +369,7 @@ TALER_MERCHANT_donau_instance_delete( * @param ddh Handle for the operation to cancel. */ void -TALER_MERCHANT_donau_instance_delete_cancel( +TALER_MERCHANT_donau_instance_delete_cancel ( struct TALER_MERCHANT_DonauInstanceDeleteHandle *ddh); #endif \ No newline at end of file diff --git a/src/testing/testing_api_cmd_pay_order.c b/src/testing/testing_api_cmd_pay_order.c @@ -104,7 +104,7 @@ struct MerchantDonauPayData uint64_t year; /** - * BUDI keypairs used in the payment (blinded_udi + pubkey). + * BUDI key pairs used in the payment (blinded_udi + pubkey). */ struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps;